diff --git a/docs/superpowers/plans/2026-05-25-muse-cloud-explainer.md b/docs/superpowers/plans/2026-05-25-muse-cloud-explainer.md index a43a616e..2435f152 100644 --- a/docs/superpowers/plans/2026-05-25-muse-cloud-explainer.md +++ b/docs/superpowers/plans/2026-05-25-muse-cloud-explainer.md @@ -2,36 +2,39 @@ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. -**Goal:** Build a HTML-first `docs/explainers/muse-cloud/` architecture explainer that helps junior backend engineers understand the P1R muse-cloud architecture, OpenAPI path composition, domain ownership, request lifecycle, and troubleshooting flow. +**Goal:** Build a HTML-first `docs/explainers/muse-cloud/` architecture explainer that helps junior backend engineers understand the P1R muse-cloud architecture, OpenAPI contract paths, request lifecycle, domain ownership, stage map, and troubleshooting flow. -**Architecture:** The explainer is a static documentation package. `index.html` is the complete reading surface; `content/*.md` files are lightweight source notes for review and maintenance; `assets/styles.css` owns the calm engineering-manual visual system; `assets/app.js` owns small local interactions such as section highlighting, path examples, troubleshooting filters, and copy buttons. +**Architecture:** `index.html` is the complete reading surface. `content/*.md` files are non-authoritative maintenance notes. HTML sections carry source markers that point back to the note file and upstream spec/contract evidence. Static checks verify the required teaching structures, not just keyword presence. -**Tech Stack:** Static HTML5, CSS custom properties, vanilla JavaScript, inline SVG/HTML diagrams, optional Mermaid CDN for simple diagrams, no build step, no backend API calls. +**Tech Stack:** Static HTML5, CSS custom properties, vanilla JavaScript, HTML/CSS/SVG-style diagrams, no build step, no backend API calls. --- +## Corrected Decisions From Review + +1. OpenAPI `paths` entries are already full external paths such as `/app-api/muse/works`; do not teach them as `prefix + resource path` post-processing. +2. HTML is the authoritative explainer. Markdown files are non-authoritative notes for review and maintenance. +3. The page must contain the minimum diagnostic payload from the spec: + - Architecture layers: `负责什么` / `不应负责什么` / `定位证据` + - Request lifecycle nodes: `常见问题` / `定位证据` / `下一步` + - P1R stage cards: `负责领域` / `真实能力` / `假完成形态` / `验收证据` + - Troubleshooting rows: `症状` / `优先定位层` / `检查项` / `下一步` / `证据` +4. Keep commit strategy simple: no task-level commits. Make one final commit after validation. +5. Local server verification must be non-blocking, use an available port, record PID, and clean up. + ## File Structure - Create: `docs/explainers/muse-cloud/README.md` - - Explains the purpose, source-of-truth hierarchy, and how to open `index.html`. - Create: `docs/explainers/muse-cloud/index.html` - - Complete HTML-first guide with full content, semantic sections, accessible navigation, embedded markdown text blocks, HTML/CSS/SVG diagrams, and troubleshooting panels. - Create: `docs/explainers/muse-cloud/content/architecture.md` - - Lightweight source notes for cloud architecture. - Create: `docs/explainers/muse-cloud/content/openapi-paths.md` - - Lightweight source notes for OpenAPI path composition. - Create: `docs/explainers/muse-cloud/content/request-lifecycle.md` - - Lightweight source notes for request lifecycle. - Create: `docs/explainers/muse-cloud/content/domain-owners.md` - - Lightweight source notes for domain owner boundaries. - Create: `docs/explainers/muse-cloud/content/p1r-stages.md` - - Lightweight source notes for P1R stage map. - Create: `docs/explainers/muse-cloud/content/troubleshooting.md` - - Lightweight source notes for troubleshooting playbook. +- Create: `docs/explainers/muse-cloud/content/sources.md` - Create: `docs/explainers/muse-cloud/assets/styles.css` - - Visual design system, layout, diagrams, responsive behavior, print styles, and accessibility states. - Create: `docs/explainers/muse-cloud/assets/app.js` - - Local-only interactions. It must not fetch real APIs, mutate repo files, or require a build step. Do not modify: @@ -42,13 +45,15 @@ Do not modify: - `.superpowers/**` - `docs/superpowers/plans/P1-execution-prompt.md` - `docs/superpowers/plans/P3-execution-prompt.md` +- `docs/superpowers/reports/**` -## Task 1: Create the Explainer Directory and Maintenance README +## Task 1: Verify Source Facts and Scaffold Notes **Files:** - Create: `docs/explainers/muse-cloud/README.md` +- Create: `docs/explainers/muse-cloud/content/*.md` -- [ ] **Step 1: Confirm the working tree and unrelated files** +- [ ] **Step 1: Inspect current worktree** Run: @@ -56,9 +61,32 @@ Run: git status --short ``` -Expected: existing unrelated untracked files may include `.superpowers/`, `docs/superpowers/plans/P1-execution-prompt.md`, and `docs/superpowers/plans/P3-execution-prompt.md`. Do not add or modify them. +Expected: unrelated untracked files may exist. Do not stage or edit them. -- [ ] **Step 2: Create the explainer directory** +- [ ] **Step 2: Verify OpenAPI full-path fact** + +Run: + +```bash +rg -n "^ /app-api/muse/works:" docs/api-contracts/content/openapi.yaml +rg -n "operationId: listWorks" docs/api-contracts/content/openapi.yaml +rg -n "^ /admin-api/muse/account/users:" docs/api-contracts/account/openapi.yaml +``` + +Expected: all three commands find matches. This proves the explainer must say contract `paths` are complete external paths. + +- [ ] **Step 3: Verify code example facts** + +Run: + +```bash +rg -n "class AppContentController|interface ContentAppService|class ContentAppServiceImpl" \ + muse-cloud/muse-module-content/muse-module-content-server/src/main/java +``` + +Expected: matches for `AppContentController`, `ContentAppService`, and `ContentAppServiceImpl`. If any class is missing, mark controller/service names in the explainer as examples instead of verified facts. + +- [ ] **Step 4: Create directories** Run: @@ -68,1052 +96,119 @@ mkdir -p docs/explainers/muse-cloud/content docs/explainers/muse-cloud/assets Expected: command exits with status `0`. -- [ ] **Step 3: Write the README** +- [ ] **Step 5: Create README and note files** -Use `apply_patch` to create `docs/explainers/muse-cloud/README.md`: +Create these files with `apply_patch`: + +- `README.md`: explain purpose, point to `index.html`, state HTML is authoritative, state Markdown notes are non-authoritative, list source priority, and warn that catch-all / generic persistence / placeholder SSE / empty list / accepted task are not P1R completion. +- `content/architecture.md`: short notes for Controller, Application Service, Domain, Query/Assembler, Persistence Mapper, External Adapter, CommonResult. +- `content/openapi-paths.md`: state that OpenAPI `paths` are already full external paths; include `/app-api/muse/works` and `listWorks`. +- `content/request-lifecycle.md`: list the lifecycle from request to `CommonResult(code, data, msg)`. +- `content/domain-owners.md`: list Content, Meta, Account, AI, Knowledge, Market, Events owner boundaries. +- `content/p1r-stages.md`: list P1R-0 through P1R-7. +- `content/troubleshooting.md`: list all eight required symptoms. +- `content/sources.md`: map each HTML section to its note file and upstream source. + +Required source mapping in `content/sources.md`: ```markdown -# muse-cloud 架构讲解包 - -这个目录用于给初级后端工程师讲清楚 P1R 规格下的 `muse-cloud` 后端架构、OpenAPI 路径组合、请求生命周期、领域 owner 和排障路径。 - -主入口是 [`index.html`](./index.html)。HTML 页面必须能独立阅读,不能要求读者先读 Markdown 才能理解系统。 - -## 内容关系 - -- `index.html`:完整讲解页面,包含正文、复杂图、路径示例、阶段地图和排障流程。 -- `content/*.md`:轻量维护稿,保存短正文、简单 Mermaid 和可 diff 的规则文本。 -- `assets/styles.css`:页面视觉系统。 -- `assets/app.js`:本地交互,例如目录定位、路径示例切换、排障筛选和复制按钮。 - -## 信息来源优先级 - -1. `docs/superpowers/specs/2026-05-25-P1R-muse-cloud-real-api-design.md` -2. `docs/superpowers/specs/2026-05-25-P1R-0-baseline-gate-design.md` -3. `docs/api-contracts/**/openapi.yaml` -4. `muse-cloud/` 当前代码事实 - -如果讲解包与上游规格或 API 合同冲突,以上游规格和 API 合同为准,修正讲解包。 - -## 明确边界 - -这个目录不证明 P1R API 已完成。catch-all、通用持久化、占位 SSE、空列表和 accepted task 都不能被解释成真实业务完成态。 - -这个目录不保存真实凭据、环境密码、token 或外部服务密钥。 +| HTML section | Maintenance note | Upstream source | +|--------------|------------------|-----------------| +| architecture | content/architecture.md | P1R real API design section 7 | +| openapi | content/openapi-paths.md | docs/api-contracts/**/openapi.yaml | +| lifecycle | content/request-lifecycle.md | P1R real API design section 5 and 7 | +| owners | content/domain-owners.md | P1R real API design section 5.3 | +| stages | content/p1r-stages.md | P1R real API design section 10 | +| troubleshooting | content/troubleshooting.md | explainer design section 7.6 | ``` -- [ ] **Step 4: Verify README exists** +- [ ] **Step 6: Verify note files** Run: ```bash -test -f docs/explainers/muse-cloud/README.md -``` - -Expected: command exits with status `0`. - -- [ ] **Step 5: Commit Task 1** - -Run: - -```bash -git add docs/explainers/muse-cloud/README.md -git commit -m "docs(explainer): add muse-cloud guide readme" -``` - -Expected: commit succeeds and only `README.md` is included. - -## Task 2: Add Markdown Source Notes - -**Files:** -- Create: `docs/explainers/muse-cloud/content/architecture.md` -- Create: `docs/explainers/muse-cloud/content/openapi-paths.md` -- Create: `docs/explainers/muse-cloud/content/request-lifecycle.md` -- Create: `docs/explainers/muse-cloud/content/domain-owners.md` -- Create: `docs/explainers/muse-cloud/content/p1r-stages.md` -- Create: `docs/explainers/muse-cloud/content/troubleshooting.md` - -- [ ] **Step 1: Create `architecture.md`** - -Use `apply_patch`: - -````markdown -# 架构总览 - -`muse-cloud` 是基于 Yudao Cloud fork 的后端工程。P1R 的目标不是让接口返回占位结果,而是让每个 OpenAPI operation 都有真实的 Controller、Application Service、Domain 规则、Persistence、外部闭环和测试证据。 - -```mermaid -flowchart TD - C[Controller] --> A[Application Service] - A --> D[Domain Service / Aggregate] - A --> Q[Query Service / Assembler] - A --> O[Outbox / Job Service] - D --> P[Persistence Mapper] - O --> E[External Adapter] - E --> N[New-API] - E --> R[RAGFlow / Knowledge Engine] - E --> F[File / Object Storage] - E --> S[SSE Event Stream] - Q --> V[OpenAPI DTO] -``` - -核心判断:Controller 只做入口转换;Application 是用例和事务边界;Domain 持有状态机和不变式;Infrastructure 负责数据库、Redis、文件、New-API、RAGFlow 和 SSE;Query/Assembler 负责读模型和 OpenAPI DTO。 -```` - -- [ ] **Step 2: Create `openapi-paths.md`** - -Use `apply_patch`: - -````markdown -# OpenAPI 路径组合 - -Muse API 路径由三段组成: - -```text -docs/api-contracts//openapi.yaml - + side prefix: /app-api/muse 或 /admin-api/muse - + resource path: 合同 paths 下的资源路径 -``` - -`operationId` 是后端映射、完成矩阵和排障时最稳定的识别键。 - -示例: - -```text -docs/api-contracts/content/openapi.yaml - -> /app-api/muse/works - -> listWorks - -> AppContentController - -> ContentAppService -``` - -规则: - -- `/app-api/muse/**` 是 app 入口。 -- `/admin-api/muse/**` 是 admin 入口。 -- app/admin 不是领域 owner。 -- 所有 Muse API 必须支持 `X-API-Version: 1`。 -- 对外响应是 `code/data/msg`,不是 `message`。 -```` - -- [ ] **Step 3: Create `request-lifecycle.md`** - -Use `apply_patch`: - -````markdown -# 请求生命周期 - -一次真实业务请求应该按固定链路流转: - -```text -HTTP Request - -> X-API-Version 校验 - -> 登录 / RBAC / owner / tenant 校验 - -> Controller DTO - -> Application Service 事务与幂等 - -> Domain 状态机和不变式 - -> Mapper / External Adapter - -> Query / Assembler - -> CommonResult(code, data, msg) -``` - -写命令必须有 `commandId` 或合同指定的等价幂等键。覆盖事实的写命令必须校验 revision、expectedVersion、expectedStatus 或 expectedActiveVersion。 - -读接口必须返回 OpenAPI 对应的业务读模型,不能返回 operation record、workflow task 原始行、通用 Map、空列表伪结果或 `persisted: true`。 -```` - -- [ ] **Step 4: Create `domain-owners.md`** - -Use `apply_patch`: - -```markdown -# 领域 owner - -后端按领域模块拆,不按 admin/app 拆。`admin-api` 和 `app-api` 只是入口不同,不能形成两套事实。 - -| 领域 | owner 事实 | 典型外部依赖 | 不应越界 | -|------|------------|--------------|----------| -| Content | 作品、章节、Block、导入导出、Suggestion Merge | File、AI suggestion | 不直接拥有市场授权 | -| Meta | MetaSchema、保护节点、功能链治理 | PostgreSQL、审计 | 不被用户槽位覆盖保护节点 | -| Account | 用户资料、权益、配额、用量、New-API 归因 | New-API、账本 | 不允许请求体伪造归因 | -| AI | Prompt、Agent、Tool Grant、任务、质量治理 | New-API、SSE | 不伪造 AI 调用成功 | -| Knowledge | 知识库、文档、切片、索引、图谱 | RAGFlow、File | 不用空列表代替索引结果 | -| Market | 资产、授权、安装、发布、申诉、handoff | Account、授权快照 | 不写目标领域绑定事实 | -| Events | SSE、任务事件、跨端事件 | Redis、SSE | 不返回固定假事件 | -``` - -- [ ] **Step 5: Create `p1r-stages.md`** - -Use `apply_patch`: - -```markdown -# P1R 阶段地图 - -P1R 采用“总 spec + 阶段 spec + 阶段 plan”的执行模型。 - -| 阶段 | 目标 | -|------|------| -| P1R-0 | 清点全部 operation,建立 API 完成矩阵 | -| P1R-1 | 补齐 Content 真实业务 API | -| P1R-2 | 实现 MetaSchema 和治理真实 API | -| P1R-3 | 实现账户、权益、配额、用量和归因真实 API | -| P1R-4 | 实现 AI 编排、任务、智能体和质量治理真实 API | -| P1R-5 | 实现 Knowledge 文档、切片、索引、检索和图谱真实 API | -| P1R-6 | 实现 Market 资产、授权、安装、发布和申诉真实 API | -| P1R-7 | 做端到端验收,证明外部闭环和统一事件流 | - -P1R-0 不输出 completed。它只证明基线矩阵可生成,不证明任何 API 已完成。 -``` - -- [ ] **Step 6: Create `troubleshooting.md`** - -Use `apply_patch`: - -```markdown -# 排障手册 - -排障优先按层定位,不要先猜业务逻辑。 - -| 症状 | 优先定位层 | 检查项 | -|------|------------|--------| -| 404 | OpenAPI 路径 / Controller | side prefix、`/muse`、资源路径、Controller mapping | -| 400 | Header / DTO | `X-API-Version`、必填字段、schema、参数校验 | -| 401/403 | Security | 登录、RBAC、owner、tenant、来源授权 | -| 409 | Domain / Idempotency | `commandId`、revision、expectedVersion、状态机 | -| 空列表但应有数据 | Query / Persistence | owner 条件、tenant 条件、读模型、mapper SQL | -| accepted 但不终态 | Job / Adapter | workflow task、outbox、外部服务回调、重试 | -| 外部服务失败 | Adapter | New-API、RAGFlow、File、SSE、超时和错误分类 | -| 响应字段不对 | DTO / CommonResult | `code/data/msg`、assembler、禁止 `message` | -``` - -- [ ] **Step 7: Verify Markdown files** - -Run: - -```bash -for f in architecture openapi-paths request-lifecycle domain-owners p1r-stages troubleshooting; do +for f in architecture openapi-paths request-lifecycle domain-owners p1r-stages troubleshooting sources; do test -f "docs/explainers/muse-cloud/content/$f.md" || exit 1 done rg -n "TBD|TODO|待定" docs/explainers/muse-cloud/content && exit 1 || true +rg -n "prefix \\+ resource|side prefix \\+ resource|路径由三段组成" docs/explainers/muse-cloud/content && exit 1 || true ``` -Expected: all files exist, and the `rg` command does not find placeholders. +Expected: files exist; no placeholders; no old wrong OpenAPI path-composition phrasing. -- [ ] **Step 8: Commit Task 2** - -Run: - -```bash -git add docs/explainers/muse-cloud/content -git commit -m "docs(explainer): add muse-cloud source notes" -``` - -Expected: commit succeeds and only `content/*.md` files are included. - -## Task 3: Build the HTML Main Document +## Task 2: Build the HTML, CSS, and JS **Files:** - Create: `docs/explainers/muse-cloud/index.html` - -- [ ] **Step 1: Create the complete HTML document** - -Use `apply_patch` to create `docs/explainers/muse-cloud/index.html` with this complete content: - -```html - - - - - - muse-cloud 架构讲解 - - - - - -
- - -
-
-

给初级后端的 P1R 导航

-

muse-cloud 架构讲解

-

这份页面解释 P1R 规格下的 muse-cloud 后端完成态:接口不只是能返回响应,而是要有真实 Controller、Application、Domain、Persistence、外部闭环和验收证据。

-
- 不要误读: - catch-all、通用持久化、占位 SSE、空列表和 accepted task 都不是 P1R 完成态。 -
-
- 1 架构 - 2 路径 - 3 生命周期 - 4 owner - 5 排障 -
-
- -
-
-

Architecture

-

系统一张图

-

真实 API 的完成态必须穿过清晰分层。Controller 只做入口转换;Application 是用例和事务边界;Domain 持有状态机和不变式;Infrastructure 连接 PostgreSQL、Redis、文件、New-API、RAGFlow 和 SSE。

-
-
-
Controller
鉴权、参数、X-API-Version: 1、DTO
-
Application Service
事务、幂等、审计、外部编排
-
- Domain
状态机 / 不变式
- Query / Assembler
读模型 / OpenAPI DTO
-
-
- Persistence Mapper
PostgreSQL
- External Adapter
New-API / RAGFlow / File / SSE
-
-
CommonResult<T>
code / data / msg
-
-
- -
-
-

OpenAPI

-

路径怎么组合

-

先找到领域合同文件,再判断入口侧,最后把合同里的资源路径接到 `/app-api/muse` 或 `/admin-api/muse` 后面。`operationId` 是后端映射和完成矩阵最稳定的识别键。

-
-
- docs/api-contracts/content/openapi.yaml - + - /app-api/muse - + - /works - = - /app-api/muse/works -
-
- - -
-
    -
  • `/app-api/muse/**` 是 app 入口,`/admin-api/muse/**` 是 admin 入口。
  • -
  • app/admin 不是领域 owner,领域事实只能由领域模块拥有。
  • -
  • 所有 Muse API 必须支持 `X-API-Version: 1`。
  • -
  • 对外响应字段是 `code/data/msg`,不能写成 `message`。
  • -
-
- -
-
-

Lifecycle

-

一次请求怎么走

-

排查业务问题时按链路逐层缩小,不要先猜数据库或前端。写命令还要额外检查 `commandId`、revision、expectedVersion、expectedStatus 或 expectedActiveVersion。

-
-
- HTTP Request - Version / Auth - Controller DTO - Application - Domain - DB / Adapter - Assembler - CommonResult -
-
- -
-
-

Owner

-

领域 owner 怎么分

-

后端按领域模块拆,不按 admin/app 拆。同一个业务事实只能有一个 owner,跨模块协作要通过 facade、Application Service、事件投影或读模型。

-
-
- - - - - - - - - - - - - -
领域owner 事实典型外部依赖不应越界
Content作品、章节、Block、导入导出File、AI suggestion不直接拥有市场授权
MetaMetaSchema、保护节点、功能链治理PostgreSQL、审计不被用户槽位覆盖保护节点
Account资料、权益、配额、用量、归因New-API、账本不允许请求体伪造归因
AIPrompt、Agent、Tool Grant、任务New-API、SSE不伪造 AI 调用成功
Knowledge知识库、文档、切片、索引、图谱RAGFlow、File不用空列表代替索引结果
Market资产、授权、安装、发布、申诉Account、授权快照不写目标领域绑定事实
EventsSSE、任务事件、跨端事件Redis、SSE不返回固定假事件
-
-
- -
-
-

Stages

-

P1R 阶段地图

-

P1R 采用“总 spec + 阶段 spec + 阶段 plan”。P1R-0 只生成完成矩阵,不输出 completed;后续阶段按领域补真实 API。

-
-
-
P1R-0API 基线门禁
-
P1R-1Content Real API
-
P1R-2Meta Real API
-
P1R-3Account Real API
-
P1R-4AI Real API
-
P1R-5Knowledge Real API
-
P1R-6Market Real API
-
P1R-7端到端验收
-
-
- -
-
-

Troubleshooting

-

问题怎么定位

-

先按症状定位层级,再找证据。不要用前端可见性、空响应或任务 accepted 状态替代后端真实完成。

-
-
-

404:先查路径组合和 Controller

检查 side prefix、`/muse`、资源路径、Controller mapping 和 catch-all。

-

400:先查 Header 和 DTO

检查 `X-API-Version: 1`、必填字段、schema 和参数校验。

-

401/403:先查 Security

检查登录、RBAC、owner、tenant 和来源授权。

-

409:先查幂等和状态机

检查 `commandId`、revision、expectedVersion、expectedStatus 和非法状态转移。

-

外部失败:先查 Adapter

检查 New-API、RAGFlow、File、SSE、超时、重试和错误分类。

-

响应字段不对:先查 DTO / CommonResult

对外响应必须是 `code/data/msg`,不能使用 `message`。

-
-
- -
-
-

Maintenance

-

维护规则

-

P1R spec、P1R-0 报告或 OpenAPI 合同更新后,必须检查本讲解包是否需要同步。冲突时以上游规格和 API 合同为准。

-
-
- 不在讲解包中记录真实凭据、环境密码、token 或外部服务密钥;不把未验证实现写成已完成。 -
-
-

Markdown 维护源

-
- architecture.md -
# 架构总览
-
-Controller -> Application Service -> Domain -> Persistence / External Adapter -> CommonResult。
-
-
- openapi-paths.md -
docs/api-contracts/<domain>/openapi.yaml + /app-api/muse 或 /admin-api/muse + resource path。
-
-
-
-
- - -
- - - -``` - -- [ ] **Step 2: Verify HTML structure** - -Run: - -```bash -node <<'NODE' -const fs = require('fs'); -const html = fs.readFileSync('docs/explainers/muse-cloud/index.html', 'utf8'); -for (const id of ['overview','architecture','openapi','lifecycle','owners','stages','troubleshooting','maintenance']) { - if (!html.includes(`id="${id}"`)) throw new Error(`missing section ${id}`); -} -for (const token of ['X-API-Version: 1','code/data/msg','catch-all','Application Service','Domain','New-API','RAGFlow']) { - if (!html.includes(token)) throw new Error(`missing required token ${token}`); -} -if (html.includes('TODO') || html.includes('TBD') || html.includes('待定')) { - throw new Error('placeholder text found'); -} -NODE -``` - -Expected: command exits with status `0`. - -- [ ] **Step 3: Commit Task 3** - -Run: - -```bash -git add docs/explainers/muse-cloud/index.html -git commit -m "docs(explainer): build muse-cloud html guide" -``` - -Expected: commit succeeds and only `index.html` is included. - -## Task 4: Add the Visual Design System - -**Files:** - Create: `docs/explainers/muse-cloud/assets/styles.css` - -- [ ] **Step 1: Create the complete CSS file** - -Use `apply_patch` to create `docs/explainers/muse-cloud/assets/styles.css` with this complete content: - -```css -:root { - color-scheme: light; - --bg: #f7f8f5; - --surface: #ffffff; - --surface-muted: #eef1ec; - --text: #1f2823; - --muted: #66746b; - --line: #d9dfd8; - --accent: #2f6f5e; - --accent-strong: #1f5749; - --warning: #a15c1b; - --danger: #a63f3f; - --code-bg: #edf2ef; - --radius: 8px; - --shadow: 0 18px 45px rgba(31, 40, 35, 0.08); - font-family: ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; -} - -* { - box-sizing: border-box; -} - -html { - scroll-behavior: smooth; -} - -body { - margin: 0; - background: var(--bg); - color: var(--text); - line-height: 1.65; -} - -a { - color: inherit; -} - -code, -pre { - font-family: "SFMono-Regular", Consolas, "Liberation Mono", monospace; -} - -code { - background: var(--code-bg); - border-radius: 4px; - padding: 0.1rem 0.28rem; -} - -pre { - background: var(--code-bg); - border-radius: var(--radius); - overflow-x: auto; - padding: 14px; -} - -.skip-link { - background: var(--text); - border-radius: 0 0 6px 6px; - color: #fff; - left: 24px; - padding: 8px 12px; - position: fixed; - top: -48px; - z-index: 20; -} - -.skip-link:focus { - top: 0; -} - -.shell { - display: grid; - grid-template-columns: 250px minmax(0, 1fr) 270px; - gap: 28px; - max-width: 1480px; - margin: 0 auto; - padding: 28px; -} - -.side-nav, -.quick-panel { - position: sticky; - top: 24px; - align-self: start; -} - -.content { - min-width: 0; -} - -.side-nav, -.quick-panel, -.section { - background: var(--surface); - border: 1px solid var(--line); - border-radius: var(--radius); - box-shadow: var(--shadow); -} - -.side-nav, -.quick-panel { - padding: 16px; -} - -.brand-block { - border-bottom: 1px solid var(--line); - display: grid; - gap: 4px; - margin-bottom: 14px; - padding-bottom: 14px; -} - -.eyebrow, -.kicker { - color: var(--accent-strong); - font-size: 0.78rem; - font-weight: 700; - letter-spacing: 0; - margin: 0 0 8px; - text-transform: uppercase; -} - -.side-nav a { - border-radius: 6px; - color: var(--muted); - display: block; - padding: 8px 10px; - text-decoration: none; -} - -.side-nav a:hover, -.side-nav a[aria-current] { - background: var(--surface-muted); - color: var(--text); -} - -.quick-panel h2 { - font-size: 1rem; - margin: 0 0 12px; -} - -.quick-panel { - display: grid; - gap: 8px; -} - -.section { - margin-bottom: 22px; - padding: clamp(22px, 4vw, 42px); -} - -.opening { - border-color: rgba(47, 111, 94, 0.28); -} - -.section-header { - max-width: 820px; - margin-bottom: 24px; -} - -h1, -h2, -h3 { - line-height: 1.18; - margin: 0 0 12px; -} - -h1 { - font-size: clamp(2rem, 5vw, 4.2rem); -} - -h2 { - font-size: clamp(1.45rem, 3vw, 2.2rem); -} - -h3 { - font-size: 1rem; -} - -.lead { - color: var(--muted); - font-size: 1.08rem; - max-width: 780px; -} - -.notice { - background: #fff8ed; - border: 1px solid #ead3ad; - border-radius: var(--radius); - color: #5f3b13; - margin: 20px 0; - padding: 14px 16px; -} - -.read-path, -.button-row { - display: flex; - flex-wrap: wrap; - gap: 8px; -} - -.read-path span { - background: var(--surface-muted); - border: 1px solid var(--line); - border-radius: 6px; - padding: 7px 10px; -} - -.diagram-card { - border: 1px solid var(--line); - border-radius: var(--radius); - background: #fbfcfa; - margin-top: 18px; - padding: 18px; - overflow-x: auto; -} - -.layered-map { - display: grid; - gap: 10px; -} - -.layer { - background: var(--surface); - border: 1px solid var(--line); - border-radius: 6px; - padding: 12px; - text-align: center; -} - -.layer span, -.layer small { - color: var(--muted); -} - -.layer.split { - display: grid; - grid-template-columns: repeat(2, minmax(180px, 1fr)); - gap: 10px; -} - -.layer.result { - border-color: rgba(47, 111, 94, 0.35); -} - -.path-composer { - align-items: center; - display: flex; - flex-wrap: wrap; - gap: 10px; -} - -.path-composer strong { - color: var(--accent-strong); - word-break: break-all; -} - -.rule-list { - color: var(--muted); - margin-top: 18px; -} - -.flow-grid { - display: grid; - grid-template-columns: repeat(4, minmax(130px, 1fr)); - gap: 10px; -} - -.flow-grid span { - background: var(--surface); - border: 1px solid var(--line); - border-radius: 6px; - padding: 10px; - text-align: center; -} - -.table-wrap { - overflow-x: auto; -} - -.owner-table { - border-collapse: collapse; - min-width: 760px; - width: 100%; -} - -.owner-table th, -.owner-table td { - border-bottom: 1px solid var(--line); - padding: 10px 12px; - text-align: left; - vertical-align: top; -} - -.owner-table th { - color: var(--accent-strong); - font-size: 0.88rem; -} - -.stage-track { - display: grid; - gap: 10px; - grid-template-columns: repeat(4, minmax(150px, 1fr)); -} - -.stage-track article { - border: 1px solid var(--line); - border-radius: 6px; - padding: 12px; -} - -.stage-track strong, -.stage-track span { - display: block; -} - -.stage-track span { - color: var(--muted); - font-size: 0.92rem; -} - -.diagnostic-list { - display: grid; - gap: 10px; -} - -.diagnostic-row { - border: 1px solid var(--line); - border-left: 4px solid var(--accent); - border-radius: 6px; - padding: 12px 14px; -} - -.diagnostic-row[data-highlight] { - background: #eef6f1; - border-color: rgba(47, 111, 94, 0.45); -} - -.copy-button, -.diagnostic-chip { - border: 1px solid var(--line); - border-radius: 6px; - background: var(--surface); - color: var(--text); - cursor: pointer; - font: inherit; - padding: 8px 10px; -} - -.copy-button[data-copied="true"] { - background: #e5f2ec; - border-color: rgba(47, 111, 94, 0.45); -} - -.copy-button:hover, -.diagnostic-chip:hover, -.copy-button:focus-visible, -.diagnostic-chip:focus-visible, -.side-nav a:focus-visible { - border-color: var(--accent); - outline: 3px solid rgba(47, 111, 94, 0.18); - outline-offset: 2px; -} - -.source-notes { - border-top: 1px solid var(--line); - margin-top: 24px; - padding-top: 18px; -} - -.source-notes details { - border: 1px solid var(--line); - border-radius: 6px; - margin-bottom: 8px; - padding: 10px; -} - -@media (max-width: 1120px) { - .shell { - grid-template-columns: 220px minmax(0, 1fr); - } - - .quick-panel { - grid-column: 1 / -1; - position: static; - } -} - -@media (max-width: 760px) { - .shell { - display: block; - padding: 14px; - } - - .side-nav, - .quick-panel { - position: static; - margin-bottom: 14px; - } - - .side-nav { - display: grid; - gap: 6px; - } - - .section { - padding: 18px; - } - - .flow-grid, - .stage-track, - .layer.split { - grid-template-columns: 1fr; - } - - h1 { - font-size: 2rem; - } -} - -@media print { - .side-nav, - .quick-panel, - .copy-button { - display: none; - } - - .shell { - display: block; - padding: 0; - } - - .section { - box-shadow: none; - break-inside: avoid; - } -} -``` - -- [ ] **Step 2: Verify CSS quality floor** - -Run: - -```bash -node <<'NODE' -const fs = require('fs'); -const css = fs.readFileSync('docs/explainers/muse-cloud/assets/styles.css', 'utf8'); -for (const token of ['--accent', '.shell', '.section', '.diagram-card', '@media (max-width: 760px)', '@media print']) { - if (!css.includes(token)) throw new Error(`missing css token ${token}`); -} -if (/letter-spacing:\s*-[0-9.]/.test(css)) throw new Error('negative letter spacing is not allowed'); -if (/border-radius:\s*(1[0-9]|[2-9][0-9])px/.test(css)) throw new Error('radius above 8px found'); -NODE -``` - -Expected: command exits with status `0`. - -- [ ] **Step 3: Commit Task 4** - -Run: - -```bash -git add docs/explainers/muse-cloud/assets/styles.css -git commit -m "docs(explainer): style muse-cloud guide" -``` - -Expected: commit succeeds and only `assets/styles.css` is included. - -## Task 5: Add Local Interactions - -**Files:** - Create: `docs/explainers/muse-cloud/assets/app.js` -- [ ] **Step 1: Implement section highlighting, copy buttons, and diagnostic filters** +- [ ] **Step 1: Create `index.html`** -Use `apply_patch` to create `docs/explainers/muse-cloud/assets/app.js`: +Create a complete standalone HTML page. It must satisfy this content contract: -```javascript -(function () { - const navLinks = Array.from(document.querySelectorAll('.side-nav a[href^="#"]')); - const sections = navLinks - .map((link) => document.querySelector(link.getAttribute('href'))) - .filter(Boolean); +1. Navigation: + - Desktop layout has left nav, main content, and right quick panel. + - Mobile layout keeps quick triage before main content, not after all content. +2. `#overview`: + - States this is an explainer, not proof of API completion. + - Explicitly says catch-all, generic persistence, placeholder SSE, empty list, and accepted task are not complete. +3. `#architecture`: + - Contains at least seven `.architecture-layer` blocks for Controller, Application Service, Domain, Query/Assembler, Persistence Mapper, External Adapter, CommonResult. + - Each block contains labels `负责什么`, `不应负责什么`, and `定位证据`. +4. `#openapi`: + - Says OpenAPI `paths` are complete external paths. + - Contains `/app-api/muse/works`, `listWorks`, `AppContentController`, and `ContentAppService`. + - Does not say paths are assembled by appending a side prefix to a resource path. + - Includes a source note pointing to `docs/api-contracts/content/openapi.yaml`. +5. `#lifecycle`: + - Contains eight `.lifecycle-node` blocks: HTTP Request, Version/Auth, Controller DTO, Application, Domain, DB/Adapter, Assembler, CommonResult. + - Each block contains labels `常见问题`, `定位证据`, and `下一步`. +6. `#owners`: + - Covers Content, Meta, Account, AI, Knowledge, Market, Events. + - Desktop may use a table; mobile must have readable owner cards or definition lists without horizontal scrolling as the only access path. +7. `#stages`: + - Contains eight `.stage-card` blocks for P1R-0 through P1R-7. + - Each block contains labels `负责领域`, `真实能力`, `假完成形态`, and `验收证据`. +8. `#troubleshooting`: + - Contains eight `.diagnostic-row` blocks for 404, 400, 401/403, 409, 空列表但应有数据, accepted 但不终态, 外部服务失败, 响应字段不对. + - Each block contains labels `症状`, `优先定位层`, `检查项`, `下一步`, and `证据`. +9. `#maintenance`: + - States HTML is authoritative and Markdown files are non-authoritative notes. + - Shows section-to-source mapping. - const setActive = (id) => { - navLinks.forEach((link) => { - const isActive = link.getAttribute('href') === `#${id}`; - link.toggleAttribute('aria-current', isActive); - }); - }; +Every major section must include `data-source="content/.md"` and `data-upstream="..."` attributes. - if ('IntersectionObserver' in window) { - const observer = new IntersectionObserver((entries) => { - const visible = entries - .filter((entry) => entry.isIntersecting) - .sort((a, b) => b.intersectionRatio - a.intersectionRatio)[0]; - if (visible && visible.target.id) { - setActive(visible.target.id); - } - }, { rootMargin: '-20% 0px -65% 0px', threshold: [0.15, 0.35, 0.65] }); - sections.forEach((section) => observer.observe(section)); - } +- [ ] **Step 2: Create `assets/styles.css`** - document.querySelectorAll('[data-copy]').forEach((button) => { - button.addEventListener('click', async () => { - const value = button.getAttribute('data-copy') || ''; - try { - await navigator.clipboard.writeText(value); - button.dataset.copied = 'true'; - button.textContent = '已复制'; - window.setTimeout(() => { - button.dataset.copied = 'false'; - button.textContent = button.getAttribute('data-label') || '复制'; - }, 1400); - } catch (error) { - button.textContent = value; - } - }); - }); +Create a calm engineering-manual style: - const rows = Array.from(document.querySelectorAll('[data-symptom]')); - document.querySelectorAll('[data-filter]').forEach((button) => { - button.addEventListener('click', () => { - const filter = button.getAttribute('data-filter'); - rows.forEach((row) => { - const symptom = row.getAttribute('data-symptom') || ''; - const match = symptom.includes(filter); - row.toggleAttribute('data-highlight', match); - if (match) { - row.scrollIntoView({ behavior: 'smooth', block: 'center' }); - } - }); - }); - }); -})(); -``` +1. Use CSS custom properties for `--bg`, `--surface`, `--text`, `--muted`, `--line`, `--accent`. +2. Avoid heavy card nesting. Sections should read like document bands; reserve bordered surfaces for diagrams, diagnostic rows, and source notes. +3. Use 8px border radius or less. +4. Use `letter-spacing: 0` or no letter spacing; no negative letter spacing. +5. Desktop: three-column layout. +6. Tablet/mobile: nav and quick triage appear before main content; content is one column; owner cards are readable without requiring table horizontal scroll. +7. Add visible focus states for links and buttons. +8. Add print styles that hide nav, quick triage, and copy buttons. -- [ ] **Step 2: Ensure copy buttons keep stable labels** +- [ ] **Step 3: Create `assets/app.js`** -In `index.html`, every copy button must have both `data-copy` and `data-label`, for example: +Implement local-only interactions: -```html - -``` +1. Active section highlighting for nav links. +2. Copy buttons for sample paths. +3. Quick triage filter buttons for all eight symptom groups. +4. A clear-filter button. +5. No `fetch`, `XMLHttpRequest`, `localStorage`, or `sessionStorage`. +6. Clipboard failure must degrade by showing the copied value in the button text. + +## Task 3: Validate the Explainer + +**Files:** +- Verify: `docs/explainers/muse-cloud/**` + +- [ ] **Step 1: Run structural validation** Run: @@ -1121,96 +216,71 @@ Run: node <<'NODE' const fs = require('fs'); const html = fs.readFileSync('docs/explainers/muse-cloud/index.html', 'utf8'); -const copyButtons = html.match(/data-copy="/g) || []; -const labels = html.match(/data-label="/g) || []; -if (copyButtons.length === 0) throw new Error('no copy buttons found'); -if (copyButtons.length !== labels.length) throw new Error('each copy button needs data-label'); -NODE -``` - -Expected: command exits with status `0`. If it fails, patch `index.html` copy buttons before continuing. - -- [ ] **Step 3: Verify JavaScript stays local-only** - -Run: - -```bash -node <<'NODE' -const fs = require('fs'); +const css = fs.readFileSync('docs/explainers/muse-cloud/assets/styles.css', 'utf8'); const js = fs.readFileSync('docs/explainers/muse-cloud/assets/app.js', 'utf8'); -for (const forbidden of ['fetch(', 'XMLHttpRequest', 'localStorage', 'sessionStorage']) { - if (js.includes(forbidden)) throw new Error(`forbidden browser API found: ${forbidden}`); -} -NODE -``` -Expected: command exits with status `0`. - -- [ ] **Step 4: Commit Task 5** - -Run: - -```bash -git add docs/explainers/muse-cloud/assets/app.js docs/explainers/muse-cloud/index.html -git commit -m "docs(explainer): add muse-cloud guide interactions" -``` - -Expected: commit succeeds and only `assets/app.js` plus any required `index.html` label fix are included. - -## Task 6: Browser Verification and Final Documentation Checks - -**Files:** -- Verify: `docs/explainers/muse-cloud/index.html` -- Verify: `docs/explainers/muse-cloud/assets/styles.css` -- Verify: `docs/explainers/muse-cloud/assets/app.js` -- Verify: `docs/explainers/muse-cloud/content/*.md` - -- [ ] **Step 1: Run static structure checks** - -Run: - -```bash -node <<'NODE' -const fs = require('fs'); -const base = 'docs/explainers/muse-cloud'; -const required = [ +const requiredFiles = [ 'README.md', - 'index.html', - 'assets/styles.css', - 'assets/app.js', 'content/architecture.md', 'content/openapi-paths.md', 'content/request-lifecycle.md', 'content/domain-owners.md', 'content/p1r-stages.md', - 'content/troubleshooting.md' + 'content/troubleshooting.md', + 'content/sources.md', + 'assets/styles.css', + 'assets/app.js', + 'index.html', ]; -for (const file of required) { - if (!fs.existsSync(`${base}/${file}`)) throw new Error(`missing ${file}`); +for (const file of requiredFiles) { + if (!fs.existsSync(`docs/explainers/muse-cloud/${file}`)) throw new Error(`missing ${file}`); } -const html = fs.readFileSync(`${base}/index.html`, 'utf8'); -for (const token of [ - 'muse-cloud 架构讲解', - 'X-API-Version: 1', - 'code/data/msg', - '/app-api/muse', - '/admin-api/muse', - 'Content', - 'Meta', - 'Account', - 'AI', - 'Knowledge', - 'Market', - 'Events' -]) { - if (!html.includes(token)) throw new Error(`missing html token ${token}`); + +for (const token of ['TBD', 'TODO', '待定']) { + if (html.includes(token) || css.includes(token) || js.includes(token)) throw new Error(`placeholder ${token}`); } + +if (html.includes('prefix + resource') || html.includes('side prefix + resource') || html.includes('路径由三段组成')) { + throw new Error('old OpenAPI path composition phrasing found'); +} +for (const token of ['/app-api/muse/works', 'listWorks', 'AppContentController', 'ContentAppService', 'X-API-Version: 1', 'code/data/msg']) { + if (!html.includes(token)) throw new Error(`missing ${token}`); +} + +const count = (pattern) => (html.match(pattern) || []).length; +if (count(/class="[^"]*architecture-layer/g) < 7) throw new Error('architecture layers missing'); +if (count(/class="[^"]*lifecycle-node/g) < 8) throw new Error('lifecycle nodes missing'); +if (count(/class="[^"]*stage-card/g) < 8) throw new Error('stage cards missing'); +if (count(/class="[^"]*diagnostic-row/g) < 8) throw new Error('diagnostic rows missing'); + +for (const token of ['负责什么', '不应负责什么', '定位证据', '常见问题', '下一步', '负责领域', '真实能力', '假完成形态', '验收证据', '优先定位层', '检查项', '证据']) { + if (!html.includes(token)) throw new Error(`missing structural label ${token}`); +} + +for (const forbidden of ['fetch(', 'XMLHttpRequest', 'localStorage', 'sessionStorage']) { + if (js.includes(forbidden)) throw new Error(`forbidden browser API ${forbidden}`); +} +if (/letter-spacing:\s*-[0-9.]/.test(css)) throw new Error('negative letter spacing'); +if (/border-radius:\s*(1[0-9]|[2-9][0-9])px/.test(css)) throw new Error('radius above 8px'); NODE ``` Expected: command exits with status `0`. -- [ ] **Step 2: Run repository whitespace checks for the explainer files** +- [ ] **Step 2: Verify examples against real source** + +Run: + +```bash +rg -n "^ /app-api/muse/works:" docs/api-contracts/content/openapi.yaml +rg -n "operationId: listWorks" docs/api-contracts/content/openapi.yaml +rg -n "class AppContentController|interface ContentAppService|class ContentAppServiceImpl" \ + muse-cloud/muse-module-content/muse-module-content-server/src/main/java +``` + +Expected: all commands find matches. + +- [ ] **Step 3: Run whitespace checks** Run: @@ -1220,61 +290,100 @@ git diff --check -- docs/explainers/muse-cloud Expected: no output and exit status `0`. -- [ ] **Step 3: Serve the static page locally** +- [ ] **Step 4: Verify direct file opening path** Run: ```bash -python3 -m http.server 4173 --directory docs/explainers/muse-cloud +node <<'NODE' +const path = require('path'); +const htmlPath = path.resolve('docs/explainers/muse-cloud/index.html'); +console.log(`file://${htmlPath}`); +NODE ``` -Expected: server prints `Serving HTTP on :: port 4173` or `Serving HTTP on 0.0.0.0 port 4173`. Keep it running for the next step, then stop it with `Ctrl+C`. +Open the printed `file://` URL in a browser. Expected: CSS and JS load because they use relative paths; page is readable without a local server. -- [ ] **Step 4: Verify in a browser** +- [ ] **Step 5: Start a non-blocking local server** -Open: - -```text -http://localhost:4173/index.html -``` - -Check: - -1. Desktop width: left nav, main content, and right quick panel are visible without overlap. -2. Mobile width: layout collapses to one column, text does not overflow. -3. Copy buttons update to `已复制` or reveal the copied value when clipboard permission is unavailable. -4. Quick diagnostic buttons scroll to or highlight matching troubleshooting rows. -5. The page clearly states catch-all and generic persistence are not P1R completion. -6. The page reads like an engineering manual, not a marketing landing page. - -- [ ] **Step 5: Capture screenshot evidence if browser tooling is available** - -If Playwright is available in the environment, run: +Run: ```bash -node <<'NODE' +PORT=4173 +while lsof -nP -iTCP:$PORT -sTCP:LISTEN >/dev/null 2>&1; do + PORT=$((PORT + 1)) +done +python3 -m http.server "$PORT" --directory docs/explainers/muse-cloud > /tmp/muse-cloud-explainer-http.log 2>&1 & +SERVER_PID=$! +echo "$SERVER_PID" > /tmp/muse-cloud-explainer-http.pid +echo "http://localhost:$PORT/index.html" +``` + +Expected: command prints a URL and returns to the shell. Keep the server running until browser and screenshot checks finish. + +- [ ] **Step 6: Browser check** + +Open the printed URL. Check: + +1. Desktop: nav, main content, and quick triage do not overlap. +2. Mobile: quick triage appears before main content and remains usable. +3. Owner boundaries are readable on mobile without relying only on a wide table. +4. Quick triage filters and clear filter work. +5. Copy buttons either copy or reveal the copied value. +6. The page reads like an engineering manual, not a marketing landing page. + +- [ ] **Step 7: Screenshot automation or explicit fallback** + +Run preflight: + +```bash +node -e "require.resolve('playwright')" +``` + +If it succeeds, run screenshots against the local server URL: + +```bash +URL="$(grep -o 'http://localhost:[0-9]*/index.html' /tmp/muse-cloud-explainer-http.log 2>/dev/null || true)" +if [ -z "$URL" ]; then + PORT="$(lsof -nP -iTCP -sTCP:LISTEN | awk '/Python/ && /LISTEN/ {print $9}' | sed -n 's/.*://p' | tail -1)" + URL="http://localhost:${PORT}/index.html" +fi +node < { const browser = await chromium.launch(); const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } }); - await page.goto('http://localhost:4173/index.html'); + await page.goto('$URL'); await page.screenshot({ path: 'docs/explainers/muse-cloud/desktop-check.png', fullPage: true }); await page.setViewportSize({ width: 390, height: 900 }); await page.screenshot({ path: 'docs/explainers/muse-cloud/mobile-check.png', fullPage: true }); await browser.close(); })(); NODE -``` - -Expected: screenshots are generated. Review them, then remove them before commit: - -```bash rm -f docs/explainers/muse-cloud/desktop-check.png docs/explainers/muse-cloud/mobile-check.png ``` -If Playwright is not available, do not install dependencies just for this explainer. Use the available browser or visual MCP tooling and note that screenshot automation was skipped. +If preflight fails, do not install dependencies. Use the available browser or visual MCP tooling and record in the final summary that Playwright screenshot automation was skipped because `require.resolve('playwright')` failed. -- [ ] **Step 6: Final status check** +- [ ] **Step 8: Stop local server** + +Run: + +```bash +if [ -f /tmp/muse-cloud-explainer-http.pid ]; then + kill "$(cat /tmp/muse-cloud-explainer-http.pid)" 2>/dev/null || true + rm -f /tmp/muse-cloud-explainer-http.pid +fi +``` + +Expected: local server stops. + +## Task 4: Final Review and Commit + +**Files:** +- Commit only: `docs/explainers/muse-cloud/**` + +- [ ] **Step 1: Inspect status** Run: @@ -1282,29 +391,49 @@ Run: git status --short ``` -Expected: only `docs/explainers/muse-cloud/**` changes from this implementation are staged or unstaged. Unrelated files such as `.superpowers/`, `P1-execution-prompt.md`, and `P3-execution-prompt.md` must remain uncommitted. +Expected: unrelated files may still exist, but only `docs/explainers/muse-cloud/**` should be part of this implementation. -- [ ] **Step 7: Final commit** +- [ ] **Step 2: Stage only explainer files** Run: ```bash -git add docs/explainers/muse-cloud -git commit -m "docs(explainer): complete muse-cloud architecture guide" +git add -- docs/explainers/muse-cloud +git diff --cached --name-only +``` + +Expected: every staged file path starts with `docs/explainers/muse-cloud/`. + +- [ ] **Step 3: Enforce staged whitelist** + +Run: + +```bash +git diff --cached --name-only | awk ' + !/^docs\/explainers\/muse-cloud\// { print "unexpected staged file: " $0; bad=1 } + END { exit bad } +' +git diff --cached --check -- docs/explainers/muse-cloud +``` + +Expected: both commands exit with status `0`. + +- [ ] **Step 4: Commit** + +Run: + +```bash +git commit -m "docs(explainer): 完成 muse-cloud 架构讲解包" -- docs/explainers/muse-cloud ``` Expected: commit succeeds and includes only `docs/explainers/muse-cloud/**`. -## Self-Review Checklist +## Completion Criteria -Before marking implementation complete: - -- [ ] `index.html` is complete enough to read without opening Markdown. -- [ ] Markdown files are lightweight source notes, not the primary reading path. -- [ ] The page explains cloud architecture, OpenAPI path composition, request lifecycle, domain owner boundaries, P1R stage map, and troubleshooting. -- [ ] The page explicitly says catch-all, generic persistence, placeholder SSE, empty lists, and accepted task responses are not P1R completion. -- [ ] The page uses `code/data/msg`, not `message`, for response explanation. -- [ ] The page includes `/app-api/muse/**`, `/admin-api/muse/**`, and `X-API-Version: 1`. -- [ ] The page does not request real backend APIs or external service credentials. -- [ ] No files under `muse-cloud/`, `muse-admin/`, `muse-studio/`, or `docs/api-contracts/**` were modified. -- [ ] Final `git status --short` is reviewed before every commit. +- `index.html` is complete enough to read without opening Markdown. +- OpenAPI section says contract `paths` are full external paths. +- The page includes all required architecture, lifecycle, stage, and troubleshooting structures. +- The page includes `/app-api/muse/**`, `/admin-api/muse/**`, `X-API-Version: 1`, and `code/data/msg`. +- The page does not request backend APIs or external credentials. +- Direct `file://` open path and local server path are both checked. +- No files under `muse-cloud/`, `muse-admin/`, `muse-studio/`, `docs/api-contracts/**`, `.superpowers/**`, `docs/superpowers/reports/**`, or P1/P3 prompt files were modified or committed.