merge: fix/wave5-deadcode-decisions
This commit is contained in:
+3
-2
@@ -5,7 +5,7 @@
|
||||
|
||||
## 焦点
|
||||
|
||||
1. **系统体检整改**:Wave 0–4 **done**;下一波 Wave 5(死代码决策 X-1~X-5)
|
||||
1. **系统体检整改**:Wave 0–5 **done**(死代码决策 X-1~X-5 已合入本地 dev)
|
||||
2. **rdev 模板深度 / document-author-only**:已合入 `origin/dev`;未晋升 test/main
|
||||
3. **红线**:禁止整支 merge FAV;不默认升 `MULTI_INTENT_PLAN_TIER=full`;`SEMANTIC_SUMMARY_REPLACE` 不随 W2 开启;FAV live 观察窗证据驱动;`FAV_DEGRADED_READ=false`;项目 143 只读
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
|
||||
| owner | task | branch | worktree | mutex | status |
|
||||
|-------|------|--------|----------|-------|--------|
|
||||
| Cursor-Auto | 2026-09-18-wave5-deadcode-decisions | fix/wave5-deadcode-decisions | - | deadcode-cleanup | done |
|
||||
| Cursor-Auto | 2026-09-18-wave4-hardening-r1-r8 | fix/wave4-hardening-r1-r8 | - | hardening | done |
|
||||
| Cursor-Auto | 2026-09-18-wave3-test-debt-blindspots | fix/wave3-test-debt-blindspots | - | test-debt | done |
|
||||
| Cursor-Auto | 2026-09-18-wave2-config-flag-align | fix/wave2-config-flag-align | - | infra | done |
|
||||
@@ -136,7 +137,7 @@
|
||||
|
||||
| holder | phase | since | scope | queue |
|
||||
|--------|-------|-------|-------|-------|
|
||||
| - | idle | 2026-09-18 | Wave 4 hardening 已合入本地 dev(见 [test-report](../test-reports/2026-09-18-wave4-hardening-r1-r8-test-report.md);merge `c4db565`) | - |
|
||||
| - | idle | 2026-09-18 | Wave 5 deadcode 已合入本地 dev(见 [test-report](../test-reports/2026-09-18-wave5-deadcode-decisions-test-report.md)) | - |
|
||||
|
||||
**phase**: `idle` | `merging` | `smoke` | `e2e`
|
||||
|
||||
|
||||
@@ -63,6 +63,8 @@ L1(SWBOOT-00…09 契约/迁移/目录钩子 + SWBOOT-10 stub)**已合入 `d
|
||||
|
||||
L1 已合 `dev`。W5a 从 `dev` 开 `feature/w5a-software-bootstrap-ui-pi`(mutex `workflow-design`)。W5b 受控执行器未启动,SWBOOT-10 保持 fail-closed stub。
|
||||
|
||||
**Wave 5 X-3(2026-09-18)**:goose 00048 三表(`project_types` / `compliance_profiles` / `project_type_workflows`;另含零读者的 `project_source_bindings` / `repo_path_allowlist`)消费方接线评估结论 = **deferred → SWBOOT-10/W5b**。不改 schema;API 继续硬编码目录。决策落点:[wave5-deadcode-decisions.md#x-3](../short-term/2026-09-18-wave5-deadcode-decisions.md#x-3)。不阻塞 W5b 立项。
|
||||
|
||||
## 相关链接
|
||||
|
||||
- [详细设计](../../system-design/2026-09-14-software-project-bootstrap-workflow.md)
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
---
|
||||
title: "Wave 5:死代码与机制收口(X-1~X-5)"
|
||||
status: done
|
||||
created: 2026-09-18
|
||||
updated: 2026-09-18
|
||||
owner: Cursor-Auto
|
||||
branch: fix/wave5-deadcode-decisions
|
||||
worktree: "-"
|
||||
mutex: deadcode-cleanup
|
||||
---
|
||||
|
||||
# Wave 5:死代码与机制收口(X-1~X-5)
|
||||
|
||||
## 目标
|
||||
|
||||
按系统体检整改方案 Wave 5,对 X-1~X-5 逐项决策并收口:删除真死 flag/组件、封存 REPLACE、标记 00048 deferred、保留 system_settings 二期键。
|
||||
|
||||
## 范围
|
||||
|
||||
- X-1~X-5 全项(含级联删除 `patchNotification`)
|
||||
|
||||
## 非目标
|
||||
|
||||
- X-6(`useTaskRunStream` 伪 ref);不接线 REPLACE;不升 MULTI_INTENT_PLAN_TIER;不改 FAV;项目 143 只读;不改 goose 00048/00049 schema;不 push
|
||||
|
||||
## 步骤
|
||||
|
||||
1. [x] 决策记录落盘(本文件「决策记录」段)
|
||||
2. [x] X-1 删除 `SEMANTIC_FALLBACK_ENABLED` + 回归测试
|
||||
3. [x] X-2 封存注释回链
|
||||
4. [x] X-3 00048 deferred 注释 + long-term
|
||||
5. [x] X-4 system_settings 6 键注释
|
||||
6. [x] X-5 删除死组件/死 API
|
||||
7. [x] 全量验收 + test-report + 合入本地 `.worktrees/dev-rdev-merge` `dev`
|
||||
|
||||
## 验收标准
|
||||
|
||||
- [x] X-1~X-5 每项有决策记录(本文件)
|
||||
- [x] `go vet ./...` / `go test -short ./...` / `cd web && npm test && npm run build`
|
||||
- [x] remediation Wave 5 行改 done;勾选「X-1~X-5 每项有决策记录」
|
||||
- [x] 文档闭环 + merge `--no-ff`
|
||||
|
||||
## 相关链接
|
||||
|
||||
- Remediation: [.doc/remediation/2026-09-17-system-health-remediation-plan.md](../../remediation/2026-09-17-system-health-remediation-plan.md)
|
||||
- Audit: [.doc/audit/2026-09-17-system-health-check.md](../../audit/2026-09-17-system-health-check.md)
|
||||
- Test report: [.doc/test-reports/2026-09-18-wave5-deadcode-decisions-test-report.md](../../test-reports/2026-09-18-wave5-deadcode-decisions-test-report.md)
|
||||
- Assets: [.doc/test-reports/assets/2026-09-18-wave5-deadcode-decisions/](../../test-reports/assets/2026-09-18-wave5-deadcode-decisions/)
|
||||
|
||||
## 决策记录
|
||||
|
||||
### X-1:`SEMANTIC_FALLBACK_ENABLED` — 删除
|
||||
|
||||
- **现状证据**:字段仅在 `internal/intent/flags.go` 解析、`internal/app/app.go` 注入 sysadmin 视图、`internal/sysadmin/features.go` 目录展示;全仓无业务消费方。三环境 compose + `.env.example` 均声明该死变量。
|
||||
- **决策**:删除(不接线)。
|
||||
- **理由**:零读者、无设计文档描述预期语义;接线需独立产品设计,保留只会继续污染配置面。
|
||||
- **影响范围**:`flags.go` / `app.go` / `features.go` / 三 compose / `.env.example`;新增 `TestComposeNoDeadSemanticFallbackConfig` 防复活。
|
||||
- **验收**:`go vet ./...`;`go test ./internal/intent/... ./internal/app/... ./internal/sysadmin/... ./internal/config/...`;`rg SEMANTIC_FALLBACK_ENABLED` 除 `.doc/` 历史外无匹配。
|
||||
|
||||
### X-2:`SEMANTIC_SUMMARY_REPLACE` — 维持封存
|
||||
|
||||
- **现状证据**:env 可读但 `semantic_summary.go` 明确 `_ = SemanticSummaryReplaceEnabled()` 且不替换 `sum.Narrative`;负例 `TestSemanticSummary_ShadowOKKeepsDeterministic` 已覆盖 `Replace: true`。
|
||||
- **决策**:维持不接线;补强注释回链本决策记录。
|
||||
- **理由**:与 CURRENT 红线及 remediation Wave 5 一致;启用需独立授权 + fail-closed 灰度设计。
|
||||
- **影响范围**:仅 `summary_flags.go` / `semantic_summary.go` 注释。
|
||||
- **验收**:`go test ./internal/ctxmgr/... -run Semantic`;diff 仅注释。
|
||||
|
||||
### X-3:goose 00048 三表 — deferred → SWBOOT-10/W5b
|
||||
|
||||
- **现状证据**:`project_types` / `compliance_profiles` / `project_type_workflows` 已建表有种子,但 API 走硬编码 `swboot.ProjectTypesCatalog()` / `ComplianceProfilesCatalog()`;`project_source_bindings` / `repo_path_allowlist` 零运行时读写;`RepoAllowlist` 仅 stub + 单测。
|
||||
- **决策**:标记 deferred,归 W5b/SWBOOT-10;**不改 schema**。
|
||||
- **理由**:FEATURE-MATRIX 明确 SWBOOT-10 仍 stub;DB 化消费方与受控执行器需独立安全评审。
|
||||
- **影响范围**:`catalog.go` / `repo_allowlist.go` 注释;long-term 软件自举文档追加一行。
|
||||
- **验收**:`go test ./internal/swboot/...`;diff 仅注释与 long-term 文档。
|
||||
|
||||
### X-4:`system_settings` 6 未接线键 — 保留待二期
|
||||
|
||||
- **现状证据**:白名单 7 键中仅 `workflow.allow_author_only_approval` 有消费方;其余 6 键(`ui.maintenance_notice`、`ui.read_only_mode`、`chat.default_session_role`、`tasks.default_auto_repair`、`kb.default_search_limit`、`notifications.stage_advance_reminder`)零业务读取。FEATURE-MATRIX 系统管理台「白名单项接消费方」列为二期。
|
||||
- **决策**:保留白名单 + 逐键注释说明预期消费方;不删。
|
||||
- **理由**:删除需改写多处白盒测试且收益低;机制先行符合一期设计,二期再接线。
|
||||
- **影响范围**:仅 `internal/sysadmin/settings.go` 注释。
|
||||
- **验收**:`go test ./internal/sysadmin/...`;diff 仅注释。
|
||||
|
||||
### X-5:前端死组件/死 API — 删除
|
||||
|
||||
- **现状证据**(`web/` 全目录扫描,排除自身):
|
||||
- `DocumentNotificationList` / `WorkflowPrerequisiteBanner` / `SettingsModal`:仅自身文件命中;`App` 使用 `UserSettingsModal`。
|
||||
- `activateWorkflow`(`api/projectWorkflow.ts`):仅定义处;实际用 `activateWorkflowInstance`。
|
||||
- `saveMessages`:已 `@deprecated` no-op,仅定义处。
|
||||
- 级联:`patchNotification` 唯一调用方为 `DocumentNotificationList`。
|
||||
- **决策**:删除上述 3 组件 + `activateWorkflow`(及类型)+ `saveMessages` + `patchNotification`;同步 import。后端 `PATCH /api/v1/notifications/{id}` 保留。
|
||||
- **理由**:真死代码;删除降低误挂载与维护噪音;后端路由未经本波审计故不删。
|
||||
- **影响范围**:见删除清单;无 barrel / 无专用单测。
|
||||
- **验收**:`cd web && npm test && npm run build`;`rg` 无残留(`activateWorkflowInstance` 除外)。
|
||||
|
||||
## 证据 / 备注
|
||||
|
||||
基线 tip:`a12c947`(Wave 4 已合入本地 `.worktrees/dev-rdev-merge` `dev`)。
|
||||
@@ -8,7 +8,7 @@ updated: 2026-09-18
|
||||
# 系统体检整改与完善实施方案
|
||||
|
||||
> 配套审计:[../audit/2026-09-17-system-health-check.md](../audit/2026-09-17-system-health-check.md)(编号 B-x 确认 bug、R-x 隐患、E-x 环境缺陷、T-x 任务、X-x 死代码、S-x 测试盲区、D-x 文档漂移)。
|
||||
> Wave 0–4 已实施收口;后续 Wave 5+ 仍按本方案推进。
|
||||
> Wave 0–5 已实施收口;后续 Wave 6+ 仍按本方案推进。
|
||||
|
||||
## 背景
|
||||
|
||||
@@ -32,7 +32,7 @@ updated: 2026-09-18
|
||||
| **Wave 2:配置与灰度入口对齐** | E-3:三环境声明 `MULTI_INTENT_PLAN_TIER`+U9–U12(legacy/false);A-4:test/main 补 SHADOW/NARRATIVE。E-4:删除 `CURSOR_RUNNER_URL`+cursor-agent。E-6:main `SEED_DEMO_USERS=false`。E-7:非法 `INTENT_V2_DECISION_TIER`→`none`。E-9:评估不改 initdb | **done**(2026-09-18):[test-report](../test-reports/2026-09-18-wave2-config-flag-align-test-report.md);三环境 env 抽检 + health/ready PASS |
|
||||
| **Wave 3:测试债与盲区** | S-1:隔离 PG(5436) 跑 `TestReconcile_LegacyRepair_PG`。S-2:删 taskstream 死跳过;plan_owner 重定向说明。S-4:每 spec npm 脚本;退役 `document-revision.spec`(消灭 DOCREV_E2E_BASE_URL)。S-5:`projectSettingsError` 复用到 members/freeform/taskRuns/chatSessions | **done**(2026-09-18):[test-report](../test-reports/2026-09-18-wave3-test-debt-blindspots-test-report.md);playwright list 29/8;reconcile PG PASS |
|
||||
| **Wave 4:隐患加固(可与 Wave 0–3 并行认领,mutex 不冲突时)** | R-1:`getOrCreateSession` 增 owner 绑定校验(session 创建时记录 owner,后续请求校验,不匹配 403)。R-2:18 处 `rows.Next()` 循环补 `rows.Err()` 检查。R-3:dispatcher Handlers 读路径加 RLock(或注释冻结「仅启动期注册」并加 archtest 约束)。R-4:`memTurns` 加 TTL/容量淘汰。R-5:pg_store JSONB 反序列化错误改为显式报错。R-6:后台 ticker 纳入 Shutdown;vault Drain 错误上报告警。R-7/R-8:SSE 重连 401/404 fail-fast、逐帧容错;登出 abort 在途流 | **done**(2026-09-18):[test-report](../test-reports/2026-09-18-wave4-hardening-r1-r8-test-report.md);Memory+PG 跨用户负例 PASS;`go test -short` / `npm test` 全绿 |
|
||||
| **Wave 5:死代码与机制收口(需逐项决策,不擅自删)** | X-1:`SEMANTIC_FALLBACK_ENABLED` 删除或接线(先决策语义兜底是否还需要)。X-2:`SEMANTIC_SUMMARY_REPLACE` 维持未接线但补注释说明为刻意封存(与红线一致)。X-3:00048 三表消费方接线评估(并入 W5b SWBOOT-10 或标记 deferred)。X-4:system_settings 6 键逐一接消费方或从白名单移除(系统管理台二期范围)。X-5:前端死组件/死 API 删除(DocumentNotificationList、WorkflowPrerequisiteBanner、SettingsModal、activateWorkflow、saveMessages)或明确保留理由 | 每项有决策记录(ADR 或 short-term 决议段);删除后 `npm run build`/`go build` 全绿 |
|
||||
| **Wave 5:死代码与机制收口(需逐项决策,不擅自删)** | X-1:删除 `SEMANTIC_FALLBACK_ENABLED`。X-2:`SEMANTIC_SUMMARY_REPLACE` 维持未接线并封存注释。X-3:00048 三表 deferred → SWBOOT-10/W5b。X-4:system_settings 6 键保留并注释二期。X-5:删除 DocumentNotificationList、WorkflowPrerequisiteBanner、SettingsModal、activateWorkflow、saveMessages 及级联 patchNotification | **done**(2026-09-18):[决策](../ops/short-term/2026-09-18-wave5-deadcode-decisions.md) · [test-report](../test-reports/2026-09-18-wave5-deadcode-decisions-test-report.md) |
|
||||
| **Wave 6:文档漂移修复** | D-1:PU8 四份契约文件状态改 done 并归档。D-2:modular-monolith ADR 状态更新为 accepted。D-3:两个 INDEX 末行已在本审计修正(复核即可)。D-4:handoff 回标已被覆盖。D-5:backlog D3/D7 同步最新状态。D-6:PROJECT.md §9「剩余 blocker」段与 gap-matrix 对齐或删除 | 漂移清单逐项回链;`.doc/audit/INDEX.md`、`remediation/INDEX.md` 与 FEATURE-MATRIX 三方口径一致 |
|
||||
| **Wave 7:既有 roadmap 收口(不改优先级,仅确认编排)** | T1/W5:SWBOOT-10 安全评审后实施(保持 fail-closed 直至授权)。T1/W6:FAV live 放量 staging→artifact→publication,每级 ≥7 天观察窗,`FAV_DEGRADED_READ=false` 不动。T1/W7:D7 待合规签字后按 ADR 整改。T2:Control Center 前端 SPA 七页人工走查。T4:首次加载性能 U1/U3–U7。T5/T6/T8:backlog 类按收益触发 | 各项沿用其原有关闭条件(见 FEATURE-MATRIX 剩余工作列);FAV/D7/tier 升级保持「需用户显式授权」红线 |
|
||||
|
||||
@@ -45,7 +45,7 @@ updated: 2026-09-18
|
||||
- [x] `TestReconcile_LegacyRepair_PG` 报告归档,gap-matrix 测试债行关闭(Wave 3)
|
||||
- [x] `playwright test --list` 与门禁文档一致(Wave 3)
|
||||
- [x] 跨用户会话挂靠负例测试入库(Wave 4)
|
||||
- [ ] X-1~X-5 每项有决策记录(Wave 5)
|
||||
- [x] X-1~X-5 每项有决策记录(Wave 5)
|
||||
- [ ] D-1~D-6 全部回链关闭(Wave 6)
|
||||
- [x] 不触碰红线:FAV_DEGRADED_READ=false、MULTI_INTENT_PLAN_TIER 默认 legacy、SEMANTIC_SUMMARY_REPLACE 不接线、项目 143 只读、禁止整支 merge FAV
|
||||
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
# Wave 5 死代码决策(X-1~X-5)测试报告
|
||||
|
||||
## 一、 测试背景 (Background)
|
||||
|
||||
系统体检整改 Wave 0–4 已合入本地 `dev@a12c947`。Wave 5 对死 flag、封存 REPLACE、00048 三表、system_settings 未接线键、前端死组件做逐项决策并收口。决策落点:[short-term](../ops/short-term/2026-09-18-wave5-deadcode-decisions.md)。
|
||||
|
||||
## 二、 测试目标 (Test Objectives)
|
||||
|
||||
- X-1:删除 `SEMANTIC_FALLBACK_ENABLED` 且回归测试防复活。
|
||||
- X-2:`SEMANTIC_SUMMARY_REPLACE` 维持不接线,注释回链决策记录。
|
||||
- X-3:00048 三表 deferred → SWBOOT-10/W5b(不改 schema)。
|
||||
- X-4:6 个未接线 system_settings 键保留并注释二期。
|
||||
- X-5:删除死组件/死 API 后前端构建与测试全绿。
|
||||
- 门禁:`go vet ./...`、`go test -short ./...`、`cd web && npm test && npm run build`。
|
||||
|
||||
## 三、 测试方案与环境 (Test Plan & Environment)
|
||||
|
||||
- OS:Linux;Go `go1.27.1 linux/amd64`;Node `v26.8.2`;npm `11.19.1`。
|
||||
- 层级:单元/包测试(`-short`)+ 前端 `tsx --test` / vitest + `tsc && vite build`。
|
||||
- 未做容器 rebuild / 真 PI E2E(本波为死代码删除与注释,无运行时行为变更)。项目 143 只读,未触碰。
|
||||
- 证据目录:`.doc/test-reports/assets/2026-09-18-wave5-deadcode-decisions/`。
|
||||
|
||||
## 四、 预期结果 (Expected Results)
|
||||
|
||||
| 项 | 判定 |
|
||||
|----|------|
|
||||
| X-1 | compose/代码无 `SEMANTIC_FALLBACK_ENABLED`(回归测试字符串除外);`TestComposeNoDeadSemanticFallbackConfig` PASS |
|
||||
| X-2 | REPLACE 仍不替换 narrative;diff 仅注释 |
|
||||
| X-3 | schema 不变;swboot 测试绿 |
|
||||
| X-4 | 白名单键仍在;sysadmin 测试绿 |
|
||||
| X-5 | `npm run build`(含 tsc)绿;死符号无引用 |
|
||||
| 全量 | vet 无输出;`go test -short` 无 FAIL;npm test 0 fail |
|
||||
|
||||
## 五、 实际测试结果与判定 (Actual Results & Evaluation)
|
||||
|
||||
### 5.1 X-1 回归测试
|
||||
|
||||
- **测试输入**:`go test -short ./internal/config/...`(含于全量 `-short`)
|
||||
- **实际输出**:`ok chat-agent-system/internal/config`(见 assets `go-test-short.txt`)
|
||||
- **评价判定**:PASS。`SEMANTIC_FALLBACK_ENABLED` 仅剩回归测试断言字符串。
|
||||
- **性能与耗时**:config 包约 0.x s(全量 short 合计约 16s)
|
||||
|
||||
### 5.2 X-2 / X-3 / X-4 包测试
|
||||
|
||||
- **测试输入**:`go test -short ./internal/ctxmgr/... ./internal/swboot/... ./internal/sysadmin/...`(含于全量)
|
||||
- **实际输出**:
|
||||
- `ok chat-agent-system/internal/ctxmgr`(cached/ok)
|
||||
- `ok chat-agent-system/internal/swboot 0.002s`
|
||||
- `ok chat-agent-system/internal/sysadmin 0.003s`
|
||||
- **评价判定**:PASS
|
||||
- **性能与耗时**:毫秒级
|
||||
|
||||
### 5.3 X-5 前端
|
||||
|
||||
- **测试输入**:`cd web && npm test && npm run build`
|
||||
- **实际输出**:
|
||||
- tsx:`tests 406 / pass 406 / fail 0`(`duration_ms 903.526321`)
|
||||
- vitest:`Test Files 3 passed (3)` / `Tests 32 passed (32)` / `Duration 533ms`
|
||||
- build:`tsc && vite build` → `✓ 4123 modules transformed`;`precompress: wrote 49 .br and 49 .gz`
|
||||
- **评价判定**:PASS
|
||||
- **性能与耗时**:npm test + build 合计约 18s
|
||||
|
||||
### 5.4 全量 Go
|
||||
|
||||
- **测试输入**:`go vet ./...`;`go test -short ./...`
|
||||
- **实际输出**:vet 空文件(exit 0);short 测试 `ok` 计数 56,无 `FAIL`/`panic`
|
||||
- **评价判定**:PASS
|
||||
- **性能与耗时**:vet ~2s;short ~16s
|
||||
|
||||
## 六、 自动化测试套件汇总 (Test Suite Execution)
|
||||
|
||||
| 命令 | 结果 | 证据 |
|
||||
|------|------|------|
|
||||
| `go vet ./...` | PASS(exit 0,无输出) | [go-vet.txt](./assets/2026-09-18-wave5-deadcode-decisions/go-vet.txt) |
|
||||
| `go test -short ./...` | PASS(56 ok,无 FAIL) | [go-test-short.txt](./assets/2026-09-18-wave5-deadcode-decisions/go-test-short.txt) |
|
||||
| `cd web && npm test` | PASS(406+32,fail 0) | [npm-test.txt](./assets/2026-09-18-wave5-deadcode-decisions/npm-test.txt) |
|
||||
| `cd web && npm run build` | PASS | [npm-build.txt](./assets/2026-09-18-wave5-deadcode-decisions/npm-build.txt) |
|
||||
|
||||
时间:2026-09-18T04:01:27Z。
|
||||
|
||||
## 七、 测试结论与后续优化建议 (Conclusion & Next Steps)
|
||||
|
||||
达到合入本地 `dev` 标准。红线未触碰:REPLACE 不接线、不升 `MULTI_INTENT_PLAN_TIER`、不改 FAV、项目 143 只读。
|
||||
|
||||
后续:Wave 6 文档漂移(D-1~D-6);SWBOOT-10/W5b 与 system_settings 二期接线仍 deferred,不在本波。
|
||||
@@ -0,0 +1,67 @@
|
||||
? chat-agent-system/cmd/artifact-vault [no test files]
|
||||
ok chat-agent-system/cmd/bizdev-cli 2.561s
|
||||
? chat-agent-system/cmd/dataset [no test files]
|
||||
ok chat-agent-system/cmd/document-content-reconcile 0.003s
|
||||
? chat-agent-system/cmd/document-revision-backfill [no test files]
|
||||
? chat-agent-system/cmd/engineering-dashboard [no test files]
|
||||
? chat-agent-system/cmd/eventmode [no test files]
|
||||
? chat-agent-system/cmd/events [no test files]
|
||||
? chat-agent-system/cmd/recategorize [no test files]
|
||||
? chat-agent-system/cmd/server [no test files]
|
||||
ok chat-agent-system/internal/agent 3.577s
|
||||
ok chat-agent-system/internal/app 0.017s
|
||||
ok chat-agent-system/internal/app/chatflow 0.004s
|
||||
ok chat-agent-system/internal/app/docadvance 0.003s
|
||||
ok chat-agent-system/internal/app/docflow 0.003s
|
||||
ok chat-agent-system/internal/app/docnotify 0.003s
|
||||
ok chat-agent-system/internal/app/docproject 0.003s
|
||||
ok chat-agent-system/internal/app/fav (cached)
|
||||
ok chat-agent-system/internal/app/intentflow 0.014s
|
||||
ok chat-agent-system/internal/app/stageflow 0.004s
|
||||
ok chat-agent-system/internal/app/uow 0.003s
|
||||
ok chat-agent-system/internal/archtest 0.178s
|
||||
ok chat-agent-system/internal/artifactvault (cached)
|
||||
ok chat-agent-system/internal/auth (cached)
|
||||
ok chat-agent-system/internal/authz 0.003s
|
||||
ok chat-agent-system/internal/bizdev 0.161s
|
||||
ok chat-agent-system/internal/chat 0.015s
|
||||
ok chat-agent-system/internal/config 0.004s
|
||||
ok chat-agent-system/internal/coordinator 0.012s
|
||||
ok chat-agent-system/internal/ctxmgr 0.086s
|
||||
ok chat-agent-system/internal/dataset (cached)
|
||||
ok chat-agent-system/internal/db 0.003s
|
||||
ok chat-agent-system/internal/doccontent (cached)
|
||||
ok chat-agent-system/internal/docduty 0.004s
|
||||
ok chat-agent-system/internal/docgov 0.027s
|
||||
ok chat-agent-system/internal/docquery 0.003s
|
||||
ok chat-agent-system/internal/docrevision 0.007s
|
||||
ok chat-agent-system/internal/engineeringdashboard 0.008s
|
||||
ok chat-agent-system/internal/events 0.005s
|
||||
ok chat-agent-system/internal/freedoc 0.027s
|
||||
ok chat-agent-system/internal/fsutil (cached)
|
||||
ok chat-agent-system/internal/httpx (cached)
|
||||
ok chat-agent-system/internal/intent 0.023s
|
||||
ok chat-agent-system/internal/kb 0.009s
|
||||
ok chat-agent-system/internal/maturity (cached)
|
||||
ok chat-agent-system/internal/notification 0.077s
|
||||
? chat-agent-system/internal/observability [no test files]
|
||||
ok chat-agent-system/internal/projectcontext 0.004s
|
||||
ok chat-agent-system/internal/projectinstance 0.005s
|
||||
ok chat-agent-system/internal/projectworkflow 0.003s
|
||||
ok chat-agent-system/internal/quote (cached)
|
||||
ok chat-agent-system/internal/replyexport (cached)
|
||||
ok chat-agent-system/internal/server 14.170s
|
||||
ok chat-agent-system/internal/squad 0.030s
|
||||
ok chat-agent-system/internal/storage 0.015s
|
||||
ok chat-agent-system/internal/stream (cached)
|
||||
ok chat-agent-system/internal/stream/transport (cached)
|
||||
ok chat-agent-system/internal/swboot 0.002s
|
||||
ok chat-agent-system/internal/sysadmin 0.003s
|
||||
ok chat-agent-system/internal/taskrun 0.186s
|
||||
ok chat-agent-system/internal/template 0.757s
|
||||
ok chat-agent-system/internal/upload (cached)
|
||||
? chat-agent-system/internal/workflow [no test files]
|
||||
ok chat-agent-system/internal/workflow/runtime 0.002s
|
||||
ok chat-agent-system/internal/workflowdef 0.003s
|
||||
ok chat-agent-system/internal/workspace (cached)
|
||||
? chat-agent-system/templates [no test files]
|
||||
@@ -0,0 +1,236 @@
|
||||
|
||||
> shuimu-workbench@1.0.0 build
|
||||
> tsc && vite build && node scripts/precompress-dist.mjs
|
||||
|
||||
vite v7.3.6 building client environment for production...
|
||||
transforming...
|
||||
✓ 4123 modules transformed.
|
||||
rendering chunks...
|
||||
computing gzip size...
|
||||
dist/index.html 0.72 kB │ gzip: 0.45 kB
|
||||
dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff 4.42 kB
|
||||
dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 4.93 kB
|
||||
dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 5.21 kB
|
||||
dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 5.47 kB
|
||||
dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff 5.98 kB
|
||||
dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff 6.19 kB
|
||||
dist/assets/KaTeX_Size1-Regular-C195tn64.woff 6.50 kB
|
||||
dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 6.91 kB
|
||||
dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 6.91 kB
|
||||
dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf 7.59 kB
|
||||
dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff 7.66 kB
|
||||
dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff 7.72 kB
|
||||
dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 9.64 kB
|
||||
dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 10.34 kB
|
||||
dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf 10.36 kB
|
||||
dist/assets/KaTeX_Script-Regular-D5yQViql.woff 10.59 kB
|
||||
dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 11.32 kB
|
||||
dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 11.35 kB
|
||||
dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf 11.51 kB
|
||||
dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 12.03 kB
|
||||
dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 12.22 kB
|
||||
dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf 12.23 kB
|
||||
dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff 12.32 kB
|
||||
dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf 12.34 kB
|
||||
dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf 12.37 kB
|
||||
dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff 13.21 kB
|
||||
dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff 13.30 kB
|
||||
dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 13.57 kB
|
||||
dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff 14.11 kB
|
||||
dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff 14.41 kB
|
||||
dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff 16.03 kB
|
||||
dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 16.40 kB
|
||||
dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 16.44 kB
|
||||
dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf 16.65 kB
|
||||
dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 16.78 kB
|
||||
dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 16.99 kB
|
||||
dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff 18.67 kB
|
||||
dist/assets/KaTeX_Math-Italic-DA0__PXp.woff 18.75 kB
|
||||
dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff 19.41 kB
|
||||
dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf 19.44 kB
|
||||
dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf 19.57 kB
|
||||
dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf 19.58 kB
|
||||
dist/assets/KaTeX_Main-Italic-BMLOBm91.woff 19.68 kB
|
||||
dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf 22.36 kB
|
||||
dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf 24.50 kB
|
||||
dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 25.32 kB
|
||||
dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 26.27 kB
|
||||
dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf 27.56 kB
|
||||
dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 28.08 kB
|
||||
dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff 29.91 kB
|
||||
dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff 30.77 kB
|
||||
dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf 31.20 kB
|
||||
dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf 31.31 kB
|
||||
dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf 32.97 kB
|
||||
dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff 33.52 kB
|
||||
dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf 33.58 kB
|
||||
dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf 51.34 kB
|
||||
dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf 53.58 kB
|
||||
dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf 63.63 kB
|
||||
dist/assets/index-BYHTgAJJ.css 36.82 kB │ gzip: 10.03 kB
|
||||
dist/assets/channel-BWaIGDQK.js 0.11 kB │ gzip: 0.12 kB
|
||||
dist/assets/init-Gi6I4Gst.js 0.15 kB │ gzip: 0.13 kB
|
||||
dist/assets/chunk-2Q5K7J3B-Bh00PV7B.js 0.19 kB │ gzip: 0.16 kB
|
||||
dist/assets/chunk-JWPE2WC7-zllJnyes.js 0.22 kB │ gzip: 0.17 kB
|
||||
dist/assets/chunk-XXDRQBXY-D7xLWH32.js 0.22 kB │ gzip: 0.20 kB
|
||||
dist/assets/chunk-5VM5RSS4-DwRnlXdD.js 0.36 kB │ gzip: 0.27 kB
|
||||
dist/assets/stateDiagram-v2-MP3YSRHH-CMni1PkL.js 0.39 kB │ gzip: 0.29 kB
|
||||
dist/assets/classDiagram-ZZMXUADV-D-sblmQI.js 0.43 kB │ gzip: 0.30 kB
|
||||
dist/assets/classDiagram-v2-VYDZK3BY-D-sblmQI.js 0.43 kB │ gzip: 0.30 kB
|
||||
dist/assets/chunk-POPQ4Y6H-CV-hKTcv.js 0.50 kB │ gzip: 0.35 kB
|
||||
dist/assets/swimlanesDiagram-VR7AAH4N-CSMO0mBI.js 0.51 kB │ gzip: 0.35 kB
|
||||
dist/assets/infoDiagram-27XIBGKW-fa-zM8z6.js 0.55 kB │ gzip: 0.38 kB
|
||||
dist/assets/sizeCapture-INFHLROL-DDBMTQik.js 0.90 kB │ gzip: 0.51 kB
|
||||
dist/assets/ordinal-Cboi1Yqb.js 1.19 kB │ gzip: 0.57 kB
|
||||
dist/assets/railroadDiagram-O6MQD6OU-BKJZqzzT.js 1.57 kB │ gzip: 0.73 kB
|
||||
dist/assets/abnfDiagram-VCTEODGH-DMcEZewO.js 1.77 kB │ gzip: 0.87 kB
|
||||
dist/assets/chunk-F27PBJKO-usLA3fwD.js 1.88 kB │ gzip: 0.83 kB
|
||||
dist/assets/pegDiagram-XKGWAZYB-D5t7eX_t.js 1.92 kB │ gzip: 0.90 kB
|
||||
dist/assets/ebnfDiagram-PWID7BFC-RqHNt9tT.js 1.97 kB │ gzip: 0.86 kB
|
||||
dist/assets/arc-DzBaewGB.js 3.43 kB │ gzip: 1.48 kB
|
||||
dist/assets/diagram-Z3DM3KII-DaRdRaxA.js 4.18 kB │ gzip: 1.81 kB
|
||||
dist/assets/defaultLocale-DX6XiGOO.js 4.69 kB │ gzip: 2.18 kB
|
||||
dist/assets/linear--qpe2Iaf.js 5.65 kB │ gzip: 2.30 kB
|
||||
dist/assets/diagram-UQ7AKVKN-RZXoXHTp.js 6.04 kB │ gzip: 2.60 kB
|
||||
dist/assets/pieDiagram-E7YTZNPT-Dn0Lbwkg.js 6.26 kB │ gzip: 2.60 kB
|
||||
dist/assets/diagram-S7CK7UJ4-yML3LoUw.js 8.18 kB │ gzip: 3.65 kB
|
||||
dist/assets/cynefinDiagram-5FMLGOSQ-CALcOVS7.js 10.06 kB │ gzip: 3.61 kB
|
||||
dist/assets/stateDiagram-D77RDMKH-BSRSjCXA.js 10.33 kB │ gzip: 3.61 kB
|
||||
dist/assets/dagre-GXQ25YYZ-Fc0ngcGC.js 10.64 kB │ gzip: 4.01 kB
|
||||
dist/assets/diagram-VSXAHHWV-D-Jngocg.js 10.68 kB │ gzip: 4.11 kB
|
||||
dist/assets/chunk-SVP7TREG-Xn5LeAbu.js 15.73 kB │ gzip: 4.41 kB
|
||||
dist/assets/diagram-VX7I27RA-CqVb28LM.js 15.92 kB │ gzip: 5.68 kB
|
||||
dist/assets/ishikawaDiagram-5VMMS53U-DYKYYyrU.js 17.43 kB │ gzip: 6.62 kB
|
||||
dist/assets/kanban-definition-UXKFOSKX-D1DPeYzD.js 20.26 kB │ gzip: 7.18 kB
|
||||
dist/assets/mindmap-definition-YA3MSWOX-BWGNSZTH.js 23.14 kB │ gzip: 7.80 kB
|
||||
dist/assets/sankeyDiagram-P5KCCOFB-CqHgXeOu.js 23.16 kB │ gzip: 8.54 kB
|
||||
dist/assets/journeyDiagram-3NMN7TZE-DereIpCw.js 23.54 kB │ gzip: 8.33 kB
|
||||
dist/assets/wardleyDiagram-VM6X3IG4-CyLHCCcx.js 25.47 kB │ gzip: 6.77 kB
|
||||
dist/assets/gitGraphDiagram-WWUBYQGX-CVvsIktm.js 29.38 kB │ gzip: 8.68 kB
|
||||
dist/assets/erDiagram-RLTQ6QDP-B7deKO5n.js 30.76 kB │ gzip: 10.57 kB
|
||||
dist/assets/timeline-definition-24CTP7MA-DO4WJS9A.js 30.79 kB │ gzip: 10.13 kB
|
||||
dist/assets/requirementDiagram-BXWQKSXE-B_V5PvFV.js 30.97 kB │ gzip: 9.71 kB
|
||||
dist/assets/quadrantDiagram-AXDQQJYC-Bz6zCFqR.js 33.98 kB │ gzip: 9.95 kB
|
||||
dist/assets/layout-C2imKr5O.js 35.43 kB │ gzip: 12.74 kB
|
||||
dist/assets/chunk-IMKFNOWR-C-Mq94A_.js 38.07 kB │ gzip: 12.51 kB
|
||||
dist/assets/blockDiagram-I7D4REHJ-rec6-G1Z.js 41.72 kB │ gzip: 13.56 kB
|
||||
dist/assets/vennDiagram-4TSXK5OY-rRqEaYZi.js 41.93 kB │ gzip: 15.67 kB
|
||||
dist/assets/xychartDiagram-S5SC5T6Z-DWXv4LlT.js 44.47 kB │ gzip: 12.58 kB
|
||||
dist/assets/chunk-TICWLB2K-yUyCyRlD.js 49.17 kB │ gzip: 15.74 kB
|
||||
dist/assets/flowDiagram-HODETNUW-DwTSI7cN.js 61.84 kB │ gzip: 19.87 kB
|
||||
dist/assets/c4Diagram-7LVT6UL2-kvq1lpAQ.js 65.44 kB │ gzip: 18.77 kB
|
||||
dist/assets/ganttDiagram-EL5Y4UJY-BPXbiJG9.js 69.66 kB │ gzip: 23.54 kB
|
||||
dist/assets/cose-bilkent-JH36ORCC-BIMvB8di.js 81.66 kB │ gzip: 22.46 kB
|
||||
dist/assets/swimlanes-42K2YHIH-DgiZ0DVD.js 115.76 kB │ gzip: 42.49 kB
|
||||
dist/assets/sequenceDiagram-WJ2MYXX4-CsY4WXBM.js 116.93 kB │ gzip: 31.11 kB
|
||||
dist/assets/architectureDiagram-5GKGNRK7-CG8j7D4N.js 151.79 kB │ gzip: 43.12 kB
|
||||
dist/assets/katex-HP8lGamR.js 261.31 kB │ gzip: 77.57 kB
|
||||
dist/assets/cytoscape.esm-BB4DxJjf.js 443.04 kB │ gzip: 141.98 kB
|
||||
dist/assets/cynefin-OW5HDTMX-Dit8THDr.js 687.91 kB │ gzip: 154.14 kB
|
||||
dist/assets/index-DhyNmxJE.js 2,829.14 kB │ gzip: 805.59 kB
|
||||
|
||||
(!) Some chunks are larger than 500 kB after minification. Consider:
|
||||
- Using dynamic import() to code-split the application
|
||||
- Use build.rollupOptions.output.manualChunks to improve chunking: https://rollupjs.org/configuration-options/#output-manualchunks
|
||||
- Adjust chunk size limit for this warning via build.chunkSizeWarningLimit.
|
||||
✓ built in 7.59s
|
||||
precompress: 49 candidate(s) under /home/weichen/projects/Agent-System/web/dist
|
||||
br assets/abnfDiagram-VCTEODGH-DMcEZewO.js 1774 → 798 (−55.0%)
|
||||
gzip assets/abnfDiagram-VCTEODGH-DMcEZewO.js 1774 → 872 (−50.8%)
|
||||
br assets/arc-DzBaewGB.js 3434 → 1285 (−62.6%)
|
||||
gzip assets/arc-DzBaewGB.js 3434 → 1449 (−57.8%)
|
||||
br assets/architectureDiagram-5GKGNRK7-CG8j7D4N.js 151788 → 36902 (−75.7%)
|
||||
gzip assets/architectureDiagram-5GKGNRK7-CG8j7D4N.js 151788 → 42819 (−71.8%)
|
||||
br assets/blockDiagram-I7D4REHJ-rec6-G1Z.js 41722 → 11938 (−71.4%)
|
||||
gzip assets/blockDiagram-I7D4REHJ-rec6-G1Z.js 41722 → 13449 (−67.8%)
|
||||
br assets/c4Diagram-7LVT6UL2-kvq1lpAQ.js 65438 → 15174 (−76.8%)
|
||||
gzip assets/c4Diagram-7LVT6UL2-kvq1lpAQ.js 65438 → 18662 (−71.5%)
|
||||
br assets/chunk-F27PBJKO-usLA3fwD.js 1876 → 730 (−61.1%)
|
||||
gzip assets/chunk-F27PBJKO-usLA3fwD.js 1876 → 828 (−55.9%)
|
||||
br assets/chunk-IMKFNOWR-C-Mq94A_.js 38069 → 10963 (−71.2%)
|
||||
gzip assets/chunk-IMKFNOWR-C-Mq94A_.js 38069 → 12427 (−67.4%)
|
||||
br assets/chunk-SVP7TREG-Xn5LeAbu.js 15728 → 3959 (−74.8%)
|
||||
gzip assets/chunk-SVP7TREG-Xn5LeAbu.js 15728 → 4413 (−71.9%)
|
||||
br assets/chunk-TICWLB2K-yUyCyRlD.js 49171 → 13023 (−73.5%)
|
||||
gzip assets/chunk-TICWLB2K-yUyCyRlD.js 49171 → 15643 (−68.2%)
|
||||
br assets/cose-bilkent-JH36ORCC-BIMvB8di.js 81662 → 19567 (−76.0%)
|
||||
gzip assets/cose-bilkent-JH36ORCC-BIMvB8di.js 81662 → 22373 (−72.6%)
|
||||
br assets/cynefin-OW5HDTMX-Dit8THDr.js 687914 → 124585 (−81.9%)
|
||||
gzip assets/cynefin-OW5HDTMX-Dit8THDr.js 687914 → 153069 (−77.7%)
|
||||
br assets/cynefinDiagram-5FMLGOSQ-CALcOVS7.js 10062 → 3233 (−67.9%)
|
||||
gzip assets/cynefinDiagram-5FMLGOSQ-CALcOVS7.js 10062 → 3598 (−64.2%)
|
||||
br assets/cytoscape.esm-BB4DxJjf.js 443036 → 119602 (−73.0%)
|
||||
gzip assets/cytoscape.esm-BB4DxJjf.js 443036 → 141741 (−68.0%)
|
||||
br assets/dagre-GXQ25YYZ-Fc0ngcGC.js 10641 → 3659 (−65.6%)
|
||||
gzip assets/dagre-GXQ25YYZ-Fc0ngcGC.js 10641 → 4024 (−62.2%)
|
||||
br assets/defaultLocale-DX6XiGOO.js 4688 → 2012 (−57.1%)
|
||||
gzip assets/defaultLocale-DX6XiGOO.js 4688 → 2166 (−53.8%)
|
||||
br assets/diagram-S7CK7UJ4-yML3LoUw.js 8182 → 3307 (−59.6%)
|
||||
gzip assets/diagram-S7CK7UJ4-yML3LoUw.js 8182 → 3662 (−55.2%)
|
||||
br assets/diagram-UQ7AKVKN-RZXoXHTp.js 6044 → 2343 (−61.2%)
|
||||
gzip assets/diagram-UQ7AKVKN-RZXoXHTp.js 6044 → 2596 (−57.0%)
|
||||
br assets/diagram-VSXAHHWV-D-Jngocg.js 10681 → 3764 (−64.8%)
|
||||
gzip assets/diagram-VSXAHHWV-D-Jngocg.js 10681 → 4132 (−61.3%)
|
||||
br assets/diagram-VX7I27RA-CqVb28LM.js 15922 → 5130 (−67.8%)
|
||||
gzip assets/diagram-VX7I27RA-CqVb28LM.js 15922 → 5689 (−64.3%)
|
||||
br assets/diagram-Z3DM3KII-DaRdRaxA.js 4176 → 1626 (−61.1%)
|
||||
gzip assets/diagram-Z3DM3KII-DaRdRaxA.js 4176 → 1800 (−56.9%)
|
||||
br assets/ebnfDiagram-PWID7BFC-RqHNt9tT.js 1970 → 784 (−60.2%)
|
||||
gzip assets/ebnfDiagram-PWID7BFC-RqHNt9tT.js 1970 → 862 (−56.2%)
|
||||
br assets/erDiagram-RLTQ6QDP-B7deKO5n.js 30755 → 9152 (−70.2%)
|
||||
gzip assets/erDiagram-RLTQ6QDP-B7deKO5n.js 30755 → 10522 (−65.8%)
|
||||
br assets/flowDiagram-HODETNUW-DwTSI7cN.js 61840 → 16342 (−73.6%)
|
||||
gzip assets/flowDiagram-HODETNUW-DwTSI7cN.js 61840 → 19714 (−68.1%)
|
||||
br assets/ganttDiagram-EL5Y4UJY-BPXbiJG9.js 69656 → 20654 (−70.3%)
|
||||
gzip assets/ganttDiagram-EL5Y4UJY-BPXbiJG9.js 69656 → 23405 (−66.4%)
|
||||
br assets/gitGraphDiagram-WWUBYQGX-CVvsIktm.js 29382 → 7766 (−73.6%)
|
||||
gzip assets/gitGraphDiagram-WWUBYQGX-CVvsIktm.js 29382 → 8676 (−70.5%)
|
||||
br assets/index-BYHTgAJJ.css 36824 → 8557 (−76.8%)
|
||||
gzip assets/index-BYHTgAJJ.css 36824 → 9952 (−73.0%)
|
||||
br assets/index-DhyNmxJE.js 2829141 → 625981 (−77.9%)
|
||||
gzip assets/index-DhyNmxJE.js 2829141 → 798236 (−71.8%)
|
||||
br assets/ishikawaDiagram-5VMMS53U-DYKYYyrU.js 17434 → 5995 (−65.6%)
|
||||
gzip assets/ishikawaDiagram-5VMMS53U-DYKYYyrU.js 17434 → 6612 (−62.1%)
|
||||
br assets/journeyDiagram-3NMN7TZE-DereIpCw.js 23536 → 7492 (−68.2%)
|
||||
gzip assets/journeyDiagram-3NMN7TZE-DereIpCw.js 23536 → 8314 (−64.7%)
|
||||
br assets/kanban-definition-UXKFOSKX-D1DPeYzD.js 20256 → 6416 (−68.3%)
|
||||
gzip assets/kanban-definition-UXKFOSKX-D1DPeYzD.js 20256 → 7151 (−64.7%)
|
||||
br assets/katex-HP8lGamR.js 261313 → 63694 (−75.6%)
|
||||
gzip assets/katex-HP8lGamR.js 261313 → 77109 (−70.5%)
|
||||
br assets/layout-C2imKr5O.js 35429 → 11552 (−67.4%)
|
||||
gzip assets/layout-C2imKr5O.js 35429 → 12742 (−64.0%)
|
||||
br assets/linear--qpe2Iaf.js 5654 → 2120 (−62.5%)
|
||||
gzip assets/linear--qpe2Iaf.js 5654 → 2311 (−59.1%)
|
||||
br assets/mindmap-definition-YA3MSWOX-BWGNSZTH.js 23140 → 6990 (−69.8%)
|
||||
gzip assets/mindmap-definition-YA3MSWOX-BWGNSZTH.js 23140 → 7777 (−66.4%)
|
||||
br assets/ordinal-Cboi1Yqb.js 1189 → 497 (−58.2%)
|
||||
gzip assets/ordinal-Cboi1Yqb.js 1189 → 574 (−51.7%)
|
||||
br assets/pegDiagram-XKGWAZYB-D5t7eX_t.js 1921 → 803 (−58.2%)
|
||||
gzip assets/pegDiagram-XKGWAZYB-D5t7eX_t.js 1921 → 904 (−52.9%)
|
||||
br assets/pieDiagram-E7YTZNPT-Dn0Lbwkg.js 6259 → 2379 (−62.0%)
|
||||
gzip assets/pieDiagram-E7YTZNPT-Dn0Lbwkg.js 6259 → 2608 (−58.3%)
|
||||
br assets/quadrantDiagram-AXDQQJYC-Bz6zCFqR.js 33975 → 8492 (−75.0%)
|
||||
gzip assets/quadrantDiagram-AXDQQJYC-Bz6zCFqR.js 33975 → 9854 (−71.0%)
|
||||
br assets/railroadDiagram-O6MQD6OU-BKJZqzzT.js 1571 → 663 (−57.8%)
|
||||
gzip assets/railroadDiagram-O6MQD6OU-BKJZqzzT.js 1571 → 734 (−53.3%)
|
||||
br assets/requirementDiagram-BXWQKSXE-B_V5PvFV.js 30965 → 8236 (−73.4%)
|
||||
gzip assets/requirementDiagram-BXWQKSXE-B_V5PvFV.js 30965 → 9638 (−68.9%)
|
||||
br assets/sankeyDiagram-P5KCCOFB-CqHgXeOu.js 23163 → 7806 (−66.3%)
|
||||
gzip assets/sankeyDiagram-P5KCCOFB-CqHgXeOu.js 23163 → 8545 (−63.1%)
|
||||
br assets/sequenceDiagram-WJ2MYXX4-CsY4WXBM.js 116927 → 25725 (−78.0%)
|
||||
gzip assets/sequenceDiagram-WJ2MYXX4-CsY4WXBM.js 116927 → 30934 (−73.5%)
|
||||
br assets/stateDiagram-D77RDMKH-BSRSjCXA.js 10326 → 3305 (−68.0%)
|
||||
gzip assets/stateDiagram-D77RDMKH-BSRSjCXA.js 10326 → 3606 (−65.1%)
|
||||
br assets/swimlanes-42K2YHIH-DgiZ0DVD.js 115756 → 36638 (−68.3%)
|
||||
gzip assets/swimlanes-42K2YHIH-DgiZ0DVD.js 115756 → 42149 (−63.6%)
|
||||
br assets/timeline-definition-24CTP7MA-DO4WJS9A.js 30794 → 9120 (−70.4%)
|
||||
gzip assets/timeline-definition-24CTP7MA-DO4WJS9A.js 30794 → 10124 (−67.1%)
|
||||
br assets/vennDiagram-4TSXK5OY-rRqEaYZi.js 41926 → 13970 (−66.7%)
|
||||
gzip assets/vennDiagram-4TSXK5OY-rRqEaYZi.js 41926 → 15620 (−62.7%)
|
||||
br assets/wardleyDiagram-VM6X3IG4-CyLHCCcx.js 25467 → 6076 (−76.1%)
|
||||
gzip assets/wardleyDiagram-VM6X3IG4-CyLHCCcx.js 25467 → 6733 (−73.6%)
|
||||
br assets/xychartDiagram-S5SC5T6Z-DWXv4LlT.js 44474 → 11050 (−75.2%)
|
||||
gzip assets/xychartDiagram-S5SC5T6Z-DWXv4LlT.js 44474 → 12519 (−71.9%)
|
||||
br fonts/OFL.txt 4474 → 1437 (−67.9%)
|
||||
gzip fonts/OFL.txt 4474 → 1980 (−55.7%)
|
||||
precompress: wrote 49 .br and 49 .gz
|
||||
@@ -0,0 +1,516 @@
|
||||
|
||||
> shuimu-workbench@1.0.0 test
|
||||
> tsx --test "src/**/!(*InteractiveCard*|*chatWindowArtifact*|*deliverableSync*).test.ts" && vitest run src/components/chat/InteractiveCard.test.ts src/components/chat/chatWindowArtifact.test.ts src/components/chat/deliverableSync.test.ts
|
||||
|
||||
✔ AuthClient: 基础登录与 .env 超级管理员鉴权 (2.133367ms)
|
||||
✔ AuthClient: 自主注册受控流程(仅填用户名密码姓名,职位强制绑定访客待指派) (0.566773ms)
|
||||
✔ AuthClient: 开启注册需审批时,注册账号处于待审状态并禁止登录 (0.64474ms)
|
||||
✔ AuthClient: 管理员创建账号必须在首次登录修改密码 (0.3182ms)
|
||||
✔ mapUserAccount: snake_case Go contract → camelCase (3.0288ms)
|
||||
✔ mapUserAccount: prefers snake_case when both present (0.246143ms)
|
||||
✔ mapLoginResult + mapMeResult map nested user and top-level must_change_password (1.00316ms)
|
||||
✔ mapUserAccountList maps admin users list (0.245342ms)
|
||||
✔ AuthClient.login + fetchMe map real snake_case wire payloads (30.061187ms)
|
||||
✔ shouldAutoRetryChat: disabled until idempotency (1.301892ms)
|
||||
✔ shouldAutoRetryTask: transient only, max 2 (0.289761ms)
|
||||
✔ shouldAutoRetryTask: document busy never retries (0.166445ms)
|
||||
✔ shouldAutoRetry alias delegates to task path (0.199004ms)
|
||||
✔ autoRetryDelayMs: 1s then 2s (0.160375ms)
|
||||
✔ fallbackErrorClass: network → transient, workspace → resumable (0.182954ms)
|
||||
✔ upsertChatSession surfaces safe INTERNAL envelope instead of raw JSON text (29.059697ms)
|
||||
[sse] 忽略无法解析的帧: {not valid json SyntaxError: Expected property name or '}' in JSON at position 1 (line 1 column 2)
|
||||
at JSON.parse (<anonymous>)
|
||||
at parseFrame (/home/weichen/projects/Agent-System/web/src/api/chatStream.ts:123:26)
|
||||
at parseSSEChunk (/home/weichen/projects/Agent-System/web/src/api/chatStream.ts:147:20)
|
||||
at TestContext.<anonymous> (/home/weichen/projects/Agent-System/web/src/api/chatStream.test.ts:41:31)
|
||||
at Test.runInAsyncScope (node:async_hooks:226:14)
|
||||
at Test.run (node:internal/test_runner/test:1402:25)
|
||||
at Test.processPendingSubtests (node:internal/test_runner/test:974:18)
|
||||
at Test.postRun (node:internal/test_runner/test:1542:19)
|
||||
at Test.run (node:internal/test_runner/test:1467:12)
|
||||
at async Test.processPendingSubtests (node:internal/test_runner/test:974:7)
|
||||
✔ parseSSEChunk: parses event and data lines (1.468046ms)
|
||||
✔ parseSSEChunk: keeps incomplete frame in rest (0.287101ms)
|
||||
✔ parseSSEChunk: flush parses final frame without trailing delimiter (0.213084ms)
|
||||
✔ parseSSEChunk: ignores heartbeat comments and keeps last id (0.373858ms)
|
||||
✔ parseSSEChunk: skips a single malformed frame and keeps parsing the next valid frame (4.605394ms)
|
||||
▶ docgov lifecycle API client
|
||||
✔ claimDocument posts to /api/v1/docgov/claim with author role (29.178773ms)
|
||||
✔ submitForReview posts to /api/v1/docgov/submit-review (4.795558ms)
|
||||
✔ reviewDecision posts action and optional reason (1.0064ms)
|
||||
✔ approverDecision posts action and rollback_to (1.32506ms)
|
||||
✔ proposeModification posts to /api/v1/docgov/proposal (0.863795ms)
|
||||
✔ decideModificationProposal posts to /api/v1/docgov/proposal/decide (0.648451ms)
|
||||
✔ docgov lifecycle API client (39.666403ms)
|
||||
✔ updateFreeformDoc exposes CONCURRENCY_CONFLICT on 409 (29.458175ms)
|
||||
✔ addProjectMember exposes structured ProjectSettingsApiError on 403 (28.434875ms)
|
||||
✔ updateProjectMember surfaces safe INTERNAL message on 500 duty-sync failure (0.799906ms)
|
||||
▶ projectSettingsError
|
||||
✔ keeps status, code, message and issues on PLAN_FROZEN (3.211875ms)
|
||||
✔ maps revision and concurrent conflicts to distinct recovery (0.375669ms)
|
||||
✔ joins validation issues but still keeps typed codes (0.244912ms)
|
||||
✔ prefers envelope code over legacy error field (0.170515ms)
|
||||
✔ unwraps nested business JSON inside VERSION_CONFLICT envelope (0.223584ms)
|
||||
✔ maps bare VERSION_CONFLICT envelope to retry_after_switch (0.139156ms)
|
||||
✔ retains the local draft when a late tab hits PLAN_FROZEN (0.141246ms)
|
||||
✔ prefers error_code over legacy bare error string (DOC_BUSY) (0.154446ms)
|
||||
✔ projectSettingsError (6.560635ms)
|
||||
✔ Project workflow types validation (1.182045ms)
|
||||
✔ createTaskRun exposes DOC_BUSY detail without a retryable run (26.62498ms)
|
||||
✔ parseSSEChunk tracks id seq for after_seq reconnect (0.34205ms)
|
||||
✔ subscribeTaskRunEvents reconnects on EOF with advancing after_seq (1.808246ms)
|
||||
✔ readTaskRunSSEOnce marks non-terminal EOF for reconnect (0.467346ms)
|
||||
✔ subscribeTaskRunEvents fails fast on 401 without retrying (0.478205ms)
|
||||
✔ subscribeTaskRunEvents fails fast on 404 without retrying (0.381409ms)
|
||||
✔ buildTaskRequest: maps five project fields + task_key + user identity (1.085558ms)
|
||||
✔ buildTaskRequest: omits optional fields when absent (0.228113ms)
|
||||
✔ buildTaskRequest: includes materials when provided and non-empty (0.941982ms)
|
||||
✔ buildResumeRequest: adds work_dir on top of task request (0.196504ms)
|
||||
✔ buildTaskRequest: canonicalizes legacy doc-p doc_id (0.322401ms)
|
||||
✔ buildTaskRequest: UI-05 pinned intake keeps task_key + doc_proj6109_intake (0.34661ms)
|
||||
✔ buildResumeRequest: forwards add_steps when provided (0.194204ms)
|
||||
✔ CategoryDock: 渲染顶部 🔔 未读提醒项及角标数字 (14.108511ms)
|
||||
✔ CategoryDock: isAllReadCompleted 为 true 时渲染完成态提示 (11.12551ms)
|
||||
✔ CategoryDock: 0 未读时不渲染红色未读角标 (4.135498ms)
|
||||
✔ CategoryDock: 分类项包含依次跳转与遍历提示信息 (7.728221ms)
|
||||
✔ InputArea keeps draft when send preflight fails (1.304392ms)
|
||||
✔ InputArea clears draft only after accepted send (0.844385ms)
|
||||
✔ InputArea shows thinking block message (0.213844ms)
|
||||
✔ InputArea keeps draft when attachment registration fails (0.31741ms)
|
||||
✔ isTemplateOnly 交付物应正确识别为初始模板展示态 (0.895844ms)
|
||||
✔ 左侧栏鼠标严密绝对跟随与夹紧测试 (Left Rail Exact Pointer Following & Clamping) (0.695719ms)
|
||||
✔ 右侧栏鼠标严密绝对跟随与反向位移测试 (Right Rail Exact Pointer Following & Inverted Delta) (0.141406ms)
|
||||
✔ detects legacy mock and neuropulse/ten-projects ids (0.896604ms)
|
||||
✔ detects colliding demo project ids (0.197314ms)
|
||||
✔ applyRestoredFileRaw: 以 prev 为权威,只补丁缺失 fileRaw,不丢新增条目 (1.244303ms)
|
||||
✔ applyRestoredFileRaw: 无可用 fileRaw 时返回同一引用 (0.219584ms)
|
||||
✔ 每人独立私聊:同一项目不同用户拥有不同 sessionId (0.955712ms)
|
||||
✔ 左侧会话列表按当前用户过滤,不可见他人私聊 (0.288671ms)
|
||||
✔ 消息严格归属各自私聊 session,无跨用户混聊 (1.960681ms)
|
||||
✔ 切换用户时 resolve 到该用户同项目私聊会话 (0.272712ms)
|
||||
✔ resolve 优先有消息的私聊,跳过空壳 sess-p{N}-u{id} (0.368869ms)
|
||||
✔ ensurePrivateSessionsForUser 为缺失用户补齐空私聊 (0.523965ms)
|
||||
✔ 多项目会话消息隔离测试仍成立(endo vs ultrasound) (0.271302ms)
|
||||
✔ window cache LRU: 驱逐带 userId 前缀的完整 cache key,不留孤儿条目 (4.70933ms)
|
||||
✔ window cache: 读写同一 userId 前缀,跨用户不串读 (0.290791ms)
|
||||
✔ parseAnchorHash: 正确解析标准深链 hash (1.728019ms)
|
||||
✔ formatAnchorHash: 正确构造深链格式 (0.171455ms)
|
||||
✔ navigateToAnchor: 快路径直接命中 DOM 节点 (0.415668ms)
|
||||
✔ navigateToAnchor: 慢路径拉取分片后命中 (80.445082ms)
|
||||
✔ resolveTurnAnchorId: 方案 A 轮次归一化测试 (1.319631ms)
|
||||
✔ 全链路场景 1: 用户自主受控注册(仅填用户名/密码/姓名,职位严格绑定访客待指派) (1.70836ms)
|
||||
✔ 全链路场景 2: 系统准入策略(关闭自主注册与开启强制审批流转) (1.01094ms)
|
||||
✔ 全链路场景 3: 管理员开通账号首次强制改密闭环 (0.332621ms)
|
||||
✔ 全链路场景 4: 环境标识随登录会话返回(dev/test/main) (1.34228ms)
|
||||
✔ cleanHeadingContent: 手工多级序号前缀清洗与专业词汇保护 (1.149606ms)
|
||||
✔ 标题多层级自动编号:单 H1 文档总标题自适应模式 (1 -> 1.1 -> 1.1.1) (0.561403ms)
|
||||
✔ 标题多层级自动编号:多 H1 标准大章模式 (0.188184ms)
|
||||
✔ 跨章节图、表、公式多级按章重编号测试 (表 1-1, 表 2-1, 图 3-1, 式 (4-1)) (0.153726ms)
|
||||
✔ 有序列表与无序列表编号与渲染功能测试 (2.176245ms)
|
||||
✔ 动态块操作闭环重编号验证 (Insert, Move, Delete) (0.255153ms)
|
||||
✔ findMatchingBlockForReview: 能够精准按审查关键词(电气安全/EMC/竞品)定位段落块 (0.862065ms)
|
||||
✔ findMatchingBlockForReview: 能够按外部指定 hint 线索精确命中目标块 (0.880684ms)
|
||||
✔ createRejectConfirmationCard: 生成合规危险级驳回门禁卡片 (0.532534ms)
|
||||
✔ isAffirmativeResponse: 准确识别双轨交互中的肯定意图输入 (0.186954ms)
|
||||
✔ findLastActiveCard: 正确在会话历史中提取最新的待决策卡片 (0.160155ms)
|
||||
✔ decrementActiveCardsTtl: 多轮对话超时递减与自动失效归档 (0.135016ms)
|
||||
✔ applyConfirmRejectToDoc & applyUndoRejectToDoc: 驳回挂载批注与60s窗口期撤销完整闭环 (0.260713ms)
|
||||
✔ resolveCardInMessages & cancelCardInMessages: 消息流中卡片终态沉淀 (0.186915ms)
|
||||
✔ classifyRejectUtterance: 询问原因 → QUERY_REJECT_REASON (1.979941ms)
|
||||
✔ classifyRejectUtterance: 明确执行 → EXECUTE_REJECT (0.167865ms)
|
||||
✔ classifyRejectUtterance: 模糊 → OTHER (0.230323ms)
|
||||
✔ resolveDocGovCard: 编制人查询 → 信息卡且无 confirm_reject (0.806726ms)
|
||||
✔ resolveDocGovCard: 审核人执行 + reviewing → 确认卡 (0.285162ms)
|
||||
✔ resolveDocGovCard: 编制人误说执行驳回 → 无权纯文本 (0.133506ms)
|
||||
✔ resolveDocGovCard: 批准人 + approving → 终审确认卡 (0.31832ms)
|
||||
✔ resolveDocGovCard: 编制人 OTHER → 引导查询无危险按钮 (0.112437ms)
|
||||
✔ canConfirmReject: 仅审核/批准 (0.142306ms)
|
||||
✔ deriveFallbackCategory identifies categories correctly (0.964621ms)
|
||||
✔ enrichMessagesWithCategories backfills categories without overwriting existing ones (0.367539ms)
|
||||
✔ Markdown ➔ Block AST 解析测试 (标题自适应编号与图表公式全链路) (2.852725ms)
|
||||
✔ 动态重编号引擎测试 (多 H1 规范章节重排验证) (0.34643ms)
|
||||
✔ Block AST ➔ Markdown 双向无损往返还原测试 (0.487736ms)
|
||||
✔ 三层交付物多用户隐私绝对隔离测试 (Multi-user Strict Privacy Isolation) (1.03766ms)
|
||||
✔ 一键共享至项目参考库流转测试 (Promote to Shared Reference Test) (0.199674ms)
|
||||
✔ 撤回共享恢复私有草稿测试 (Revoke Shared Reference Test) (0.376889ms)
|
||||
✔ 受控工作流纯净性红线检验 (Workflow Purity Security Red Line) (0.135206ms)
|
||||
✔ isSameMessage: clientTurnId match (1.069979ms)
|
||||
✔ isSameMessage: serverId match without turn (0.254583ms)
|
||||
✔ filterPendingLiveMessages: real temp vs server id (0.255533ms)
|
||||
✔ mergeLiveOverHistory preserves server id from live (0.261122ms)
|
||||
✔ applyPhaseEvent: appends first phase as running node (1.227643ms)
|
||||
✔ applyPhaseEvent: completes predecessors when a new phase arrives (0.235553ms)
|
||||
✔ applyPhaseEvent: same phase refreshes label in place (no duplicate node) (0.256192ms)
|
||||
✔ applyPhaseEvent: unknown phase falls back to hourglass meta (0.165575ms)
|
||||
✔ applyPhaseEvent: prefers backend message over short label (1.00684ms)
|
||||
✔ applyPhaseEvent: step_completed closes running entry without appending (0.220524ms)
|
||||
✔ applyPhaseEvent: stage_completed closes entries (0.246493ms)
|
||||
✔ applyPhaseEvent: task_failed appends warning node (0.160845ms)
|
||||
✔ applyPhaseEvent: decorates label with step index / total steps (0.170125ms)
|
||||
✔ applyPhaseEvent: repeated step_executing with step_index gets separate nodes (0.210104ms)
|
||||
✔ applyPhaseEvent: ignores event without phase payload (0.105647ms)
|
||||
✔ applyPhaseEvent: step_failed marks running entry as failed (keeps step label) (0.155286ms)
|
||||
✔ applyPhaseEvent: step_failed appends failed node when no running entry exists (0.806356ms)
|
||||
✔ applyPhaseEvent: retry of a failed step resets it to running (0.142056ms)
|
||||
✔ PLAN_CONFIRM 卡片来自 PlanView 且不触达 AST (1.171055ms)
|
||||
✔ 409 冲突把卡片标为 outdated 并换上最新 revision (0.261942ms)
|
||||
✔ GET reconcile 用权威 PlanView 覆盖本地 executing,不假装成功 (0.254153ms)
|
||||
✔ 过期计划映射 expired (0.135756ms)
|
||||
✔ 非计划卡不被误判 (0.120206ms)
|
||||
✔ 集成验证:项目切换时交付物严格隔离,新建项目即刻包含 7 份模板副本 (5.541715ms)
|
||||
✔ 集成验证:三层隐私与项目物理隔离双重约束 (1.67508ms)
|
||||
✔ 用例 1: DEFAULT_ROLES 包含 5 种默认角色且具备预期权限 (1.02325ms)
|
||||
✔ 用例 2: 系统管理员 (ADMIN) 与具备 perm:manage 用户的通配豁免特权 (0.218684ms)
|
||||
✔ 用例 3: 常规工作流基线可见性 (workflow:view_all) 判定 (0.272901ms)
|
||||
✔ 用例 4: 单文档覆盖 - designated_only (仅编制/审核/批准人可见,其他用户被精确拦截) (2.114047ms)
|
||||
✔ 用例 5: 单文档覆盖 - role_whitelist (仅白名单内角色可见,其他角色被精确拦截) (0.328901ms)
|
||||
✔ 用例 6: 单文档覆盖 - public (放宽可见性,无 workflow:view_all 权限的访客也能查阅) (0.143076ms)
|
||||
✔ 用例 7: 私密文档隔离强红线 (category === private 无论任何管理员或单文档覆盖,非创建者绝对不可见) (0.265342ms)
|
||||
✔ 用例 8: 核心业务动作权限拦截判定 (canPerformDocAction & hasPermission) (0.183335ms)
|
||||
✔ 用例 9: filterVisibleDeliverablesWithRBAC 能够批处理过滤大盘交付物 (1.34669ms)
|
||||
✔ 集成场景 1: 动态新增合规审计员角色,并配置其仅能查阅特定单文档覆盖 (1.331621ms)
|
||||
✔ 集成场景 2: 服务端 capabilities 变更后权限即刻生效 (0.229343ms)
|
||||
✔ 集成场景 3: 单文档覆盖 designated_only 严格收紧与撤回恢复全流程 (0.407608ms)
|
||||
✔ renderInlineMarkdown: renders bold, italic, strikethrough (1.054229ms)
|
||||
✔ renderInlineMarkdown: escapes raw HTML tags to prevent XSS (0.447167ms)
|
||||
✔ renderInlineMarkdown: blocks javascript: link hrefs (0.150066ms)
|
||||
✔ renderInlineMarkdown: renders inline code and preserved line breaks (0.263912ms)
|
||||
✔ renderInlineMarkdown: renders headings lists and tables for chat replies (0.504775ms)
|
||||
✔ sanitizeMermaidCode: auto wraps unquoted labels containing brackets or comparison operators (0.285662ms)
|
||||
(node:442565) ExperimentalWarning: localStorage is not available because --localstorage-file was not provided.
|
||||
(Use `node --trace-warnings ...` to show where the warning was created)
|
||||
✔ DOCX 渲染器行内文本与公式解析测试 (parseInlineContent) (4.858686ms)
|
||||
✔ DOCX 渲染器 LaTeX 原生 OMML 转换引擎测试 (latexToOmml) (1.933843ms)
|
||||
✔ DOCX 渲染器 Mermaid 语法净化与容错测试 (sanitizeMermaidCode) (0.256492ms)
|
||||
✔ DOCX 渲染器 SVG 净化防 Canvas 污染测试 (cleanSvgForCanvas) (0.32988ms)
|
||||
✔ DOCX 渲染器全量编译测试 (buildDocxModel & exportDocumentToDocx) (56.318598ms)
|
||||
✔ XLSX 渲染器多表提取与工作簿编译测试 (buildXlsxWorkbook & exportDocumentToXlsx) (6.100099ms)
|
||||
▶ Reply Document IR & Export Engine
|
||||
✔ deriveReplyTitle and sanitizeFilename derive title and safe file name (1.03846ms)
|
||||
✔ deriveReplyTitle handles text without heading (0.255212ms)
|
||||
✔ parseReplyMarkdown parses headers, lists, code, and tables (1.447077ms)
|
||||
✔ renderClipboardHtml renders self-contained styling for office tools (0.448417ms)
|
||||
✔ renderClipboardHtml converts LaTeX formulas to native MathML for Word/Office (2.941853ms)
|
||||
✔ renderPlainText strips formatting cleanly (0.474056ms)
|
||||
✔ serializeMarkdown round-trips to valid markdown (0.368019ms)
|
||||
✔ stripUnsafeHtml strips script tags and dangerous event handlers (0.407498ms)
|
||||
✔ Reply Document IR & Export Engine (8.740511ms)
|
||||
✔ createProjectTemplateDeliverables: 必须精确生成 7 份全生命周期受控文档副本 (6.294284ms)
|
||||
✔ resolveRequirementsTaskKey prefers intake for 初始需求表 (1.105478ms)
|
||||
✔ resolveRequirementsTaskKey selects user_requirements for explicit URS (0.168585ms)
|
||||
✔ UI-02 first-round prompt with 首轮+用户需求+摸底 must be intake not user_requirements (0.616561ms)
|
||||
✔ 首轮 / 需求摸底 / 摸底 beat bare 用户需求 (0.123236ms)
|
||||
✔ bare 用户需求 without V0.1 markers stays user_requirements (0.092537ms)
|
||||
✔ matchTemplateDeliverable uses intake key when pool only has URS template (0.129236ms)
|
||||
✔ resolveTaskTemplateForMessage keeps intake key with URS-only pool (0.139596ms)
|
||||
✔ matchTemplateDeliverable prefers intake when both templates exist (0.109956ms)
|
||||
✔ read-only request does not start a document task: 总结一下用户初始需求表 (0.101807ms)
|
||||
✔ read-only request does not start a document task: 查看用户初始需求表 (0.131446ms)
|
||||
✔ read-only request does not start a document task: 用户初始需求表现在谁在编辑 (0.082077ms)
|
||||
✔ read-only request does not start a document task: 分析一下用户需求表里的风险 (0.056588ms)
|
||||
✔ read-only request does not start a document task: 用户初始需求表 (0.054759ms)
|
||||
✔ explicit write action still starts intake (0.088698ms)
|
||||
✔ explicit update action still starts URS (0.064458ms)
|
||||
✔ UI-05 prompt without active doc still keyword-routes to feasibility_reg (0.104627ms)
|
||||
✔ UI-05 prompt + registered intake pins intake and doc_proj6109_intake (0.247803ms)
|
||||
✔ read-only current-doc request does not start a task (0.082688ms)
|
||||
✔ template-only active slot is not pinned; keywords still apply (0.082097ms)
|
||||
✔ explicit named deliverable can switch away from active intake (0.218433ms)
|
||||
✔ domain 注册法规 without current-doc deixis still routes feasibility_reg (0.063428ms)
|
||||
✔ mismatched title vs docId suffix prefers docId taskKey (0.073868ms)
|
||||
✔ deixis 当前文档 pins registered intake (0.072117ms)
|
||||
✔ deixis 本文件 pins registered intake (0.039049ms)
|
||||
✔ deixis 这份文档 pins registered intake (0.0306ms)
|
||||
✔ deixis 本文档 pins registered intake (0.030609ms)
|
||||
✔ deixis 本受控文档 pins registered intake (0.035109ms)
|
||||
✔ no active doc does not invent a pin (0.069898ms)
|
||||
✔ UI-11 named 系统设计输入 beats negated 需求 background (0.052618ms)
|
||||
✔ mixed generate-design + do-not-cover-requirements stays prelim_design (0.039309ms)
|
||||
✔ current-doc pin still wins when no other deliverable is named (0.046759ms)
|
||||
▶ document revision overlay modes
|
||||
✔ pure hides all decorations (0.880784ms)
|
||||
✔ manual only marks human hunks (0.158156ms)
|
||||
✔ all marks human and agent differently (0.104947ms)
|
||||
✔ same human hunk stays amber in manual and all (no pairwise style guarantee) (0.167525ms)
|
||||
✔ exposes needs-content error code for empty orphan docs (0.100117ms)
|
||||
✔ matches overlay hunks by section or text when ids differ (0.289151ms)
|
||||
✔ aligns client block ids from head_block_map by order (0.239573ms)
|
||||
✔ document revision overlay modes (3.268573ms)
|
||||
▶ toolbar refresh + report entry reachability
|
||||
✔ pickLatestChangeReportId prefers highest version with report (0.349869ms)
|
||||
✔ first squad run alone still exposes a reachable report id (0.180424ms)
|
||||
✔ second squad run keeps both history report entries and latest shortcut (0.228394ms)
|
||||
✔ revisionRefreshKey is an independent refresh signal from docId (0.137756ms)
|
||||
✔ toolbar refresh + report entry reachability (1.223624ms)
|
||||
▶ change report view model
|
||||
✔ surfaces risk items, project impact, doc impacts with advice, and open questions (0.665951ms)
|
||||
✔ marks complete analysis as PI narrative vs factual fields (0.259053ms)
|
||||
✔ preferReportId prioritizes pending over committed latest (0.114127ms)
|
||||
✔ change report view model (1.217724ms)
|
||||
▶ revisionToolbarMount
|
||||
✔ exposes clear non-error template preview hint copy (0.579292ms)
|
||||
✔ template doc-p + isTemplateOnly=true does not mount (0.140756ms)
|
||||
✔ legacy doc-001 does not mount (0.139306ms)
|
||||
✔ unregistered doc-upload orphan does not mount (must register first) (0.129796ms)
|
||||
✔ server-registered upload (doc-free) and freeform mount (0.163895ms)
|
||||
✔ canonical doc_proj and generated non-template doc-p mount (0.102297ms)
|
||||
✔ revisionToolbarMount (3.055339ms)
|
||||
✔ useChatWindow: 所有 saveSessionWindowCache 调用必须传入 userId (1.204544ms)
|
||||
✔ window fetch race: stale response ignored by generation (1.35847ms)
|
||||
✔ mapRecordToMessage: 正确映射服务端消息记录为前端 ChatMessage (1.599323ms)
|
||||
✔ refresh window: 空 messages 日桶不算有效窗口 (0.200014ms)
|
||||
✔ mapRawBucket: 正确映射服务端按日分片桶 (0.691669ms)
|
||||
✔ Category navigation: chip 选中后准确匹配目标分类最新轮次并触发归一化定位 (0.290012ms)
|
||||
✔ turn runtime: session A stream survives session B switch (0.959152ms)
|
||||
✔ abortAllSessions aborts controllers across all sessions (0.307491ms)
|
||||
✔ syncAdminUserDirectories calls local reload then parent onRefreshUsers (1.726478ms)
|
||||
✔ syncAdminUserDirectories fails closed when onRefreshUsers is a dead/missing prop (0.695379ms)
|
||||
✔ syncAdminUserDirectories surfaces parent refresh errors (no silent swallow) (0.288862ms)
|
||||
✔ afterProjectMemberPersisted syncs members then refreshes workflow docs (1.274302ms)
|
||||
✔ afterProjectMemberPersisted does not refresh when member sync fails (0.416957ms)
|
||||
▶ blockEditorDialogPolicy
|
||||
✔ pins an accessible dialog label and exact save name (0.856915ms)
|
||||
✔ rejects page-wide fuzzy /保存/ that would hit private-library buttons (0.355239ms)
|
||||
✔ blockEditorDialogPolicy (2.452337ms)
|
||||
▶ resolveSaveTargetBlock
|
||||
✔ keeps the editor and does not fake success when the block id is gone (0.69134ms)
|
||||
✔ never retargets another block that happens to share the same content (0.859235ms)
|
||||
✔ resolveSaveTargetBlock (2.641202ms)
|
||||
▶ editor session document binding
|
||||
✔ fail-closes when activeDocId drifts away from the opened editor doc (0.255842ms)
|
||||
✔ accepts a save only when the editor is still bound to the active doc (0.158825ms)
|
||||
✔ blocks switching documents while the editor is open (0.173725ms)
|
||||
✔ editor session document binding (0.98624ms)
|
||||
▶ interpretBlockSaveError
|
||||
✔ 409 keeps the draft in the editor (0.223793ms)
|
||||
✔ other PATCH failures also keep the editor (4.524666ms)
|
||||
✔ interpretBlockSaveError (5.0373ms)
|
||||
▶ blockedGovernanceCopy
|
||||
✔ recognizes blocked run status and fixed hint, not generation success (0.713689ms)
|
||||
✔ blockedGovernanceCopy (1.562914ms)
|
||||
▶ commentPolicy
|
||||
✔ lets author/reviewer/approver annotate and keeps their comment role (0.741708ms)
|
||||
✔ keeps viewer read-only: no add entry and no reviewer/approver cast (0.134636ms)
|
||||
✔ commentPolicy (1.954861ms)
|
||||
▶ controlledContentInvalidation
|
||||
✔ hydrate success then late list_sync keeps cache/ready and does not bump resume (1.151666ms)
|
||||
✔ post_task invalidation drops active cache and requires a new hydrate epoch (0.177284ms)
|
||||
✔ editor open / save epoch keeps draft and rejects a stale GET after PATCH (0.327711ms)
|
||||
✔ never drops the active doc without signaling rehydrate (0.073408ms)
|
||||
✔ candidate helper keeps template / doc-001 / freeform / upload out of wipe lists (0.310821ms)
|
||||
✔ dropRecordKeys is a no-op when ids are absent (0.099687ms)
|
||||
✔ controlledContentInvalidation (2.981431ms)
|
||||
▶ controlledDocHydrate
|
||||
✔ shouldLoadControlledContent when non-template workflow doc (0.908313ms)
|
||||
✔ shouldLoadControlledContent rejects local legacy/mock ids like doc-001 (0.132726ms)
|
||||
✔ shouldLoadControlledContent keeps canonical, freeform-off, and template preview rules (0.118116ms)
|
||||
✔ hydrateControlledDocumentContent never requests content/revision for doc-001 (0.285842ms)
|
||||
✔ hydrateControlledDocumentContent never requests for template preview or registered freeform (0.189844ms)
|
||||
✔ shouldLoadControlledContent stays inside toolbar server-backed eligibility (0.243203ms)
|
||||
✔ resolveCanonicalDocId maps doc-p56-intake to doc_proj56_intake (0.298491ms)
|
||||
✔ canonicalDocIdForTask maps doc-p56-research to doc_proj56_research (0.173705ms)
|
||||
✔ hydrate treats projection missing as ledger content, not 404 (1.219373ms)
|
||||
✔ attaches hydrate ETag as PATCH baseline and ignores a later GET (0.762538ms)
|
||||
✔ rejects hydrate when revision-state network or 5xx fails (3.787338ms)
|
||||
✔ aligns markdownToBlockAST random ids onto revision-state head_block_map (1.540194ms)
|
||||
✔ leaves Math.random ids when head_block_map is omitted (why gate + fail-closed remain) (0.493745ms)
|
||||
✔ rejects controlled hydrate on NOT_FOUND or NEEDS_CONTENT (0.35913ms)
|
||||
✔ fingerprint stays stable when only object identity changes (0.134816ms)
|
||||
✔ dedupes in-flight hydrate for the same doc fingerprint (0.306921ms)
|
||||
✔ aligned late hydrate still cannot overwrite a PATCH-success baseline (0.372389ms)
|
||||
✔ controlledDocHydrate (12.7879ms)
|
||||
✔ createClientTurnId uses randomUUID when available (0.871035ms)
|
||||
✔ createClientTurnId falls back to getRandomValues UUID v4 (0.549034ms)
|
||||
✔ createClientTurnId throws when no secure random API exists (0.700279ms)
|
||||
✔ selectNonCreatorInitialMembers skips creator only (1.641371ms)
|
||||
✔ buildManagedMemberUpsertPayload sends document_duty on add/update/restore (0.308001ms)
|
||||
✔ buildInitialMemberUpsertPayload maps reviewer UI role to member API role (0.204814ms)
|
||||
✔ persistInitialProjectMembers awaits each add and stops on failure (2.01429ms)
|
||||
✔ ensureCreatedProjectWithMembers skips recreate on bootstrap id and retries members (0.539254ms)
|
||||
✔ beginCreateProjectSubmit rejects concurrent second entry until end (31.499155ms)
|
||||
✔ resolveCreateProjectSubmitPhase uses bootstrap to choose syncing label (0.436717ms)
|
||||
✔ createProjectSubmitButtonLabel and close block while submitting (0.551424ms)
|
||||
✔ shouldCloneMedicalTemplates skips the 7-pack for software domain (0.149046ms)
|
||||
▶ resolveCurrentDocRole
|
||||
✔ resolves A as author by authoritative user id (0.900363ms)
|
||||
✔ does not promote reviewer B or stranger to author (0.193924ms)
|
||||
✔ resolves real USER_B id as reviewer and never name-promotes placeholder 2 (0.158135ms)
|
||||
✔ fail-closes unknown identity to viewer even if projectPermission says author (0.134816ms)
|
||||
✔ matches name only for legacy local docs without duty ids (0.122656ms)
|
||||
✔ does not name-promote when server controlled duties are zero ids (0.195344ms)
|
||||
✔ resolveCurrentDocRole (4.133298ms)
|
||||
▶ currentProjectRestore
|
||||
✔ rejects empty project names (0.850794ms)
|
||||
✔ rejects page-wide project name when banner and sidebar both match (0.232943ms)
|
||||
✔ scopes the visible entry to complementary exact title (0.650211ms)
|
||||
✔ scopes the current project assertion to banner exact text (0.195434ms)
|
||||
✔ currentProjectRestore (2.890154ms)
|
||||
▶ deliverableLeaseSync
|
||||
✔ returns the same array reference when leases are unchanged (0.980741ms)
|
||||
✔ returns a new array only when a lease actually changes (0.251062ms)
|
||||
✔ deliverableLeaseSync (2.455357ms)
|
||||
▶ docGovActionVisibility
|
||||
✔ hides all approval actions on unmanaged docs (0.66075ms)
|
||||
✔ gates separated duties by user id, not folded role label (0.189615ms)
|
||||
✔ author_only: same user holds all three duties and sees all actions (0.130617ms)
|
||||
✔ author_only: non-duty user sees no actions (0.109637ms)
|
||||
✔ docGovActionVisibility (2.058909ms)
|
||||
▶ documentBlockDomAttrs
|
||||
✔ marks a real DocumentBlock in pure mode without overlay kind (0.535855ms)
|
||||
✔ keeps the same stable attrs when overlay is later attached separately (0.193125ms)
|
||||
✔ documentBlockDomAttrs (1.487596ms)
|
||||
▶ durableBodySnippet
|
||||
✔ rejects empty persisted snippets (1.205554ms)
|
||||
✔ lets a later successful revision replace the old snippet (0.229833ms)
|
||||
✔ does not promote a conflict-failed draft to the expected body (0.159406ms)
|
||||
✔ durableBodySnippet (3.512086ms)
|
||||
▶ dutyCellAssertion
|
||||
✔ parses author, reviewer, and viewer from select value or badge role (1.073568ms)
|
||||
✔ must not infer author from a row whose option labels include 编制负责人 (0.538474ms)
|
||||
✔ accepts only a disabled non-assignable reviewer select (0.178185ms)
|
||||
✔ rejects an enabled reviewer select when the reviewer cannot assign leads (0.168145ms)
|
||||
✔ rejects canAssignLead=true even if the reviewer select is disabled (0.131186ms)
|
||||
✔ rejects viewer, author, approver, and unknown values for USER_B (0.250203ms)
|
||||
✔ dutyCellAssertion (3.69673ms)
|
||||
▶ decideHydrationApply
|
||||
✔ applies only when gen, doc, save epoch match and editor is closed (1.490336ms)
|
||||
✔ rejects a response for another document (0.189915ms)
|
||||
✔ rejects a superseded generation (0.184564ms)
|
||||
✔ decideHydrationApply (3.098188ms)
|
||||
▶ createEditorHydrationGate
|
||||
✔ does not start a new hydrate while the editor is open (UI-06 fill-window GET) (0.467236ms)
|
||||
✔ deferred hydrate that resolves after the editor opens does not replace the session (1.636581ms)
|
||||
✔ covers hydrate resolving between fill and save (0.272002ms)
|
||||
✔ asks for a hydrate resume after editor_open blocked the first apply (0.202534ms)
|
||||
✔ asks for a hydrate resume when beginHydration is skipped while editing (0.145156ms)
|
||||
✔ requestHydrationResume is consumed once after a protected edit session (0.123936ms)
|
||||
✔ late hydrate after a successful PATCH does not roll back the new baseline (0.229793ms)
|
||||
✔ createEditorHydrationGate (3.71626ms)
|
||||
✔ mergeSessionUser prefers session identity over stale catalog entry (1.34718ms)
|
||||
▶ playwrightPhaseArtifacts
|
||||
✔ defaults unknown or empty UI_PHASE to main (0.863244ms)
|
||||
✔ keeps post-restart distinct from main so the second run cannot overwrite traces (0.241533ms)
|
||||
✔ keeps main results at the historical filename and isolates post-restart recordPass (0.247733ms)
|
||||
✔ shares phase-state and secrets across phases (0.089228ms)
|
||||
✔ playwrightPhaseArtifacts (2.624352ms)
|
||||
✔ resolvePostTaskActiveDocId prefers server doc_proj for taskKey (0.727278ms)
|
||||
✔ resolvePostTaskActiveDocId honors preferred server doc id (0.160215ms)
|
||||
✔ shouldSwitchTemplateSlotToServer when active is local template slot (0.362119ms)
|
||||
✔ pollUntilPostTaskServerDoc retries until same taskKey server doc appears (0.32802ms)
|
||||
✔ pollUntilPostTaskServerDoc returns null after maxAttempts without relying on setState (0.241713ms)
|
||||
✔ mapServerProjectToProjectInfo maps creator and preserves local members (0.808116ms)
|
||||
✔ mergeProjectLists drops local-only demo seed projects by default (0.351299ms)
|
||||
✔ mergeProjectLists can retain local-only demo when explicitly opted in (0.211873ms)
|
||||
✔ documentDutyFromMemberRole maps UI reviewer and creator (0.944061ms)
|
||||
✔ exclusiveMemberRoleChange keeps a single reviewer (0.211394ms)
|
||||
✔ memberRoleFromDocumentDuty does not invent reviewer from access role (0.232983ms)
|
||||
✔ toApiProjectRole maps doc roles to member and creator to owner (0.844425ms)
|
||||
✔ fromApiProjectRole maps owner to creator for project creator (0.183064ms)
|
||||
✔ mapApiMemberToProjectMember builds UI record (0.369479ms)
|
||||
▶ projectSettingsDuties
|
||||
✔ toggles full to none by atomically clearing duties (1.400778ms)
|
||||
✔ blocks partial saves and locates required/governance issue codes (0.370379ms)
|
||||
✔ allows an all-none configurable draft without three members (0.149776ms)
|
||||
✔ requires three members once any document is managed (0.176795ms)
|
||||
✔ resolves author_only to A/A/A without three-member requirement (0.552783ms)
|
||||
✔ rejects author_only without author or with conflicting reviewers (0.205514ms)
|
||||
✔ mixed full + author_only still requires three members for the full doc (0.239813ms)
|
||||
✔ toggleAuthorOnly clears review/approve slots and restores on disable (0.176235ms)
|
||||
✔ projectSettingsDuties (5.525156ms)
|
||||
▶ projectSettingsMode
|
||||
✔ enters frozen when can_edit_plan is false or the project is active (0.630921ms)
|
||||
✔ stays planning only for an editable draft (0.116687ms)
|
||||
✔ lists enabled duties for the frozen snapshot and skips disabled tasks (0.67543ms)
|
||||
✔ keeps the local draft after PLAN_FROZEN refresh and switches to frozen (0.119567ms)
|
||||
✔ projectSettingsMode (2.439987ms)
|
||||
▶ revisionEtag
|
||||
✔ uses stale hydrate ETag for PATCH and never the latest GET (0.612872ms)
|
||||
✔ does not invent 0:0 when controlled hydrate misses etag or errors (0.142166ms)
|
||||
✔ refuses controlled save without a hydrate ETag (0.365619ms)
|
||||
✔ allows freeform bootstrap 0:0 only (0.084067ms)
|
||||
✔ revisionEtag (2.076818ms)
|
||||
▶ saveControlledBlockEdit
|
||||
✔ successful save calls PATCH once with bound docId, hydrate ETag, body and client_block_map (2.308481ms)
|
||||
✔ does not PATCH when editorDocId and activeDocId diverge (0.220834ms)
|
||||
✔ does not PATCH on stale block id and keeps the draft (0.204274ms)
|
||||
✔ 409 does not close the editor or drop the draft (0.719039ms)
|
||||
✔ network errors do not close the editor or drop the draft (0.294271ms)
|
||||
✔ saveControlledBlockEdit (4.899045ms)
|
||||
✔ parseProjectIdFromSessionId decodes sess-p{id}-u{user} (0.870885ms)
|
||||
✔ resolveSessionProjectId prefers explicit id then session id encoding (0.180025ms)
|
||||
✔ mapServerSessionToProjectSession fills project metadata (0.566063ms)
|
||||
✔ mergeSessionLists keeps local stage while applying server counts (0.217224ms)
|
||||
▶ squadCompletionObservation
|
||||
✔ accepts a directly completed adjacent assistant without requiring inflight (0.739148ms)
|
||||
✔ accepts a turn that passed through inflight then completed (0.205684ms)
|
||||
✔ rejects error terminals, missing correspondence, and visible errors (0.124586ms)
|
||||
✔ keeps waiting while still inflight and does not treat inflight as success (0.113337ms)
|
||||
✔ squadCompletionObservation (2.299321ms)
|
||||
✔ resolveReconnectRunId prefers explicit opts and ignores attempt without opts (0.646301ms)
|
||||
✔ shouldSubscribeExistingRun only for in-flight statuses (0.151046ms)
|
||||
✔ isRemoteTaskTerminal covers confirmation and quality_failed (0.094168ms)
|
||||
✔ nextAttemptOpts carries retained runId for reconnect without new POST (0.091117ms)
|
||||
▶ uploadFreeformRegister
|
||||
✔ buildUploadClientKey prefers explicit hint then doc-upload stamp (0.597952ms)
|
||||
✔ registerUploadAttachments hard-fails without creating orphan on API error (12.772321ms)
|
||||
✔ registerUploadAttachments rejects local doc-upload id from server (0.299341ms)
|
||||
✔ registerUploadAttachments returns server doc-free ids and freeform facts (0.417027ms)
|
||||
✔ deliverableFromFreeformUpload maps server id and upload facts (0.140195ms)
|
||||
✔ uploadFreeformRegister (15.191758ms)
|
||||
▶ workflowCategoryNavigation
|
||||
✔ private menu cannot see doc_proj* so it must switch to workflow before exact select (1.071378ms)
|
||||
✔ fail-closes when the workflow menu is showing but the exact doc is missing (0.30882ms)
|
||||
✔ label and taskKey opens always require the workflow category (0.110247ms)
|
||||
✔ workflowCategoryNavigation (2.319251ms)
|
||||
▶ no external Google Fonts on login shell
|
||||
✔ index.html has no fonts.googleapis / fonts.gstatic (0.632041ms)
|
||||
✔ styles reference self-hosted fonts.css (0.262622ms)
|
||||
✔ no external Google Fonts on login shell (1.630742ms)
|
||||
▶ 用户上传文档多格式在线预览体系与渲染分流测试
|
||||
✔ 1. 上传附件时正确注入 sourceType: "uploaded_file" 与完整 fileMeta 元数据 (0.597192ms)
|
||||
✔ 2. 扩展名分类规则准确分发至对应的高保真渲染器类型 (0.164615ms)
|
||||
✔ 3. 受控工作流交付物严格隔离:绝不命中 FilePreviewPanel,保持 BlockRenderer 纯净 (0.135666ms)
|
||||
✔ 4. 上传文档支持受控流转:从个人私密一键共享至项目参考库后,在线预览依然可用且权限标态正确 (0.123146ms)
|
||||
✔ 用户上传文档多格式在线预览体系与渲染分流测试 (1.773707ms)
|
||||
▶ 项目立项、成员职责分配与退出留痕治理测试套件
|
||||
✔ 1. 默认项目库包含完整元数据与法定三权成员 (active 状态) (0.508425ms)
|
||||
✔ 2. 跨用户会话可见性严格隔离:未加入成员完全不可见 (0.185615ms)
|
||||
✔ 3. 创建者添加新成员并分配职责后,新成员即刻可见该项目会话 (0.118767ms)
|
||||
✔ 4. 中途加入后期退出 (exited):保留项目会话但标记已退出只读留痕 (0.113626ms)
|
||||
✔ 5. 超级管理员 (ADMIN) 享有全量通配可见与管理兜底特权 (0.090528ms)
|
||||
✔ 项目立项、成员职责分配与退出留痕治理测试套件 (1.956352ms)
|
||||
▶ 新建项目工程文档模板质量与纯净性保障套件(对标官方参考系统)
|
||||
✔ 1. 必须精确生成 7 份官方全流程受控工作流模板且标记纯净 (0.496545ms)
|
||||
✔ 2. 杜绝旧项目 CUSA-NeuroPulse 串号泄露,动态注入新项目信息 (0.484096ms)
|
||||
✔ 3. 严格对标参考系统模板规范要素,绝非 3 行残缺摘要 (0.232263ms)
|
||||
✔ 4. 缓存丢失场景下即时自愈重建 AST 能力验证 (0.191584ms)
|
||||
✔ 新建项目工程文档模板质量与纯净性保障套件(对标官方参考系统) (4.371831ms)
|
||||
▶ 用户上传文档私密归档与共享流转测试
|
||||
✔ 10 个评审项目均包含已共享的 URS 交付物与 AST,支持跨角色合规查阅且会话均以 assistant 闭环 (1.189995ms)
|
||||
✔ 用户上传附件自动生成私密交付物并在 AST 中建立隔离,支持流转至项目参考库 (1.207574ms)
|
||||
✔ 单轮对话支持一次上传多个文档(上限5个),批量生成私密交付物并遵循数量约束 (0.414727ms)
|
||||
✔ 用户上传文档私密归档与共享流转测试 (3.488406ms)
|
||||
▶ 工作流切换与消息展示时序测试套件
|
||||
✔ 1. 当存在历史 seed 消息且追加新实时消息时,新消息必须严格沉淀在末尾 (0.633981ms)
|
||||
✔ 2. 切回项目时相同正文不应因 temp/server id 差异重复展示 (0.134076ms)
|
||||
✔ 工作流切换与消息展示时序测试套件 (1.415858ms)
|
||||
✔ DeliverableItem 必须包含 projectId 与 isTemplateOnly 属性 (0.598822ms)
|
||||
ℹ tests 406
|
||||
ℹ suites 36
|
||||
ℹ pass 406
|
||||
ℹ fail 0
|
||||
ℹ cancelled 0
|
||||
ℹ skipped 0
|
||||
ℹ todo 0
|
||||
ℹ duration_ms 903.526321
|
||||
|
||||
RUN v5.0.0 /home/weichen/projects/Agent-System/web
|
||||
|
||||
(node:443082) ExperimentalWarning: localStorage is not available because --localstorage-file was not provided.
|
||||
(Use `node --trace-warnings ...` to show where the warning was created)
|
||||
|
||||
Test Files 3 passed (3)
|
||||
Tests 32 passed (32)
|
||||
Start at 04:01:01
|
||||
Duration 533ms (import 50%, transform 44%, tests 5%, worker 1%)
|
||||
|
||||
@@ -99,7 +99,6 @@ MULTI_INTENT_U9_SEGMENT=false
|
||||
MULTI_INTENT_U10_MUTATE=false
|
||||
MULTI_INTENT_U11_DOCGOV=false
|
||||
MULTI_INTENT_U12_RUN_CONTROL=false
|
||||
SEMANTIC_FALLBACK_ENABLED=false
|
||||
# W2: shadow only. Do not set SEMANTIC_SUMMARY_REPLACE=true; REPLACE is unwired.
|
||||
SEMANTIC_SUMMARY_SHADOW=false
|
||||
SEMANTIC_SUMMARY_REPLACE=false
|
||||
|
||||
@@ -114,7 +114,6 @@ services:
|
||||
MULTI_INTENT_U10_MUTATE: ${MULTI_INTENT_U10_MUTATE:-false}
|
||||
MULTI_INTENT_U11_DOCGOV: ${MULTI_INTENT_U11_DOCGOV:-false}
|
||||
MULTI_INTENT_U12_RUN_CONTROL: ${MULTI_INTENT_U12_RUN_CONTROL:-false}
|
||||
SEMANTIC_FALLBACK_ENABLED: ${SEMANTIC_FALLBACK_ENABLED:-false}
|
||||
# W2 PU6: shadow semantic summary; REPLACE stays unwired even if env is true
|
||||
SEMANTIC_SUMMARY_SHADOW: ${SEMANTIC_SUMMARY_SHADOW:-false}
|
||||
SEMANTIC_SUMMARY_REPLACE: ${SEMANTIC_SUMMARY_REPLACE:-false}
|
||||
|
||||
@@ -116,7 +116,6 @@ services:
|
||||
MULTI_INTENT_U10_MUTATE: ${MULTI_INTENT_U10_MUTATE:-false}
|
||||
MULTI_INTENT_U11_DOCGOV: ${MULTI_INTENT_U11_DOCGOV:-false}
|
||||
MULTI_INTENT_U12_RUN_CONTROL: ${MULTI_INTENT_U12_RUN_CONTROL:-false}
|
||||
SEMANTIC_FALLBACK_ENABLED: ${SEMANTIC_FALLBACK_ENABLED:-false}
|
||||
SEMANTIC_SUMMARY_SHADOW: ${SEMANTIC_SUMMARY_SHADOW:-false}
|
||||
PI_CHANGE_REPORT_NARRATIVE: ${PI_CHANGE_REPORT_NARRATIVE:-false}
|
||||
FAV_DEGRADED_READ: "false"
|
||||
|
||||
@@ -113,7 +113,6 @@ services:
|
||||
MULTI_INTENT_U10_MUTATE: ${MULTI_INTENT_U10_MUTATE:-false}
|
||||
MULTI_INTENT_U11_DOCGOV: ${MULTI_INTENT_U11_DOCGOV:-false}
|
||||
MULTI_INTENT_U12_RUN_CONTROL: ${MULTI_INTENT_U12_RUN_CONTROL:-false}
|
||||
SEMANTIC_FALLBACK_ENABLED: ${SEMANTIC_FALLBACK_ENABLED:-false}
|
||||
SEMANTIC_SUMMARY_SHADOW: ${SEMANTIC_SUMMARY_SHADOW:-false}
|
||||
PI_CHANGE_REPORT_NARRATIVE: ${PI_CHANGE_REPORT_NARRATIVE:-false}
|
||||
FAV_DEGRADED_READ: ${FAV_DEGRADED_READ:-false}
|
||||
|
||||
@@ -286,7 +286,6 @@ func sysadminFeatureValues(cfg *config.Config) func() map[string]string {
|
||||
"INTENT_V2_DECISION_TIER": string(iflags.V2DecisionTier),
|
||||
"WORKFLOW_INTENTS_ENABLED": strconv.FormatBool(iflags.WorkflowIntentsEnabled),
|
||||
"MULTI_INTENT_ENABLED": strconv.FormatBool(iflags.MultiIntentEnabled),
|
||||
"SEMANTIC_FALLBACK_ENABLED": strconv.FormatBool(iflags.SemanticFallbackEnabled),
|
||||
"MULTI_INTENT_PLAN_TIER": string(pflags.PlanTier),
|
||||
"MULTI_INTENT_U9_SEGMENT": strconv.FormatBool(pflags.U9Segment),
|
||||
"MULTI_INTENT_U10_MUTATE": strconv.FormatBool(pflags.U10Mutate),
|
||||
|
||||
@@ -99,3 +99,13 @@ func TestComposeNoDeadCursorRunnerConfig(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// X-1: SEMANTIC_FALLBACK_ENABLED has zero Go readers repo-wide and no design
|
||||
// doc describing intended semantics; Wave 5 decision = delete, not re-declare.
|
||||
func TestComposeNoDeadSemanticFallbackConfig(t *testing.T) {
|
||||
for _, name := range composeFilesForFlagCheck {
|
||||
if strings.Contains(readComposeFile(t, name), "SEMANTIC_FALLBACK_ENABLED") {
|
||||
t.Errorf("%s: dead SEMANTIC_FALLBACK_ENABLED must stay removed (Wave5 X-1)", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,7 +253,9 @@ func applySemanticShadow(ctx context.Context, sum *ChatSummary, msgs []ChatMessa
|
||||
if report.Protocol == "" {
|
||||
report.Protocol = semanticProtocolV1
|
||||
}
|
||||
// Explicitly do NOT swap sum.Narrative even if REPLACE env is true.
|
||||
// Explicitly do NOT swap sum.Narrative even if REPLACE env is true
|
||||
// (Wave 5 X-2 sealed; see wave5-deadcode-decisions.md#x-2).
|
||||
// Keep the call so envBool parsing stays covered; do not "fix" by wiring REPLACE.
|
||||
_ = SemanticSummaryReplaceEnabled()
|
||||
sum.Shadow = report
|
||||
}
|
||||
|
||||
@@ -7,12 +7,18 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
EnvSemanticSummaryShadow = "SEMANTIC_SUMMARY_SHADOW"
|
||||
EnvSemanticSummaryShadow = "SEMANTIC_SUMMARY_SHADOW"
|
||||
// SEALED (Wave 5 X-2, 2026-09-18): REPLACE must stay unwired indefinitely per
|
||||
// system-health-remediation-plan.md Wave5 + red-line "SEMANTIC_SUMMARY_REPLACE
|
||||
// 不接线". Enabling REPLACE to swap served narrative requires a separate
|
||||
// explicit user authorization + fail-closed rollout design; do not wire it as
|
||||
// a side effect of an unrelated change. See .doc/ops/short-term/2026-09-18-
|
||||
// wave5-deadcode-decisions.md#x-2.
|
||||
EnvSemanticSummaryReplace = "SEMANTIC_SUMMARY_REPLACE"
|
||||
)
|
||||
|
||||
// SummaryFeatureFlags controls semantic summary rollout.
|
||||
// REPLACE is reserved and must remain unwired in PU6 shadow delivery.
|
||||
// REPLACE is reserved and must remain unwired in PU6 shadow delivery (Wave 5 X-2 sealed).
|
||||
type SummaryFeatureFlags struct {
|
||||
Shadow bool
|
||||
Replace bool
|
||||
@@ -51,7 +57,8 @@ func SemanticSummaryShadowEnabled() bool {
|
||||
}
|
||||
|
||||
// SemanticSummaryReplaceEnabled reports the REPLACE env bit.
|
||||
// PU6 must not use this to swap the served deterministic narrative.
|
||||
// SEALED (Wave 5 X-2): PU6 must not use this to swap the served deterministic narrative.
|
||||
// See .doc/ops/short-term/2026-09-18-wave5-deadcode-decisions.md#x-2.
|
||||
func SemanticSummaryReplaceEnabled() bool {
|
||||
return CurrentSummaryFlags().Replace
|
||||
}
|
||||
|
||||
@@ -20,12 +20,11 @@ const (
|
||||
|
||||
// FeatureFlags Phase 5 意图识别灰度开关(环境变量覆盖,默认保守)
|
||||
type FeatureFlags struct {
|
||||
IntentV2Shadow bool
|
||||
IntentV2Decision bool
|
||||
V2DecisionTier V2DecisionTier
|
||||
WorkflowIntentsEnabled bool
|
||||
MultiIntentEnabled bool
|
||||
SemanticFallbackEnabled bool
|
||||
IntentV2Shadow bool
|
||||
IntentV2Decision bool
|
||||
V2DecisionTier V2DecisionTier
|
||||
WorkflowIntentsEnabled bool
|
||||
MultiIntentEnabled bool
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -40,7 +39,6 @@ func defaultFeatureFlags() FeatureFlags {
|
||||
V2DecisionTier: parseV2DecisionTier(os.Getenv("INTENT_V2_DECISION_TIER")),
|
||||
WorkflowIntentsEnabled: envBool("WORKFLOW_INTENTS_ENABLED", true),
|
||||
MultiIntentEnabled: envBool("MULTI_INTENT_ENABLED", true),
|
||||
SemanticFallbackEnabled: envBool("SEMANTIC_FALLBACK_ENABLED", false),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -89,6 +89,10 @@ type CreationPreviewResult struct {
|
||||
}
|
||||
|
||||
// ProjectTypesCatalog returns seeded software project types.
|
||||
// Wave 5 X-3: goose 00048 tables project_types / project_type_workflows exist with
|
||||
// seed rows, but HTTP catalog still serves this hard-coded list. DB-backed
|
||||
// consumption is deferred to SWBOOT-10 / W5b. See
|
||||
// .doc/ops/short-term/2026-09-18-wave5-deadcode-decisions.md#x-3.
|
||||
func ProjectTypesCatalog() []ProjectTypeCatalogItem {
|
||||
return []ProjectTypeCatalogItem{
|
||||
{
|
||||
@@ -115,6 +119,9 @@ func ProjectTypesCatalog() []ProjectTypeCatalogItem {
|
||||
}
|
||||
|
||||
// ComplianceProfilesCatalog returns seeded compliance profiles.
|
||||
// Wave 5 X-3: goose 00048 table compliance_profiles is seeded but unused at
|
||||
// runtime; this hard-coded catalog remains authoritative until SWBOOT-10 / W5b.
|
||||
// See .doc/ops/short-term/2026-09-18-wave5-deadcode-decisions.md#x-3.
|
||||
func ComplianceProfilesCatalog() []ComplianceProfileCatalogItem {
|
||||
return []ComplianceProfileCatalogItem{
|
||||
{Key: "none", Name: "无外部合规预设", Description: "不预设外部法规步骤;鉴权/审计/安全基线仍生效"},
|
||||
|
||||
@@ -15,6 +15,10 @@ var (
|
||||
)
|
||||
|
||||
// RepoAllowlist is an in-memory / admin-seeded path prefix allowlist stub.
|
||||
// Wave 5 X-3: goose 00048 table repo_path_allowlist (and project_source_bindings)
|
||||
// have zero runtime readers; this type is unit-test-only until SWBOOT-10 wires
|
||||
// DB-backed allowlist + real controlled patch execution. See
|
||||
// .doc/ops/short-term/2026-09-18-wave5-deadcode-decisions.md#x-3.
|
||||
type RepoAllowlist struct {
|
||||
prefixes []string
|
||||
}
|
||||
|
||||
@@ -37,7 +37,6 @@ var featureCatalog = []featureSpec{
|
||||
{key: "INTENT_V2_DECISION_TIER", label: "意图 V2 决策档位", group: "intent", fallback: "none", description: "V2 灰度风险档位:none/query/generate/write_low/write_high/all"},
|
||||
{key: "WORKFLOW_INTENTS_ENABLED", label: "工作流意图", group: "intent", fallback: "true", description: "识别工作流类意图"},
|
||||
{key: "MULTI_INTENT_ENABLED", label: "多意图识别", group: "intent", fallback: "true", description: "单条消息多意图拆解"},
|
||||
{key: "SEMANTIC_FALLBACK_ENABLED", label: "语义兜底", group: "intent", fallback: "false", description: "规则意图未命中时的语义兜底"},
|
||||
{key: "MULTI_INTENT_PLAN_TIER", label: "多意图计划档位", group: "intentflow", fallback: "legacy", description: "意图计划灰度:legacy/shadow/auto_read/confirm/full"},
|
||||
{key: "MULTI_INTENT_U9_SEGMENT", label: "U9 LLM 分段", group: "intentflow", fallback: "false", description: "LLM 意图分段提议"},
|
||||
{key: "MULTI_INTENT_U10_MUTATE", label: "U10 变更适配器", group: "intentflow", fallback: "false", description: "低/中风险 mutate 动作适配器"},
|
||||
|
||||
@@ -66,6 +66,17 @@ type SettingUpdate struct {
|
||||
}
|
||||
|
||||
// defaultSettingSpecs 一期白名单:先做机制,键空间可后续扩展。
|
||||
//
|
||||
// Wave 5 X-4: 除 SettingKeyAllowAuthorOnlyApproval 已接线外,下列 6 键暂无运行时
|
||||
// 消费方;决策 = 保留白名单待系统管理台二期接线(FEATURE-MATRIX「白名单项接消费方」),
|
||||
// 不从白名单删除。见 .doc/ops/short-term/2026-09-18-wave5-deadcode-decisions.md#x-4。
|
||||
// 预期消费方(二期):
|
||||
// - ui.maintenance_notice → 站点顶部维护公告横幅
|
||||
// - ui.read_only_mode → 写操作中间件 / 前端写入口阻断
|
||||
// - chat.default_session_role → 会话创建默认职责
|
||||
// - tasks.default_auto_repair → 独立任务运行默认 auto_repair
|
||||
// - kb.default_search_limit → 知识库检索默认 limit
|
||||
// - notifications.stage_advance_reminder → 阶段就绪提醒 ticker 总开关
|
||||
func defaultSettingSpecs() []SettingSpec {
|
||||
return []SettingSpec{
|
||||
{Key: "ui.maintenance_notice", Label: "维护公告", Group: "ui", Type: SettingTypeString, Default: "", Writable: true, RequiresRestart: false, Description: "全站维护公告横幅文案;空字符串表示关闭"},
|
||||
|
||||
@@ -99,10 +99,3 @@ export function decideAgentCandidate(
|
||||
{ method: 'POST', body: JSON.stringify(body) },
|
||||
);
|
||||
}
|
||||
|
||||
export function patchNotification(id: number, status: 'read' | 'acted' | 'dismissed') {
|
||||
return api<{ ok: boolean }>(`/api/v1/notifications/${id}`, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify({ status }),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,8 +4,6 @@ import {
|
||||
WorkflowDefinition,
|
||||
DocumentLineage,
|
||||
CreateProjectInput,
|
||||
ActivateWorkflowInput,
|
||||
ActivateWorkflowResult,
|
||||
} from '../types/projectWorkflow';
|
||||
import { getBearerAuthHeaders } from './authHeaders';
|
||||
|
||||
@@ -68,29 +66,6 @@ export async function fetchProjectWorkflows(projectId: number): Promise<ProjectW
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export async function activateWorkflow(
|
||||
projectId: number,
|
||||
input: ActivateWorkflowInput
|
||||
): Promise<ActivateWorkflowResult> {
|
||||
const res = await fetch(`${API_BASE}/api/v1/projects/${projectId}/active-workflow`, {
|
||||
method: 'PUT',
|
||||
headers: getBearerAuthHeaders('application/json'),
|
||||
body: JSON.stringify(input),
|
||||
});
|
||||
if (!res.ok) {
|
||||
const text = await res.text();
|
||||
let msg = text;
|
||||
try {
|
||||
const parsed = JSON.parse(text);
|
||||
if (parsed.message) msg = parsed.message;
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
throw new Error(msg || `切换激活工作流失败: ${res.status}`);
|
||||
}
|
||||
return res.json();
|
||||
}
|
||||
|
||||
export async function fetchWorkflowCatalog(): Promise<WorkflowDefinition[]> {
|
||||
const res = await fetch(`${API_BASE}/api/v1/workflows`, {
|
||||
headers: getBearerAuthHeaders(),
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
import { patchNotification } from '../../api/documentRevision';
|
||||
|
||||
export type DocNotificationItem = {
|
||||
id: number;
|
||||
type: string;
|
||||
title: string;
|
||||
body: string;
|
||||
status: string;
|
||||
created_at?: string;
|
||||
};
|
||||
|
||||
export function DocumentNotificationList(props: {
|
||||
items: DocNotificationItem[];
|
||||
onChanged?: () => void;
|
||||
}) {
|
||||
return (
|
||||
<ul aria-label="文档变更通知" style={{ listStyle: 'none', padding: 0, margin: 0 }}>
|
||||
{props.items.map((n) => (
|
||||
<li key={n.id} style={{ borderBottom: '1px solid #eee', padding: '8px 0' }}>
|
||||
<div><strong>{n.title}</strong></div>
|
||||
<div style={{ fontSize: 13 }}>{n.body}</div>
|
||||
<div style={{ display: 'flex', gap: 6, marginTop: 4 }}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={async () => {
|
||||
await patchNotification(n.id, 'read');
|
||||
props.onChanged?.();
|
||||
}}
|
||||
>
|
||||
已读
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={async () => {
|
||||
await patchNotification(n.id, 'acted');
|
||||
props.onChanged?.();
|
||||
}}
|
||||
>
|
||||
已处理
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={async () => {
|
||||
await patchNotification(n.id, 'dismissed');
|
||||
props.onChanged?.();
|
||||
}}
|
||||
>
|
||||
忽略
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
@@ -1,826 +0,0 @@
|
||||
import { FC, useState, useEffect } from 'react';
|
||||
import {
|
||||
X,
|
||||
Settings,
|
||||
Shield,
|
||||
Plus,
|
||||
Trash2,
|
||||
Check,
|
||||
Users,
|
||||
Key,
|
||||
Info,
|
||||
CheckCircle2,
|
||||
} from 'lucide-react';
|
||||
import { RoleDefinition, PermissionKey } from '../../types/rbac';
|
||||
import { UserProfile } from '../../types/docgov';
|
||||
|
||||
export interface SettingsModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
roles: RoleDefinition[];
|
||||
onSaveRoles: (roles: RoleDefinition[]) => void;
|
||||
users: UserProfile[];
|
||||
currentUser: UserProfile;
|
||||
onUpdateUserRole: (userId: number, roleId: string) => void;
|
||||
}
|
||||
|
||||
const ALL_PERMISSION_ITEMS: {
|
||||
key: PermissionKey;
|
||||
label: string;
|
||||
desc: string;
|
||||
category: '项目与流程' | '权限与安全' | '文档与导出';
|
||||
}[] = [
|
||||
{
|
||||
key: 'project:create',
|
||||
label: '创建新项目/立项',
|
||||
desc: '允许在左侧栏发起新研发项目与阶段模板克隆',
|
||||
category: '项目与流程',
|
||||
},
|
||||
{
|
||||
key: 'project:assign_lead',
|
||||
label: '设置/变更文档责任人',
|
||||
desc: '允许指派或变更文档的法定编制人、审核人与批准人',
|
||||
category: '项目与流程',
|
||||
},
|
||||
{
|
||||
key: 'perm:manage',
|
||||
label: '系统权限与角色管理',
|
||||
desc: '超级管理特权,可增删改角色库并配置全局权限',
|
||||
category: '权限与安全',
|
||||
},
|
||||
{
|
||||
key: 'workflow:config_visibility',
|
||||
label: '配置工作流默认可见性',
|
||||
desc: '允许配置整个研发阶段或工作流的基线可见性规则',
|
||||
category: '权限与安全',
|
||||
},
|
||||
{
|
||||
key: 'doc:override_visibility',
|
||||
label: '配置单文档可见性覆盖',
|
||||
desc: '允许对敏感或涉密文档设置独立可见性(覆盖工作流默认)',
|
||||
category: '权限与安全',
|
||||
},
|
||||
{
|
||||
key: 'workflow:view_all',
|
||||
label: '查阅常规受控工作流文档',
|
||||
desc: '默认是否可浏览项目内的受控工作流核心交付物',
|
||||
category: '文档与导出',
|
||||
},
|
||||
{
|
||||
key: 'doc:export',
|
||||
label: '导出受控成果物 (DOCX/XLSX)',
|
||||
desc: '允许将交付物编译导出为合规 DOCX 报告或测算 XLSX 表格',
|
||||
category: '文档与导出',
|
||||
},
|
||||
];
|
||||
|
||||
export const SettingsModal: FC<SettingsModalProps> = ({
|
||||
isOpen,
|
||||
onClose,
|
||||
roles,
|
||||
onSaveRoles,
|
||||
users,
|
||||
currentUser,
|
||||
onUpdateUserRole,
|
||||
}) => {
|
||||
const [activeTab, setActiveTab] = useState<'roles' | 'users'>('roles');
|
||||
const [selectedRoleId, setSelectedRoleId] = useState<string>(roles[0]?.id || 'role-admin');
|
||||
const [editRoles, setEditRoles] = useState<RoleDefinition[]>(roles);
|
||||
const [isCreatingRole, setIsCreatingRole] = useState(false);
|
||||
const [newRoleName, setNewRoleName] = useState('');
|
||||
const [newRoleCode, setNewRoleCode] = useState('');
|
||||
const [newRoleDesc, setNewRoleDesc] = useState('');
|
||||
const [saveSuccessTip, setSaveSuccessTip] = useState(false);
|
||||
|
||||
// 同步外部 roles
|
||||
useEffect(() => {
|
||||
setEditRoles(roles);
|
||||
if (!roles.some((r) => r.id === selectedRoleId) && roles.length > 0) {
|
||||
setSelectedRoleId(roles[0].id);
|
||||
}
|
||||
}, [roles]);
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (e: KeyboardEvent) => {
|
||||
if (e.key === 'Escape' && isOpen) {
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||
}, [isOpen, onClose]);
|
||||
|
||||
if (!isOpen) return null;
|
||||
|
||||
const currentSelectedRole =
|
||||
editRoles.find((r) => r.id === selectedRoleId) || editRoles[0];
|
||||
|
||||
const handleTogglePermission = (permKey: PermissionKey) => {
|
||||
if (!currentSelectedRole || currentSelectedRole.isSystem) return;
|
||||
|
||||
const exists = currentSelectedRole.permissions.includes(permKey);
|
||||
const updatedPermissions = exists
|
||||
? currentSelectedRole.permissions.filter((k) => k !== permKey)
|
||||
: [...currentSelectedRole.permissions, permKey];
|
||||
|
||||
const updatedRoles = editRoles.map((r) =>
|
||||
r.id === currentSelectedRole.id
|
||||
? { ...r, permissions: updatedPermissions, updatedAt: new Date().toLocaleString() }
|
||||
: r
|
||||
);
|
||||
|
||||
setEditRoles(updatedRoles);
|
||||
onSaveRoles(updatedRoles);
|
||||
showSavedNotification();
|
||||
};
|
||||
|
||||
const handleCreateRole = () => {
|
||||
if (!newRoleName.trim() || !newRoleCode.trim()) return;
|
||||
|
||||
const newId = `role-custom-${Date.now()}`;
|
||||
const newRole: RoleDefinition = {
|
||||
id: newId,
|
||||
name: newRoleName.trim(),
|
||||
code: newRoleCode.trim().toUpperCase(),
|
||||
description: newRoleDesc.trim() || '自定义业务角色',
|
||||
isSystem: false,
|
||||
permissions: ['workflow:view_all', 'doc:export'],
|
||||
createdAt: new Date().toLocaleString(),
|
||||
updatedAt: new Date().toLocaleString(),
|
||||
};
|
||||
|
||||
const nextRoles = [...editRoles, newRole];
|
||||
setEditRoles(nextRoles);
|
||||
onSaveRoles(nextRoles);
|
||||
setSelectedRoleId(newId);
|
||||
setIsCreatingRole(false);
|
||||
setNewRoleName('');
|
||||
setNewRoleCode('');
|
||||
setNewRoleDesc('');
|
||||
showSavedNotification();
|
||||
};
|
||||
|
||||
const handleDeleteRole = (roleId: string) => {
|
||||
const roleToDelete = editRoles.find((r) => r.id === roleId);
|
||||
if (!roleToDelete || roleToDelete.isSystem) return;
|
||||
|
||||
if (window.confirm(`确定要删除角色【${roleToDelete.name}】吗?删除后该角色用户将自动退回普通工程师。`)) {
|
||||
const nextRoles = editRoles.filter((r) => r.id !== roleId);
|
||||
setEditRoles(nextRoles);
|
||||
onSaveRoles(nextRoles);
|
||||
setSelectedRoleId(nextRoles[0]?.id || 'role-admin');
|
||||
showSavedNotification();
|
||||
}
|
||||
};
|
||||
|
||||
const showSavedNotification = () => {
|
||||
setSaveSuccessTip(true);
|
||||
setTimeout(() => setSaveSuccessTip(false), 2000);
|
||||
};
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed',
|
||||
inset: 0,
|
||||
backgroundColor: 'rgba(11, 14, 20, 0.75)',
|
||||
backdropFilter: 'blur(6px)',
|
||||
zIndex: 1000,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
padding: '20px',
|
||||
}}
|
||||
onClick={(e) => {
|
||||
if (e.target === e.currentTarget) onClose();
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
width: '840px',
|
||||
maxWidth: '95vw',
|
||||
height: '620px',
|
||||
maxHeight: '90vh',
|
||||
backgroundColor: 'var(--bg-panel, #131820)',
|
||||
borderRadius: '12px',
|
||||
border: '1px solid var(--border)',
|
||||
boxShadow: 'var(--shadow-lg)',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
overflow: 'hidden',
|
||||
animation: 'scaleIn 0.18s ease-out',
|
||||
}}
|
||||
>
|
||||
{/* 顶部标题栏 */}
|
||||
<div
|
||||
style={{
|
||||
padding: '14px 20px',
|
||||
borderBottom: '1px solid var(--border)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
backgroundColor: 'var(--bg-subtle, #1A212C)',
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
|
||||
<div
|
||||
style={{
|
||||
width: '34px',
|
||||
height: '34px',
|
||||
borderRadius: '8px',
|
||||
backgroundColor: 'rgba(79, 107, 255, 0.15)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
color: 'var(--brand-500)',
|
||||
}}
|
||||
>
|
||||
<Settings size={18} />
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ fontSize: '15px', fontWeight: 600, color: 'var(--text-strong)' }}>
|
||||
系统权限与角色管理控制台
|
||||
</div>
|
||||
<div style={{ fontSize: '11px', color: 'var(--text-muted)', marginTop: '2px' }}>
|
||||
全局动态角色 CRUD · 权限配置矩阵 · 人员角色委派与实测切换
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
|
||||
{saveSuccessTip && (
|
||||
<span
|
||||
style={{
|
||||
fontSize: '12px',
|
||||
color: 'var(--success)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '4px',
|
||||
animation: 'fadeIn 0.2s',
|
||||
}}
|
||||
>
|
||||
<CheckCircle2 size={14} /> 已实时自动保存
|
||||
</span>
|
||||
)}
|
||||
<button
|
||||
onClick={onClose}
|
||||
style={{
|
||||
background: 'none',
|
||||
border: 'none',
|
||||
cursor: 'pointer',
|
||||
color: 'var(--text-muted)',
|
||||
padding: '6px',
|
||||
borderRadius: '6px',
|
||||
}}
|
||||
>
|
||||
<X size={18} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 标签栏导航 */}
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
borderBottom: '1px solid var(--border)',
|
||||
backgroundColor: 'var(--bg-card, #161D27)',
|
||||
padding: '0 16px',
|
||||
gap: '8px',
|
||||
}}
|
||||
>
|
||||
<button
|
||||
onClick={() => setActiveTab('roles')}
|
||||
style={{
|
||||
padding: '10px 16px',
|
||||
fontSize: '13px',
|
||||
fontWeight: 600,
|
||||
background: 'none',
|
||||
border: 'none',
|
||||
borderBottom: activeTab === 'roles' ? '2px solid var(--brand-500)' : '2px solid transparent',
|
||||
color: activeTab === 'roles' ? 'var(--brand-500)' : 'var(--text-muted)',
|
||||
cursor: 'pointer',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '6px',
|
||||
}}
|
||||
>
|
||||
<Shield size={15} />
|
||||
<span>全局角色与默认权限矩阵</span>
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => setActiveTab('users')}
|
||||
style={{
|
||||
padding: '10px 16px',
|
||||
fontSize: '13px',
|
||||
fontWeight: 600,
|
||||
background: 'none',
|
||||
border: 'none',
|
||||
borderBottom: activeTab === 'users' ? '2px solid var(--brand-500)' : '2px solid transparent',
|
||||
color: activeTab === 'users' ? 'var(--brand-500)' : 'var(--text-muted)',
|
||||
cursor: 'pointer',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '6px',
|
||||
}}
|
||||
>
|
||||
<Users size={15} />
|
||||
<span>人员角色委派与当前登录人切换</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 内容区 */}
|
||||
<div style={{ flex: 1, overflow: 'hidden', display: 'flex' }}>
|
||||
{activeTab === 'roles' ? (
|
||||
<div style={{ display: 'flex', width: '100%', height: '100%' }}>
|
||||
{/* 左侧角色列表 */}
|
||||
<div
|
||||
style={{
|
||||
width: '260px',
|
||||
borderRight: '1px solid var(--border)',
|
||||
backgroundColor: 'var(--bg-subtle, #1A212C)',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
padding: '12px',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
borderBottom: '1px solid var(--border-light)',
|
||||
}}
|
||||
>
|
||||
<span style={{ fontSize: '11.5px', fontWeight: 600, color: 'var(--text-muted)' }}>
|
||||
系统全局角色 ({editRoles.length})
|
||||
</span>
|
||||
<button
|
||||
onClick={() => setIsCreatingRole(true)}
|
||||
style={{
|
||||
padding: '4px 8px',
|
||||
borderRadius: 'var(--r-sm)',
|
||||
background: 'var(--brand-500)',
|
||||
border: 'none',
|
||||
color: '#fff',
|
||||
fontSize: '11px',
|
||||
fontWeight: 600,
|
||||
cursor: 'pointer',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '3px',
|
||||
}}
|
||||
>
|
||||
<Plus size={12} />
|
||||
<span>新增角色</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 新增角色输入面板 */}
|
||||
{isCreatingRole && (
|
||||
<div
|
||||
style={{
|
||||
padding: '10px',
|
||||
backgroundColor: 'var(--bg-card)',
|
||||
borderBottom: '1px solid var(--border)',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '6px',
|
||||
}}
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="角色名称 (如: 质量总监)"
|
||||
value={newRoleName}
|
||||
onChange={(e) => setNewRoleName(e.target.value)}
|
||||
style={{
|
||||
padding: '6px 8px',
|
||||
fontSize: '12px',
|
||||
borderRadius: '4px',
|
||||
border: '1px solid var(--border)',
|
||||
background: 'var(--bg-panel)',
|
||||
color: 'var(--text-strong)',
|
||||
}}
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="角色代号 (如: QA_DIR)"
|
||||
value={newRoleCode}
|
||||
onChange={(e) => setNewRoleCode(e.target.value)}
|
||||
style={{
|
||||
padding: '6px 8px',
|
||||
fontSize: '12px',
|
||||
borderRadius: '4px',
|
||||
border: '1px solid var(--border)',
|
||||
background: 'var(--bg-panel)',
|
||||
color: 'var(--text-strong)',
|
||||
}}
|
||||
/>
|
||||
<div style={{ display: 'flex', gap: '6px', marginTop: '4px' }}>
|
||||
<button
|
||||
onClick={handleCreateRole}
|
||||
style={{
|
||||
flex: 1,
|
||||
padding: '4px',
|
||||
fontSize: '11.5px',
|
||||
fontWeight: 600,
|
||||
background: 'var(--brand-500)',
|
||||
color: '#fff',
|
||||
border: 'none',
|
||||
borderRadius: '4px',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
确认创建
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setIsCreatingRole(false)}
|
||||
style={{
|
||||
flex: 1,
|
||||
padding: '4px',
|
||||
fontSize: '11.5px',
|
||||
background: 'var(--bg-subtle)',
|
||||
color: 'var(--text-muted)',
|
||||
border: '1px solid var(--border)',
|
||||
borderRadius: '4px',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 角色卡片列表 */}
|
||||
<div style={{ flex: 1, overflowY: 'auto', padding: '8px' }}>
|
||||
{editRoles.map((role) => {
|
||||
const isSelected = role.id === selectedRoleId;
|
||||
return (
|
||||
<div
|
||||
key={role.id}
|
||||
onClick={() => setSelectedRoleId(role.id)}
|
||||
style={{
|
||||
padding: '10px 12px',
|
||||
borderRadius: '6px',
|
||||
marginBottom: '6px',
|
||||
cursor: 'pointer',
|
||||
backgroundColor: isSelected ? 'var(--bg-active, #1F2837)' : 'transparent',
|
||||
border: isSelected ? '1px solid var(--brand-500)' : '1px solid transparent',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
transition: 'all 0.15s ease',
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
|
||||
<span
|
||||
style={{
|
||||
fontSize: '13px',
|
||||
fontWeight: 600,
|
||||
color: isSelected ? 'var(--brand-400, #4F6BFF)' : 'var(--text-strong)',
|
||||
}}
|
||||
>
|
||||
{role.name}
|
||||
</span>
|
||||
{role.isSystem && (
|
||||
<span
|
||||
style={{
|
||||
fontSize: '9.5px',
|
||||
padding: '1px 4px',
|
||||
borderRadius: '3px',
|
||||
backgroundColor: 'rgba(239, 68, 68, 0.15)',
|
||||
color: 'var(--danger)',
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
SYSTEM
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div style={{ fontSize: '11px', color: 'var(--text-muted)', marginTop: '2px' }}>
|
||||
代码: {role.code} · {role.permissions.length} 项权限
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!role.isSystem && (
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleDeleteRole(role.id);
|
||||
}}
|
||||
title="删除该自定义角色"
|
||||
style={{
|
||||
background: 'none',
|
||||
border: 'none',
|
||||
color: 'var(--text-faint)',
|
||||
cursor: 'pointer',
|
||||
padding: '4px',
|
||||
}}
|
||||
onMouseEnter={(e) => (e.currentTarget.style.color = 'var(--danger)')}
|
||||
onMouseLeave={(e) => (e.currentTarget.style.color = 'var(--text-faint)')}
|
||||
>
|
||||
<Trash2 size={13} />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 右侧权限配置矩阵 */}
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
overflowY: 'auto',
|
||||
padding: '20px',
|
||||
backgroundColor: 'var(--bg-panel)',
|
||||
}}
|
||||
>
|
||||
{currentSelectedRole && (
|
||||
<>
|
||||
<div style={{ borderBottom: '1px solid var(--border)', paddingBottom: '12px' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
|
||||
<div style={{ fontSize: '16px', fontWeight: 700, color: 'var(--text-strong)' }}>
|
||||
{currentSelectedRole.name} ({currentSelectedRole.code})
|
||||
</div>
|
||||
{currentSelectedRole.isSystem && (
|
||||
<span
|
||||
style={{
|
||||
fontSize: '11px',
|
||||
color: 'var(--danger)',
|
||||
backgroundColor: 'rgba(239, 68, 68, 0.1)',
|
||||
padding: '2px 8px',
|
||||
borderRadius: '4px',
|
||||
}}
|
||||
>
|
||||
🛡️ 系统内置超级管理员,默认具备所有权限且防误删
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div style={{ fontSize: '12px', color: 'var(--text-muted)', marginTop: '4px' }}>
|
||||
{currentSelectedRole.description}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: '16px', display: 'flex', flexDirection: 'column', gap: '14px' }}>
|
||||
<div
|
||||
style={{
|
||||
fontSize: '12.5px',
|
||||
fontWeight: 600,
|
||||
color: 'var(--text-muted)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '6px',
|
||||
}}
|
||||
>
|
||||
<Key size={14} />
|
||||
<span>默认赋予的原子功能权限项 (勾选即刻生效):</span>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr', gap: '8px' }}>
|
||||
{ALL_PERMISSION_ITEMS.map((item) => {
|
||||
const isChecked =
|
||||
currentSelectedRole.isSystem ||
|
||||
currentSelectedRole.permissions.includes(item.key);
|
||||
const isDisabled = currentSelectedRole.isSystem;
|
||||
|
||||
return (
|
||||
<div
|
||||
key={item.key}
|
||||
onClick={() => {
|
||||
if (!isDisabled) handleTogglePermission(item.key);
|
||||
}}
|
||||
style={{
|
||||
padding: '10px 14px',
|
||||
borderRadius: '8px',
|
||||
border: '1px solid var(--border)',
|
||||
backgroundColor: isChecked ? 'rgba(79, 107, 255, 0.05)' : 'var(--bg-subtle)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
cursor: isDisabled ? 'default' : 'pointer',
|
||||
transition: 'all 0.15s ease',
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '2px' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
||||
<span style={{ fontSize: '13px', fontWeight: 600, color: 'var(--text-strong)' }}>
|
||||
{item.label}
|
||||
</span>
|
||||
<span
|
||||
style={{
|
||||
fontSize: '10px',
|
||||
padding: '1px 6px',
|
||||
borderRadius: '4px',
|
||||
backgroundColor: 'var(--border-light)',
|
||||
color: 'var(--text-muted)',
|
||||
}}
|
||||
>
|
||||
{item.category}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ fontSize: '11px', color: 'var(--text-muted)' }}>
|
||||
<code>{item.key}</code> · {item.desc}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
width: '20px',
|
||||
height: '20px',
|
||||
borderRadius: '4px',
|
||||
border: isChecked ? '1px solid var(--brand-500)' : '1px solid var(--border)',
|
||||
backgroundColor: isChecked ? 'var(--brand-500)' : 'transparent',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
color: '#fff',
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
{isChecked && <Check size={14} />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
/* Tab 2: 人员角色委派与当前登录人切换 */
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
padding: '20px',
|
||||
overflowY: 'auto',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '14px',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontSize: '12px',
|
||||
color: 'var(--text)',
|
||||
lineHeight: 1.6,
|
||||
backgroundColor: 'var(--bg-subtle)',
|
||||
padding: '12px 16px',
|
||||
borderRadius: '6px',
|
||||
border: '1px solid var(--border)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '8px',
|
||||
}}
|
||||
>
|
||||
<Info size={16} color="var(--brand-500)" style={{ flexShrink: 0 }} />
|
||||
<span>
|
||||
在此为研发中心人员委派全局角色。支持一键将当前会话登录人切换为不同角色身份,快速实测“新建项目”、“指派负责人”及“单文档可见性覆盖”的受控拦截效果。
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>
|
||||
{users.map((u) => {
|
||||
const isCurrent = u.id === currentUser.id;
|
||||
const userRoleId = u.roleId || 'role-engineer';
|
||||
|
||||
return (
|
||||
<div
|
||||
key={u.id}
|
||||
style={{
|
||||
padding: '14px 16px',
|
||||
borderRadius: '8px',
|
||||
border: isCurrent ? '1.5px solid var(--brand-500)' : '1px solid var(--border)',
|
||||
backgroundColor: isCurrent ? 'rgba(79, 107, 255, 0.08)' : 'var(--bg-subtle)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
|
||||
<div
|
||||
style={{
|
||||
width: '38px',
|
||||
height: '38px',
|
||||
borderRadius: '50%',
|
||||
backgroundColor: isCurrent ? 'var(--brand-500)' : 'var(--border)',
|
||||
color: '#fff',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
fontWeight: 700,
|
||||
fontSize: '14px',
|
||||
}}
|
||||
>
|
||||
{u.name.slice(0, 1)}
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
|
||||
<span style={{ fontSize: '13.5px', fontWeight: 600, color: 'var(--text-strong)' }}>
|
||||
{u.name}
|
||||
</span>
|
||||
<span style={{ fontSize: '11px', color: 'var(--text-muted)' }}>
|
||||
工号: {u.employeeId}
|
||||
</span>
|
||||
{isCurrent && (
|
||||
<span
|
||||
style={{
|
||||
fontSize: '10px',
|
||||
fontWeight: 600,
|
||||
color: 'var(--brand-500)',
|
||||
backgroundColor: 'rgba(79, 107, 255, 0.15)',
|
||||
padding: '1px 6px',
|
||||
borderRadius: 'var(--r-pill)',
|
||||
}}
|
||||
>
|
||||
当前登录身份
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div style={{ fontSize: '11px', color: 'var(--text-muted)', marginTop: '2px' }}>
|
||||
{u.department} · {u.position}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
|
||||
{/* 角色委派下拉框 */}
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
|
||||
<span style={{ fontSize: '11.5px', color: 'var(--text-muted)' }}>委派全局角色:</span>
|
||||
<select
|
||||
value={userRoleId}
|
||||
onChange={(e) => onUpdateUserRole(u.id, e.target.value)}
|
||||
style={{
|
||||
padding: '6px 10px',
|
||||
fontSize: '12px',
|
||||
borderRadius: '6px',
|
||||
border: '1px solid var(--border)',
|
||||
backgroundColor: 'var(--bg-panel)',
|
||||
color: 'var(--text-strong)',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
{editRoles.map((r) => (
|
||||
<option key={r.id} value={r.id}>
|
||||
{r.name} ({r.code})
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
{isCurrent ? (
|
||||
<span style={{ fontSize: '12px', color: 'var(--brand-500)', fontWeight: 600, width: '120px', textAlign: 'center' }}>
|
||||
✓ 当前登录身份
|
||||
</span>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 底部按钮栏 */}
|
||||
<div
|
||||
style={{
|
||||
padding: '12px 20px',
|
||||
borderTop: '1px solid var(--border)',
|
||||
backgroundColor: 'var(--bg-subtle, #1A212C)',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<span style={{ fontSize: '11px', color: 'var(--text-muted)' }}>
|
||||
水木东方医疗器械智能协同工作台 · 全生命周期权限治理
|
||||
</span>
|
||||
<button
|
||||
onClick={onClose}
|
||||
style={{
|
||||
padding: '6px 18px',
|
||||
borderRadius: '6px',
|
||||
border: 'none',
|
||||
background: 'var(--brand-grad, linear-gradient(135deg, #4F6BFF 0%, #3B50D9 100%))',
|
||||
color: '#fff',
|
||||
fontSize: '12.5px',
|
||||
fontWeight: 600,
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
完成并关闭
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1,63 +0,0 @@
|
||||
import React from 'react';
|
||||
import { AlertTriangle, ArrowRight } from 'lucide-react';
|
||||
|
||||
interface WorkflowPrerequisiteBannerProps {
|
||||
workflowKey: string;
|
||||
hasApprovedSystemReqs: boolean;
|
||||
onSwitchToProductManager: () => void;
|
||||
}
|
||||
|
||||
export const WorkflowPrerequisiteBanner: React.FC<WorkflowPrerequisiteBannerProps> = ({
|
||||
workflowKey,
|
||||
hasApprovedSystemReqs,
|
||||
onSwitchToProductManager,
|
||||
}) => {
|
||||
if (workflowKey !== 'test_manager' || hasApprovedSystemReqs) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
margin: '8px 16px',
|
||||
padding: '8px 12px',
|
||||
borderRadius: '8px',
|
||||
background: 'rgba(245, 158, 11, 0.1)',
|
||||
border: '1px solid rgba(245, 158, 11, 0.3)',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
gap: '12px',
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', fontSize: '12px', color: '#b45309' }}>
|
||||
<AlertTriangle size={16} style={{ color: '#d97706', flexShrink: 0 }} />
|
||||
<span>
|
||||
<strong>前置依赖未就绪:</strong>
|
||||
智能测试经理工作流强依赖同项目已批准的《系统需求规格书 (RD-SR)》。请先由产品经理完成编制并经三级审批批准。
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={onSwitchToProductManager}
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '4px',
|
||||
padding: '4px 10px',
|
||||
borderRadius: '6px',
|
||||
background: '#d97706',
|
||||
color: '#ffffff',
|
||||
border: 'none',
|
||||
fontSize: '11px',
|
||||
fontWeight: 600,
|
||||
cursor: 'pointer',
|
||||
whiteSpace: 'nowrap',
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
<span>前往产品经理编制需求</span>
|
||||
<ArrowRight size={12} />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -137,11 +137,6 @@ export function loadMessages(): ChatMessage[] {
|
||||
return enrichMessagesWithCategories(result);
|
||||
}
|
||||
|
||||
/** @deprecated 全量 MESSAGES 持久化已废弃,请使用 saveSessionWindowCache */
|
||||
export function saveMessages(_messages: ChatMessage[]): void {
|
||||
// no-op: 权威历史来自服务端窗口接口
|
||||
}
|
||||
|
||||
// ==================================================================================
|
||||
// P0: 会话窗口 LRU 缓存与输入草稿持久化 (替代原全量 MESSAGES 持久化)
|
||||
// ==================================================================================
|
||||
|
||||
@@ -75,17 +75,3 @@ export interface CreateProjectInput {
|
||||
/** @deprecated Accepted by API for catalog checks only; create dialog must not send this. */
|
||||
initial_workflow_key?: string;
|
||||
}
|
||||
|
||||
export interface ActivateWorkflowInput {
|
||||
workflow_key: string;
|
||||
expected_version: number;
|
||||
reason?: string;
|
||||
}
|
||||
|
||||
export interface ActivateWorkflowResult {
|
||||
project_id: number;
|
||||
active_workflow_key: string;
|
||||
version: number;
|
||||
changed_at: string;
|
||||
changed_by: number;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user