From 62cd8c45dd65a3891aa4be7be54db8e44425f27c Mon Sep 17 00:00:00 2001
From: jackyu66git
Date: Thu, 13 Aug 2026 01:46:34 +0800
Subject: [PATCH] =?UTF-8?q?chore:=20=E5=90=88=E5=85=A5=20stash=20Ops=20har?=
=?UTF-8?q?dening=20=E4=B8=8E=20migration=20000041?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Ask/catalog 权限与审计加固、量表读权限统一,以及未提交的 ops hardening 变更。
Co-authored-by: Cursor
---
.../feature-spec/ops-ask-operations.md | 3 +-
.../feature-spec/ops-funnel-definition.md | 4 +-
.../feature-spec/ops-scale-definition.md | 5 +-
.gates/ECR-012-report.json | 53 ++++++++++++++++
.gates/ECR-016-report.json | 18 ++++++
.gates/ECR-017-report.json | 18 ++++++
.gates/ECR-018-report.json | 18 ++++++
.gates/ECR-019-report.json | 18 ++++++
.gates/ECR-020-report.json | 18 ++++++
.gates/ECR-021-report.json | 18 ++++++
.gates/ECR-022-report.json | 18 ++++++
.gates/ECR-023-report.json | 18 ++++++
.gates/ECR-024-report.json | 18 ++++++
.gates/ECR-025-report.json | 18 ++++++
.gates/ECR-026-report.json | 18 ++++++
.gates/ECR-027-report.json | 18 ++++++
.gates/ECR-028-report.json | 18 ++++++
.gates/ECR-029-report.json | 18 ++++++
.gates/ECR-030-report.json | 18 ++++++
.gates/ECR-031-report.json | 18 ++++++
.gates/ECR-032-report.json | 18 ++++++
.gates/ECR-033-report.json | 18 ++++++
.gates/ECR-034-report.json | 18 ++++++
.gates/ECR-035-report.json | 18 ++++++
.gates/ECR-036-report.json | 18 ++++++
.gates/ECR-037-report.json | 18 ++++++
.gates/ECR-038-report.json | 18 ++++++
.gates/ECR-039-report.json | 18 ++++++
.gates/ECR-040-report.json | 18 ++++++
apps/admin-h5/src/api/client.ts | 14 ++++-
apps/admin-h5/src/pages/AskPage.vue | 21 +++++--
apps/admin-h5/src/pages/CatalogHubPage.vue | 20 +++---
apps/api/internal/handler/admin_ask_ops.go | 26 +++++++-
apps/api/internal/handler/admin_content.go | 2 +-
.../internal/handler/admin_explore_scales.go | 4 +-
.../handler/admin_funnel_definition.go | 4 +-
.../handler/admin_quality_feedback.go | 4 ++
apps/api/internal/handler/ask_feedback.go | 4 ++
apps/api/internal/integration/ask_ops_test.go | 63 ++++++++++++++++++-
.../integration/quality_feedback_test.go | 29 +++++++++
.../internal/middleware/admin_permission.go | 30 ++++++---
.../api/internal/repository/ai_config_repo.go | 4 +-
.../internal/repository/block_policy_repo.go | 2 +-
apps/api/internal/repository/cms_repo.go | 4 +-
.../repository/content_safety_repo.go | 2 +-
.../internal/repository/crisis_event_repo.go | 2 +-
apps/api/internal/repository/crisis_repo.go | 2 +-
.../repository/funnel_definition_repo.go | 2 +-
.../internal/repository/handoff_case_repo.go | 2 +-
.../repository/image_card_deck_repo.go | 2 +-
.../repository/intervention_outcome_repo.go | 2 +-
.../repository/knowledge_chunk_repo.go | 2 +-
.../repository/moderation_case_repo.go | 2 +-
.../repository/privacy_request_repo.go | 2 +-
.../repository/quality_feedback_repo.go | 32 ++++++++++
.../repository/report_template_repo.go | 2 +-
.../internal/repository/rhythm_config_repo.go | 2 +-
apps/api/internal/repository/scale_repo.go | 2 +-
.../repository/scheduled_publication_repo.go | 2 +-
.../internal/repository/star_config_repo.go | 2 +-
.../repository/tool_definition_repo.go | 2 +-
apps/api/internal/service/admin/ask_ops.go | 24 ++++++-
.../service/admin/quality_feedback.go | 9 ++-
apps/api/internal/service/admin/rbac.go | 3 +-
apps/api/internal/service/ask/feedback.go | 3 +
.../migrations/000041_ops_hardening.down.sql | 7 +++
.../migrations/000041_ops_hardening.up.sql | 13 ++++
docs/CHANGELOG.md | 1 +
docs/WAVE0/CATALOG_VS_CASE.md | 17 +++++
proto/openapi.yaml | 30 +++++++--
70 files changed, 848 insertions(+), 69 deletions(-)
create mode 100644 .gates/ECR-012-report.json
create mode 100644 .gates/ECR-016-report.json
create mode 100644 .gates/ECR-017-report.json
create mode 100644 .gates/ECR-018-report.json
create mode 100644 .gates/ECR-019-report.json
create mode 100644 .gates/ECR-020-report.json
create mode 100644 .gates/ECR-021-report.json
create mode 100644 .gates/ECR-022-report.json
create mode 100644 .gates/ECR-023-report.json
create mode 100644 .gates/ECR-024-report.json
create mode 100644 .gates/ECR-025-report.json
create mode 100644 .gates/ECR-026-report.json
create mode 100644 .gates/ECR-027-report.json
create mode 100644 .gates/ECR-028-report.json
create mode 100644 .gates/ECR-029-report.json
create mode 100644 .gates/ECR-030-report.json
create mode 100644 .gates/ECR-031-report.json
create mode 100644 .gates/ECR-032-report.json
create mode 100644 .gates/ECR-033-report.json
create mode 100644 .gates/ECR-034-report.json
create mode 100644 .gates/ECR-035-report.json
create mode 100644 .gates/ECR-036-report.json
create mode 100644 .gates/ECR-037-report.json
create mode 100644 .gates/ECR-038-report.json
create mode 100644 .gates/ECR-039-report.json
create mode 100644 .gates/ECR-040-report.json
create mode 100644 apps/api/migrations/000041_ops_hardening.down.sql
create mode 100644 apps/api/migrations/000041_ops_hardening.up.sql
create mode 100644 docs/WAVE0/CATALOG_VS_CASE.md
diff --git a/.ai/product/feature-spec/ops-ask-operations.md b/.ai/product/feature-spec/ops-ask-operations.md
index d8841a0..b1c89a5 100644
--- a/.ai/product/feature-spec/ops-ask-operations.md
+++ b/.ai/product/feature-spec/ops-ask-operations.md
@@ -21,7 +21,8 @@
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
| GET | `/admin/ask/threads` | `admin.ask.read` | 列表(可选 `user_id`) |
-| GET | `/admin/ask/threads/:id` | `admin.ask.read` | 详情含消息 |
+| GET | `/admin/ask/threads/:id` | `admin.ask.read` | 会话 meta(不含原文) |
+| GET | `/admin/ask/threads/:id/messages` | `admin.ask.transcript.read` | 对话原文(审计 `ask.transcript.read`) |
## Migration
diff --git a/.ai/product/feature-spec/ops-funnel-definition.md b/.ai/product/feature-spec/ops-funnel-definition.md
index 5ffe301..c3beb03 100644
--- a/.ai/product/feature-spec/ops-funnel-definition.md
+++ b/.ai/product/feature-spec/ops-funnel-definition.md
@@ -19,8 +19,8 @@
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
-| GET | `/admin/analytics/funnel-definitions` | `admin.analytics.read` | 只读 |
-| GET | `/admin/analytics/funnel-definitions/{id}` | `admin.analytics.read` | 只读 |
+| GET | `/admin/analytics/funnel-definitions` | `admin.growth.read` | 只读 |
+| GET | `/admin/analytics/funnel-definitions/{id}` | `admin.growth.read` | 只读 |
## Migration
diff --git a/.ai/product/feature-spec/ops-scale-definition.md b/.ai/product/feature-spec/ops-scale-definition.md
index d053372..198a204 100644
--- a/.ai/product/feature-spec/ops-scale-definition.md
+++ b/.ai/product/feature-spec/ops-scale-definition.md
@@ -19,8 +19,9 @@ PATCH status(已有 content.write)· 题干编辑 · UGC · 真支付
| Method | Path | 权限 | 语义 |
|--------|------|------|------|
-| GET | `/admin/explore/scales` | `admin.explore.read` | 只读 |
-| GET | `/admin/explore/scales/{id}` | `admin.explore.read` | 只读 |
+| GET | `/admin/explore/scales` | `admin.explore.read` 或 `admin.content.write` | 只读 |
+| GET | `/admin/explore/scales/{id}` | `admin.explore.read` 或 `admin.content.write` | 只读 |
+| GET | `/admin/scales` | `admin.explore.read` 或 `admin.content.write` | 只读(与 explore 同源) |
## Migration
diff --git a/.gates/ECR-012-report.json b/.gates/ECR-012-report.json
new file mode 100644
index 0000000..17a908c
--- /dev/null
+++ b/.gates/ECR-012-report.json
@@ -0,0 +1,53 @@
+{
+ "ecr": "ECR-012",
+ "result": "BLOCK",
+ "ess_version": "v1.0",
+ "gate_version": "0.1.2",
+ "project_profile": "unknown",
+ "checks": {
+ "artifact": true,
+ "role_boundary": true,
+ "backend_boundary": true,
+ "traceability": false,
+ "tests": false
+ },
+ "violations": [
+ {
+ "rule": "TR-REVIEW-001",
+ "description": "Backend change requires CODE_REVIEW / code-review handoff artifact",
+ "fix_required": [
+ "Write reviewer decision artifact",
+ "Re-run gate"
+ ],
+ "detail": "ECR-012",
+ "check": "traceability-check"
+ },
+ {
+ "rule": "TR-COMMIT-001",
+ "description": "Trace chain requires implementation commit evidence",
+ "fix_required": [
+ "Record commit sha in TRACEABILITY or TEST_REPORT",
+ "Re-run gate"
+ ],
+ "detail": "ECR-012",
+ "check": "traceability-check"
+ },
+ {
+ "rule": "TE-MISSING-001",
+ "description": "Missing TEST_REPORT for implemented change",
+ "fix_required": [
+ "Add docs/TEST_REPORT/ECR-xxx.md",
+ "Re-run gate"
+ ],
+ "detail": "ECR-012",
+ "check": "test-evidence-check"
+ }
+ ],
+ "errors": [
+ "[traceability-check] BLOCK [TR-REVIEW-001]: Backend change requires CODE_REVIEW / code-review handoff artifact \u2014 ECR-012",
+ "[traceability-check] BLOCK [TR-COMMIT-001]: Trace chain requires implementation commit evidence \u2014 ECR-012",
+ "[test-evidence-check] BLOCK [TE-MISSING-001]: Missing TEST_REPORT for implemented change \u2014 ECR-012"
+ ],
+ "warnings": [],
+ "timestamp": "2026-08-07T06:51:10Z"
+}
diff --git a/.gates/ECR-016-report.json b/.gates/ECR-016-report.json
new file mode 100644
index 0000000..09e3717
--- /dev/null
+++ b/.gates/ECR-016-report.json
@@ -0,0 +1,18 @@
+{
+ "ecr": "ECR-016",
+ "result": "PASS",
+ "ess_version": "v1.0",
+ "gate_version": "0.1.2",
+ "project_profile": "unknown",
+ "checks": {
+ "artifact": true,
+ "role_boundary": true,
+ "backend_boundary": true,
+ "traceability": true,
+ "tests": true
+ },
+ "violations": [],
+ "errors": [],
+ "warnings": [],
+ "timestamp": "2026-08-07T10:30:16Z"
+}
diff --git a/.gates/ECR-017-report.json b/.gates/ECR-017-report.json
new file mode 100644
index 0000000..7199ae5
--- /dev/null
+++ b/.gates/ECR-017-report.json
@@ -0,0 +1,18 @@
+{
+ "ecr": "ECR-017",
+ "result": "PASS",
+ "ess_version": "v1.0",
+ "gate_version": "0.1.2",
+ "project_profile": "unknown",
+ "checks": {
+ "artifact": true,
+ "role_boundary": true,
+ "backend_boundary": true,
+ "traceability": true,
+ "tests": true
+ },
+ "violations": [],
+ "errors": [],
+ "warnings": [],
+ "timestamp": "2026-08-07T10:39:01Z"
+}
diff --git a/.gates/ECR-018-report.json b/.gates/ECR-018-report.json
new file mode 100644
index 0000000..5954f7c
--- /dev/null
+++ b/.gates/ECR-018-report.json
@@ -0,0 +1,18 @@
+{
+ "ecr": "ECR-018",
+ "result": "PASS",
+ "ess_version": "v1.0",
+ "gate_version": "0.1.2",
+ "project_profile": "unknown",
+ "checks": {
+ "artifact": true,
+ "role_boundary": true,
+ "backend_boundary": true,
+ "traceability": true,
+ "tests": true
+ },
+ "violations": [],
+ "errors": [],
+ "warnings": [],
+ "timestamp": "2026-08-07T11:26:01Z"
+}
diff --git a/.gates/ECR-019-report.json b/.gates/ECR-019-report.json
new file mode 100644
index 0000000..f01299d
--- /dev/null
+++ b/.gates/ECR-019-report.json
@@ -0,0 +1,18 @@
+{
+ "ecr": "ECR-019",
+ "result": "PASS",
+ "ess_version": "v1.0",
+ "gate_version": "0.1.2",
+ "project_profile": "unknown",
+ "checks": {
+ "artifact": true,
+ "role_boundary": true,
+ "backend_boundary": true,
+ "traceability": true,
+ "tests": true
+ },
+ "violations": [],
+ "errors": [],
+ "warnings": [],
+ "timestamp": "2026-08-07T14:13:03Z"
+}
diff --git a/.gates/ECR-020-report.json b/.gates/ECR-020-report.json
new file mode 100644
index 0000000..cf10708
--- /dev/null
+++ b/.gates/ECR-020-report.json
@@ -0,0 +1,18 @@
+{
+ "ecr": "ECR-020",
+ "result": "PASS",
+ "ess_version": "v1.0",
+ "gate_version": "0.1.2",
+ "project_profile": "unknown",
+ "checks": {
+ "artifact": true,
+ "role_boundary": true,
+ "backend_boundary": true,
+ "traceability": true,
+ "tests": true
+ },
+ "violations": [],
+ "errors": [],
+ "warnings": [],
+ "timestamp": "2026-08-07T15:01:53Z"
+}
diff --git a/.gates/ECR-021-report.json b/.gates/ECR-021-report.json
new file mode 100644
index 0000000..d011c0d
--- /dev/null
+++ b/.gates/ECR-021-report.json
@@ -0,0 +1,18 @@
+{
+ "ecr": "ECR-021",
+ "result": "PASS",
+ "ess_version": "v1.0",
+ "gate_version": "0.1.2",
+ "project_profile": "unknown",
+ "checks": {
+ "artifact": true,
+ "role_boundary": true,
+ "backend_boundary": true,
+ "traceability": true,
+ "tests": true
+ },
+ "violations": [],
+ "errors": [],
+ "warnings": [],
+ "timestamp": "2026-08-07T16:26:53Z"
+}
diff --git a/.gates/ECR-022-report.json b/.gates/ECR-022-report.json
new file mode 100644
index 0000000..4aa521a
--- /dev/null
+++ b/.gates/ECR-022-report.json
@@ -0,0 +1,18 @@
+{
+ "ecr": "ECR-022",
+ "result": "PASS",
+ "ess_version": "v1.0",
+ "gate_version": "0.1.2",
+ "project_profile": "unknown",
+ "checks": {
+ "artifact": true,
+ "role_boundary": true,
+ "backend_boundary": true,
+ "traceability": true,
+ "tests": true
+ },
+ "violations": [],
+ "errors": [],
+ "warnings": [],
+ "timestamp": "2026-08-07T17:28:27Z"
+}
diff --git a/.gates/ECR-023-report.json b/.gates/ECR-023-report.json
new file mode 100644
index 0000000..f6b5b18
--- /dev/null
+++ b/.gates/ECR-023-report.json
@@ -0,0 +1,18 @@
+{
+ "ecr": "ECR-023",
+ "result": "PASS",
+ "ess_version": "v1.0",
+ "gate_version": "0.1.2",
+ "project_profile": "unknown",
+ "checks": {
+ "artifact": true,
+ "role_boundary": true,
+ "backend_boundary": true,
+ "traceability": true,
+ "tests": true
+ },
+ "violations": [],
+ "errors": [],
+ "warnings": [],
+ "timestamp": "2026-08-07T17:48:29Z"
+}
diff --git a/.gates/ECR-024-report.json b/.gates/ECR-024-report.json
new file mode 100644
index 0000000..24363e6
--- /dev/null
+++ b/.gates/ECR-024-report.json
@@ -0,0 +1,18 @@
+{
+ "ecr": "ECR-024",
+ "result": "PASS",
+ "ess_version": "v1.0",
+ "gate_version": "0.1.2",
+ "project_profile": "unknown",
+ "checks": {
+ "artifact": true,
+ "role_boundary": true,
+ "backend_boundary": true,
+ "traceability": true,
+ "tests": true
+ },
+ "violations": [],
+ "errors": [],
+ "warnings": [],
+ "timestamp": "2026-08-07T19:05:41Z"
+}
diff --git a/.gates/ECR-025-report.json b/.gates/ECR-025-report.json
new file mode 100644
index 0000000..ba699c5
--- /dev/null
+++ b/.gates/ECR-025-report.json
@@ -0,0 +1,18 @@
+{
+ "ecr": "ECR-025",
+ "result": "PASS",
+ "ess_version": "v1.0",
+ "gate_version": "0.1.2",
+ "project_profile": "unknown",
+ "checks": {
+ "artifact": true,
+ "role_boundary": true,
+ "backend_boundary": true,
+ "traceability": true,
+ "tests": true
+ },
+ "violations": [],
+ "errors": [],
+ "warnings": [],
+ "timestamp": "2026-08-07T19:08:40Z"
+}
diff --git a/.gates/ECR-026-report.json b/.gates/ECR-026-report.json
new file mode 100644
index 0000000..898d225
--- /dev/null
+++ b/.gates/ECR-026-report.json
@@ -0,0 +1,18 @@
+{
+ "ecr": "ECR-026",
+ "result": "PASS",
+ "ess_version": "v1.0",
+ "gate_version": "0.1.2",
+ "project_profile": "unknown",
+ "checks": {
+ "artifact": true,
+ "role_boundary": true,
+ "backend_boundary": true,
+ "traceability": true,
+ "tests": true
+ },
+ "violations": [],
+ "errors": [],
+ "warnings": [],
+ "timestamp": "2026-08-07T19:13:36Z"
+}
diff --git a/.gates/ECR-027-report.json b/.gates/ECR-027-report.json
new file mode 100644
index 0000000..5f2edfe
--- /dev/null
+++ b/.gates/ECR-027-report.json
@@ -0,0 +1,18 @@
+{
+ "ecr": "ECR-027",
+ "result": "PASS",
+ "ess_version": "v1.0",
+ "gate_version": "0.1.2",
+ "project_profile": "unknown",
+ "checks": {
+ "artifact": true,
+ "role_boundary": true,
+ "backend_boundary": true,
+ "traceability": true,
+ "tests": true
+ },
+ "violations": [],
+ "errors": [],
+ "warnings": [],
+ "timestamp": "2026-08-07T19:14:17Z"
+}
diff --git a/.gates/ECR-028-report.json b/.gates/ECR-028-report.json
new file mode 100644
index 0000000..4e3d03e
--- /dev/null
+++ b/.gates/ECR-028-report.json
@@ -0,0 +1,18 @@
+{
+ "ecr": "ECR-028",
+ "result": "PASS",
+ "ess_version": "v1.0",
+ "gate_version": "0.1.2",
+ "project_profile": "unknown",
+ "checks": {
+ "artifact": true,
+ "role_boundary": true,
+ "backend_boundary": true,
+ "traceability": true,
+ "tests": true
+ },
+ "violations": [],
+ "errors": [],
+ "warnings": [],
+ "timestamp": "2026-08-07T19:14:30Z"
+}
diff --git a/.gates/ECR-029-report.json b/.gates/ECR-029-report.json
new file mode 100644
index 0000000..330dfd1
--- /dev/null
+++ b/.gates/ECR-029-report.json
@@ -0,0 +1,18 @@
+{
+ "ecr": "ECR-029",
+ "result": "PASS",
+ "ess_version": "v1.0",
+ "gate_version": "0.1.2",
+ "project_profile": "unknown",
+ "checks": {
+ "artifact": true,
+ "role_boundary": true,
+ "backend_boundary": true,
+ "traceability": true,
+ "tests": true
+ },
+ "violations": [],
+ "errors": [],
+ "warnings": [],
+ "timestamp": "2026-08-07T19:14:44Z"
+}
diff --git a/.gates/ECR-030-report.json b/.gates/ECR-030-report.json
new file mode 100644
index 0000000..e079afb
--- /dev/null
+++ b/.gates/ECR-030-report.json
@@ -0,0 +1,18 @@
+{
+ "ecr": "ECR-030",
+ "result": "PASS",
+ "ess_version": "v1.0",
+ "gate_version": "0.1.2",
+ "project_profile": "unknown",
+ "checks": {
+ "artifact": true,
+ "role_boundary": true,
+ "backend_boundary": true,
+ "traceability": true,
+ "tests": true
+ },
+ "violations": [],
+ "errors": [],
+ "warnings": [],
+ "timestamp": "2026-08-07T19:15:00Z"
+}
diff --git a/.gates/ECR-031-report.json b/.gates/ECR-031-report.json
new file mode 100644
index 0000000..d5f7185
--- /dev/null
+++ b/.gates/ECR-031-report.json
@@ -0,0 +1,18 @@
+{
+ "ecr": "ECR-031",
+ "result": "PASS",
+ "ess_version": "v1.0",
+ "gate_version": "0.1.2",
+ "project_profile": "unknown",
+ "checks": {
+ "artifact": true,
+ "role_boundary": true,
+ "backend_boundary": true,
+ "traceability": true,
+ "tests": true
+ },
+ "violations": [],
+ "errors": [],
+ "warnings": [],
+ "timestamp": "2026-08-07T19:15:15Z"
+}
diff --git a/.gates/ECR-032-report.json b/.gates/ECR-032-report.json
new file mode 100644
index 0000000..ea1f5ce
--- /dev/null
+++ b/.gates/ECR-032-report.json
@@ -0,0 +1,18 @@
+{
+ "ecr": "ECR-032",
+ "result": "PASS",
+ "ess_version": "v1.0",
+ "gate_version": "0.1.2",
+ "project_profile": "unknown",
+ "checks": {
+ "artifact": true,
+ "role_boundary": true,
+ "backend_boundary": true,
+ "traceability": true,
+ "tests": true
+ },
+ "violations": [],
+ "errors": [],
+ "warnings": [],
+ "timestamp": "2026-08-07T19:15:30Z"
+}
diff --git a/.gates/ECR-033-report.json b/.gates/ECR-033-report.json
new file mode 100644
index 0000000..5ab79e4
--- /dev/null
+++ b/.gates/ECR-033-report.json
@@ -0,0 +1,18 @@
+{
+ "ecr": "ECR-033",
+ "result": "PASS",
+ "ess_version": "v1.0",
+ "gate_version": "0.1.2",
+ "project_profile": "unknown",
+ "checks": {
+ "artifact": true,
+ "role_boundary": true,
+ "backend_boundary": true,
+ "traceability": true,
+ "tests": true
+ },
+ "violations": [],
+ "errors": [],
+ "warnings": [],
+ "timestamp": "2026-08-07T19:15:45Z"
+}
diff --git a/.gates/ECR-034-report.json b/.gates/ECR-034-report.json
new file mode 100644
index 0000000..97e9685
--- /dev/null
+++ b/.gates/ECR-034-report.json
@@ -0,0 +1,18 @@
+{
+ "ecr": "ECR-034",
+ "result": "PASS",
+ "ess_version": "v1.0",
+ "gate_version": "0.1.2",
+ "project_profile": "unknown",
+ "checks": {
+ "artifact": true,
+ "role_boundary": true,
+ "backend_boundary": true,
+ "traceability": true,
+ "tests": true
+ },
+ "violations": [],
+ "errors": [],
+ "warnings": [],
+ "timestamp": "2026-08-07T19:15:59Z"
+}
diff --git a/.gates/ECR-035-report.json b/.gates/ECR-035-report.json
new file mode 100644
index 0000000..94a902a
--- /dev/null
+++ b/.gates/ECR-035-report.json
@@ -0,0 +1,18 @@
+{
+ "ecr": "ECR-035",
+ "result": "PASS",
+ "ess_version": "v1.0",
+ "gate_version": "0.1.2",
+ "project_profile": "unknown",
+ "checks": {
+ "artifact": true,
+ "role_boundary": true,
+ "backend_boundary": true,
+ "traceability": true,
+ "tests": true
+ },
+ "violations": [],
+ "errors": [],
+ "warnings": [],
+ "timestamp": "2026-08-07T19:16:12Z"
+}
diff --git a/.gates/ECR-036-report.json b/.gates/ECR-036-report.json
new file mode 100644
index 0000000..fb50a56
--- /dev/null
+++ b/.gates/ECR-036-report.json
@@ -0,0 +1,18 @@
+{
+ "ecr": "ECR-036",
+ "result": "PASS",
+ "ess_version": "v1.0",
+ "gate_version": "0.1.2",
+ "project_profile": "unknown",
+ "checks": {
+ "artifact": true,
+ "role_boundary": true,
+ "backend_boundary": true,
+ "traceability": true,
+ "tests": true
+ },
+ "violations": [],
+ "errors": [],
+ "warnings": [],
+ "timestamp": "2026-08-07T19:16:26Z"
+}
diff --git a/.gates/ECR-037-report.json b/.gates/ECR-037-report.json
new file mode 100644
index 0000000..a40daed
--- /dev/null
+++ b/.gates/ECR-037-report.json
@@ -0,0 +1,18 @@
+{
+ "ecr": "ECR-037",
+ "result": "PASS",
+ "ess_version": "v1.0",
+ "gate_version": "0.1.2",
+ "project_profile": "unknown",
+ "checks": {
+ "artifact": true,
+ "role_boundary": true,
+ "backend_boundary": true,
+ "traceability": true,
+ "tests": true
+ },
+ "violations": [],
+ "errors": [],
+ "warnings": [],
+ "timestamp": "2026-08-07T19:16:42Z"
+}
diff --git a/.gates/ECR-038-report.json b/.gates/ECR-038-report.json
new file mode 100644
index 0000000..0f613d0
--- /dev/null
+++ b/.gates/ECR-038-report.json
@@ -0,0 +1,18 @@
+{
+ "ecr": "ECR-038",
+ "result": "PASS",
+ "ess_version": "v1.0",
+ "gate_version": "0.1.2",
+ "project_profile": "unknown",
+ "checks": {
+ "artifact": true,
+ "role_boundary": true,
+ "backend_boundary": true,
+ "traceability": true,
+ "tests": true
+ },
+ "violations": [],
+ "errors": [],
+ "warnings": [],
+ "timestamp": "2026-08-07T19:16:57Z"
+}
diff --git a/.gates/ECR-039-report.json b/.gates/ECR-039-report.json
new file mode 100644
index 0000000..b48212b
--- /dev/null
+++ b/.gates/ECR-039-report.json
@@ -0,0 +1,18 @@
+{
+ "ecr": "ECR-039",
+ "result": "PASS",
+ "ess_version": "v1.0",
+ "gate_version": "0.1.2",
+ "project_profile": "unknown",
+ "checks": {
+ "artifact": true,
+ "role_boundary": true,
+ "backend_boundary": true,
+ "traceability": true,
+ "tests": true
+ },
+ "violations": [],
+ "errors": [],
+ "warnings": [],
+ "timestamp": "2026-08-07T19:17:12Z"
+}
diff --git a/.gates/ECR-040-report.json b/.gates/ECR-040-report.json
new file mode 100644
index 0000000..51587da
--- /dev/null
+++ b/.gates/ECR-040-report.json
@@ -0,0 +1,18 @@
+{
+ "ecr": "ECR-040",
+ "result": "PASS",
+ "ess_version": "v1.0",
+ "gate_version": "0.1.2",
+ "project_profile": "unknown",
+ "checks": {
+ "artifact": true,
+ "role_boundary": true,
+ "backend_boundary": true,
+ "traceability": true,
+ "tests": true
+ },
+ "violations": [],
+ "errors": [],
+ "warnings": [],
+ "timestamp": "2026-08-07T19:20:29Z"
+}
diff --git a/apps/admin-h5/src/api/client.ts b/apps/admin-h5/src/api/client.ts
index 9ce106e..56504ac 100644
--- a/apps/admin-h5/src/api/client.ts
+++ b/apps/admin-h5/src/api/client.ts
@@ -272,8 +272,18 @@ export const adminApi = {
message_count: number
created_at: string
updated_at: string
- messages: Array<{ id: string; role: string; content: string; created_at: string }>
}>('GET', `/ask/threads/${id}`),
+ askThreadMessages: (id: string) =>
+ request<{
+ id: string
+ user_id: string
+ profile_id: string
+ scene?: string
+ message_count: number
+ created_at: string
+ updated_at: string
+ messages: Array<{ id: string; role: string; content: string; created_at: string }>
+ }>('GET', `/ask/threads/${id}/messages`),
filterRules: () =>
request<{
items: Array<{
@@ -482,7 +492,7 @@ export const adminApi = {
request>('GET', `/ai/tools/${id}`),
blockPolicies: () =>
request<{ items: Array> }>('GET', '/content-safety/block-policies'),
- blockPolicie: (id: string) =>
+ blockPolicy: (id: string) =>
request>('GET', `/content-safety/block-policies/${id}`),
cases: () =>
request<{ items: Array> }>('GET', '/content-safety/cases'),
diff --git a/apps/admin-h5/src/pages/AskPage.vue b/apps/admin-h5/src/pages/AskPage.vue
index 333d58c..4ba8c89 100644
--- a/apps/admin-h5/src/pages/AskPage.vue
+++ b/apps/admin-h5/src/pages/AskPage.vue
@@ -5,7 +5,9 @@ import { adminApi } from '@/api/client'
import { useAuthStore } from '@/stores/auth'
type Thread = Awaited>['items'][number]
-type Detail = Awaited>
+type Detail = Awaited> & {
+ messages?: Array<{ id: string; role: string; content: string; created_at: string }>
+}
type Feedback = Awaited>['items'][number]
const auth = useAuthStore()
@@ -20,8 +22,10 @@ const rating = ref(4)
const tag = ref('helpful')
const note = ref('')
const fbMsg = ref('')
+const transcriptDenied = ref(false)
const canWriteFeedback = () => auth.can('admin.ask.feedback.write')
+const canReadTranscript = () => auth.can('admin.ask.transcript.read')
async function load() {
loading.value = true
@@ -42,8 +46,16 @@ async function openThread(id: string) {
detailErr.value = ''
selected.value = null
fbMsg.value = ''
+ transcriptDenied.value = false
try {
- selected.value = await adminApi.askThread(id)
+ const meta = await adminApi.askThread(id)
+ selected.value = { ...meta, messages: [] }
+ if (canReadTranscript()) {
+ const full = await adminApi.askThreadMessages(id)
+ selected.value = full
+ } else {
+ transcriptDenied.value = true
+ }
} catch (e) {
detailErr.value = e instanceof Error ? e.message : '详情失败'
} finally {
@@ -123,8 +135,9 @@ onMounted(load)
{{ selected.scene || '—' }} · {{ selected.message_count }} 条 ·
用户
-