docs: P6 hosted submission (plan A, self-hosted embed) spec + plan + roadmap status

This commit is contained in:
2026-09-30 22:53:53 +08:00
parent 36286b8a25
commit d22672a1e4
3 changed files with 123 additions and 1 deletions
@@ -0,0 +1,49 @@
# P6 hosted 投稿(方案 A)· 实施计划
依据:`docs/specs/2026-09-30-hosted-submission-design.md`(D-A…D-F 定稿)。分支名统一 `feat/p6-hosted`,各仓各自实现者自拉自提交,**不 push**;合主由控制者 `--no-ff`。
## Task 1 · server 校验收紧(crearte-server)
基线 master(`ca24805` 起)。改动点:
1. `src/internal/service/content.go` `ValidateSubmission`(约 :327 起的 switch):
- `metadata_change` 分支:取现有 work 后新增不可变校验——`RuntimeOrDefault(p) != work.Runtime` → `ValidationError{Fields: []string{"runtime: immutable on metadata_change"}}`(D-B)。
- 通用(new_work 与 metadata_change 都过):`runtime == hosted && bundleUploadID != ""` → `ValidationError{Fields: []string{"bundle_upload_id: hosted works cannot carry a bundle"}}`(D-C)。
- `new_version` 分支已 virtual-only,不动。
2. 单测(同文件所在包 service):
- hosted+bundle → 校验失败且字段名精确;
- metadata_change 改 runtime(hosted↔external/virtual 双向至少各一)→ 失败;runtime 相同 → 通过既有路径;
- 回归确认:hosted new_work 无 bundle、带合法 https hostedUrl 仍通过(validate.go:140 既有)。
3. 集成测试(带 `TEST_DATABASE_URL` 的既有风格,参考 admin 面集成六场景文件):hosted 草稿→submit-review→approve→`works` 行 `hosted_url/play_origin/fallback` 读回一致;`GET /api/games/<user/slug>` 载荷含 `runtime:"hosted"` 与 `hostedUrl`。
4. `CHANGELOG.md` 中英成对,版本 **0.13.0**(Added=校验护栏+集成覆盖;Changed 若有措辞按实际)。
红线:零迁移、零新依赖、不动 handler 响应形状、不动 audit/admin 面、Approve 落库路径(PayloadToWork 映射已全,见 spec §1)不改。
验证(容器内,goproxy.cn 姿势同 AGENTS.md):`gofmt -l` 空、`go vet ./...`、`go build ./...`、`go test -p 1 ./internal/...`;带 db 的集成腿控制者跑,实现者确保无 `TEST_DATABASE_URL` 时 skip 语义与既有一致。
## Task 2 · crearte 前端表单打通
基线 master(`900f13a` 起),源码根 `crearte/src`。改动点:
1. 内容层类型:提交 payload runtime 联合扩为三档,`hostedUrl`、`fallback` 字段落位(对齐 server `WorkPayload` json 名 `hostedUrl`/`fallback`/`runtime`)。
2. `app/views/SubmitFormView.vue`:
- `:33` 表单 runtime 加 `'hosted'`;`:408-415` radio 组加第三项「自托管内嵌」(`data-testid="runtime-hosted"`)。
- hosted 档字段:`hostedUrl`(必填 https)、`fallback` 选择(`external|hosted|none`,默认 `external`);fallback=external 时 `url` 必填(前端校验镜像 server)。
- 提交形状:hosted 时 payload 带 `runtime:'hosted'` + `hostedUrl` + `fallback`,**不带** bundle/version/entry/features;`kind` 仅 new_work / metadata_change(new_version radio 保持 virtual-only 逻辑)。
- 删除 `:144-147` hosted 预填拒绝;预填回填 hostedUrl/fallback(注意 server GET 载荷字段名)。
- hosted 档下 cover 上传照常。
3. vitest(SubmitFormView.test.ts 等):hosted 提交形状、必填校验失败面、预填回填、new_version 禁入 hosted。
4. e2e:`admin-flow.spec.ts` 或提交流既有夹具内加 hosted 提交流(表单→draft payload 断言);**不动** `full-loop.spec.ts` / `playwright.stack.config.ts`。
5. `docs/CHANGELOG.md` 中英成对,版本 **0.19.0**。
红线:不动运行时三件套(sw/agent/bootstrap/GameHost/useGameFrame——播放端零改动是 spec 决策);不动管理面;apiRepo 无新端点。
验证(`crearte/src`):`npx vitest run`、`npm run typecheck`、`npx playwright test e2e/admin-flow.spec.ts`、`npm run e2e` 全 RC=0。
## Task 3 · deploy 核证(控制者,非实现者任务)
生产/dev 主站响应头无 `frame-src` 限制即记录在案;若有则模板补丁+复验。
## 合主与验收(控制者)
双路独立审查(spec→代码逐条,PASS with notes 起)→ 六腿验收(spec §5)→ `--no-ff` 合主推送 → wrapper 记账(ROADMAP P6 行 + spec/plan 索引行 + CHANGELOG 0.2.7)→ 删分支。