diff --git a/.gitignore b/.gitignore index d55c5d2..194fc68 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,5 @@ var/ web/vite.config.js web/vite.config.d.ts *.log +.playwright-mcp/ +register-create.png diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..acdda56 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,23 @@ +# Changelog + +ContentCloud 的重要变更记录在此文件中。 + +## [0.2.0] - 2026-07-26 + +### Added + +- 增加 V2 本地工作区能力,覆盖来源登记、知识治理、Brief、创意批次、ScriptPackage、LocalRun、发布与拉取流程。 +- 增加 V2 JSON Schema、CLI/MCP 命令、XLSX 导出能力及完整回归测试。 +- 增加邀请注册流程,受邀用户可直接加入邀请方租户。 +- 增加无 Docker 的 systemd 部署配置,支持独立运行 Server 与 Worker。 + +### Changed + +- 更新 V2 产品路线、业务能力、领域模型、交付计划和实现状态文档。 +- 统一 CLI 与 npm 工作区版本为 `0.2.0`,GitHub 发布标签为 `v0.2`。 + +### Fixed + +- 移除核心迁移对 `pgcrypto` 扩展的无效依赖。 +- 将邀请接受和邀请注册改为原子存储操作,避免并发重复兑换和部分写入。 +- 加强本地工作区路径校验,阻止通过符号链接读取工作区外文件。 diff --git a/IMPLEMENTATION_PLAN.md b/IMPLEMENTATION_PLAN.md new file mode 100644 index 0000000..6ae7069 --- /dev/null +++ b/IMPLEMENTATION_PLAN.md @@ -0,0 +1,34 @@ +# V2 单轨审批收敛实施计划 + +对应 `docs/roadmap/v2/03-domain-and-data-model.md` §2.1/§2.2 与 `14-implementation-status.md` 的 P0 缺口。 + +目标:把客户审批、导出、交付和结果绑定从 V1 `script_version` 收敛到 `SubmissionRevision` / `ApprovedSnapshot`,并补上 Brief 的策略血缘。 + +## Stage 1: Brief 策略血缘 +**Goal**: `contracts/brief-2.0.schema.json` 已要求 `strategy_version_id`,让本地 Brief lint 与之一致。 +**Success Criteria**: 缺少 `strategy_version_id` 的 Brief 被 lint 拒绝并给出稳定错误码。 +**Tests**: `internal/localworkspace/script_test.go` 增加缺字段用例;既有用例补字段后仍通过。 +**Status**: Complete +**Notes**: `LocalBrief.StrategyVersionID` 加入必填校验;新增 `BRIEF_STRATEGY_NOT_APPROVED` 校验其落在已 pull 的 strategy ApprovedSnapshot;新增 `domain.IsNotFound` helper 用于区分"缺对象"与真实故障。 + +## Stage 2: 客户审批改挂 SubmissionRevision +**Goal**: ReviewGrant 绑定具体 SubmissionRevision,内部/客户两阶段决定写入同一 revision,客户批准后生成 ApprovedSnapshot。 +**Success Criteria**: +- `ApprovalDecision.DecisionStage` 区分 `internal` / `client` +- 客户批准前必须已有同一 revision 的 internal 批准 +- 新 revision 出现后旧 grant 自动失效 +- 客户批准生成 ApprovedSnapshot,hash 等于 revision content_hash +**Tests**: internal→client 正常路径、跳过 internal 被拒、grant 失效、OTP 错误、重复决定。 +**Status**: Not Started + +## Stage 3: 导出改由 ApprovedSnapshot 驱动 +**Goal**: JSON/Markdown/XLSX 从批准快照的 canonical 内容生成,不再要求 V1 ScriptVersion。 +**Success Criteria**: 三种格式由同一快照生成,manifest 记录 snapshot ID 与 revision hash。 +**Tests**: 三格式导出内容一致性、未批准快照拒绝导出。 +**Status**: Not Started + +## Stage 4: 交付、结果与影子快照回填 +**Goal**: DeliveryPackage 引用 ApprovedSnapshot;PerformanceObservation 绑定快照;V1 已批准 ScriptVersion 回填 `origin=v1_import` 只读影子快照。 +**Success Criteria**: 回填后历史导出内容与 hash 不变;dry-run 报告数量与不可映射项。 +**Tests**: 回填幂等、hash 不变、跨租户负测。 +**Status**: Not Started diff --git a/README.md b/README.md index d29311e..68b5073 100644 --- a/README.md +++ b/README.md @@ -80,12 +80,14 @@ export CONTENTCLOUD_S3_SECRET_ACCESS_KEY='...' ./bin/contentcloud-server ``` -Worker 使用相同数据库与对象存储配置,并要求 ClamAV: +Worker 使用相同数据库与对象存储配置。v0.2 默认不安装或调用 ClamAV;开放不可信文件上传前,可显式启用恶意文件扫描: ```bash CONTENTCLOUD_REQUIRE_MALWARE_SCAN=1 ./bin/contentcloud-worker ``` +生产环境可按 [systemd 部署说明](deploy/systemd/README.md) 在无 Docker 模式下运行 Server 与 Worker,并由 Nginx 反向代理到 Server 的本地监听地址。 + 可用 `docker compose up --build` 启动 Server、Worker、PostgreSQL 和 MinIO 的完整本地拓扑。Compose 中的凭据只用于本机开发。 ## 首次项目连接 diff --git a/VERSION b/VERSION new file mode 100644 index 0000000..0ea3a94 --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +0.2.0 diff --git a/contracts/brief-2.0.schema.json b/contracts/brief-2.0.schema.json new file mode 100644 index 0000000..4b21325 --- /dev/null +++ b/contracts/brief-2.0.schema.json @@ -0,0 +1,54 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://contentcloud.goodvision.cn/schemas/brief-2.0.schema.json", + "title": "ContentCloud Marketing Video Brief 2.0", + "type": "object", + "additionalProperties": false, + "required": ["id", "kind", "status", "schema_version", "deliverability", "strategy_version_id", "campaign_id", "experiment_id", "channel", "objective", "audience", "scenario", "demand_moment", "pain_point", "primary_selling_point", "support_points", "positioning", "visualization_plan_ids", "asset_ids", "truth_strategy", "plan_b", "tone", "brand_rule_ids", "approved_claim_ids", "forbidden_claims", "hook_expectation", "narrative_constraints", "cta", "primary_variable", "controlled_variables", "measurement_window", "eligible_knowledge_ids", "blocked_knowledge_ids", "rights_ids", "risk_decision_ids", "duration_min_ms", "duration_max_ms", "aspect_ratio", "blocked_reasons", "missing_inputs"], + "properties": { + "id": {"type": "string", "minLength": 1}, + "kind": {"const": "brief"}, + "status": {"enum": ["candidate", "blocked"]}, + "schema_version": {"const": "2.0"}, + "deliverability": {"enum": ["blocked", "review_ready"]}, + "strategy_version_id": {"type": "string", "minLength": 1}, + "campaign_id": {"type": "string", "minLength": 1}, + "experiment_id": {"type": "string", "minLength": 1}, + "channel": {"type": "string", "minLength": 1}, + "objective": {"type": "string", "minLength": 1}, + "audience": {"type": "string", "minLength": 1}, + "scenario": {"type": "string", "minLength": 1}, + "demand_moment": {"type": "string", "minLength": 1}, + "pain_point": {"type": "string", "minLength": 1}, + "primary_selling_point": {"type": "string", "minLength": 1}, + "support_points": {"type": "array", "maxItems": 3, "items": {"type": "string"}}, + "positioning": {"type": "string", "minLength": 1}, + "visualization_plan_ids": {"type": "array", "uniqueItems": true, "items": {"type": "string"}}, + "asset_ids": {"type": "array", "uniqueItems": true, "items": {"type": "string"}}, + "truth_strategy": {"type": "string", "minLength": 1}, + "plan_b": {"type": "string", "minLength": 1}, + "tone": {"type": "string", "minLength": 1}, + "brand_rule_ids": {"type": "array", "uniqueItems": true, "items": {"type": "string"}}, + "approved_claim_ids": {"type": "array", "uniqueItems": true, "items": {"type": "string"}}, + "forbidden_claims": {"type": "array", "uniqueItems": true, "items": {"type": "string"}}, + "hook_expectation": {"type": "string", "minLength": 1}, + "narrative_constraints": {"type": "array", "items": {"type": "string"}}, + "cta": {"type": "string", "minLength": 1}, + "primary_variable": {"enum": ["hook", "audience", "scenario", "visualization", "cta", "duration"]}, + "controlled_variables": {"type": "array", "uniqueItems": true, "items": {"type": "string"}}, + "measurement_window": {"type": "string", "minLength": 1}, + "eligible_knowledge_ids": {"type": "array", "uniqueItems": true, "items": {"type": "string"}}, + "blocked_knowledge_ids": {"type": "array", "uniqueItems": true, "items": {"type": "string"}}, + "rights_ids": {"type": "array", "uniqueItems": true, "items": {"type": "string"}}, + "risk_decision_ids": {"type": "array", "uniqueItems": true, "items": {"type": "string"}}, + "duration_min_ms": {"type": "integer", "minimum": 1000, "maximum": 600000}, + "duration_max_ms": {"type": "integer", "minimum": 1000, "maximum": 600000}, + "aspect_ratio": {"enum": ["9:16", "16:9", "1:1", "4:5"]}, + "blocked_reasons": {"type": "array", "items": {"type": "string"}}, + "missing_inputs": {"type": "array", "items": {"type": "string"}} + }, + "allOf": [ + {"if": {"properties": {"deliverability": {"const": "blocked"}}}, "then": {"properties": {"status": {"const": "blocked"}, "blocked_reasons": {"minItems": 1}}}}, + {"if": {"properties": {"deliverability": {"const": "review_ready"}}}, "then": {"properties": {"status": {"const": "candidate"}, "eligible_knowledge_ids": {"minItems": 1}, "visualization_plan_ids": {"minItems": 1}, "blocked_reasons": {"maxItems": 0}, "missing_inputs": {"maxItems": 0}}}} + ] +} diff --git a/contracts/creative-directions-2.0.schema.json b/contracts/creative-directions-2.0.schema.json new file mode 100644 index 0000000..c3a4831 --- /dev/null +++ b/contracts/creative-directions-2.0.schema.json @@ -0,0 +1,25 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://contentcloud.goodvision.cn/schemas/creative-directions-2.0.schema.json", + "title": "ContentCloud Creative Directions 2.0", + "type": "array", + "minItems": 1, + "maxItems": 20, + "items": { + "type": "object", + "additionalProperties": false, + "required": ["id", "title", "angle", "hook_type", "visual_motif", "narrative", "tone", "target_emotion", "risk_refs", "status"], + "properties": { + "id": {"type": "string", "minLength": 1}, + "title": {"type": "string", "minLength": 1}, + "angle": {"type": "string", "minLength": 1}, + "hook_type": {"type": "string", "minLength": 1}, + "visual_motif": {"type": "string", "minLength": 1}, + "narrative": {"type": "array", "minItems": 1, "items": {"type": "string", "minLength": 1}}, + "tone": {"type": "string"}, + "target_emotion": {"type": "string"}, + "risk_refs": {"type": "array", "uniqueItems": true, "items": {"type": "string"}}, + "status": {"enum": ["candidate", "selected", "rejected"]} + } + } +} diff --git a/contracts/embed.go b/contracts/embed.go index ab9caf9..dcabefb 100644 --- a/contracts/embed.go +++ b/contracts/embed.go @@ -5,6 +5,15 @@ import _ "embed" //go:embed script-package-1.1.schema.json var ScriptPackageSchema []byte +//go:embed script-package-2.0.schema.json +var ScriptPackageV2Schema []byte + +//go:embed brief-2.0.schema.json +var BriefV2Schema []byte + +//go:embed creative-directions-2.0.schema.json +var CreativeDirectionsV2Schema []byte + //go:embed task-contract-1.0.schema.json var TaskContractSchema []byte diff --git a/contracts/embed_test.go b/contracts/embed_test.go new file mode 100644 index 0000000..23fb782 --- /dev/null +++ b/contracts/embed_test.go @@ -0,0 +1,22 @@ +package contracts + +import ( + "encoding/json" + "testing" +) + +func TestEmbeddedSchemasAreValidJSON(t *testing.T) { + for name, body := range map[string][]byte{ + "knowledge-candidates-1.0": KnowledgeCandidatesSchema, + "brief-2.0": BriefV2Schema, + "creative-directions-2.0": CreativeDirectionsV2Schema, + "script-package-1.1": ScriptPackageSchema, + "script-package-2.0": ScriptPackageV2Schema, + "task-contract-1.0": TaskContractSchema, + } { + var schema map[string]any + if len(body) == 0 || json.Unmarshal(body, &schema) != nil || schema["$id"] == "" { + t.Fatalf("embedded schema %s is missing or invalid", name) + } + } +} diff --git a/contracts/script-package-2.0.schema.json b/contracts/script-package-2.0.schema.json new file mode 100644 index 0000000..4051b5b --- /dev/null +++ b/contracts/script-package-2.0.schema.json @@ -0,0 +1,108 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://contentcloud.goodvision.cn/schemas/script-package-2.0.schema.json", + "title": "ContentCloud Script Package 2.0", + "type": "object", + "additionalProperties": false, + "required": ["id", "kind", "status", "schema_version", "deliverability", "project_id", "script_id", "creative_batch_id", "brief_version_id", "context_snapshot_id", "direction", "title", "channel", "duration_ms", "aspect_ratio", "cover", "narrative_structure", "shots", "citations", "asset_requirements", "experiment", "global_constraints", "blocked_reasons", "missing_inputs", "validation_declarations"], + "properties": { + "id": {"type": "string", "minLength": 1}, + "kind": {"const": "script_package"}, + "status": {"enum": ["candidate", "blocked"]}, + "schema_version": {"const": "2.0"}, + "deliverability": {"enum": ["blocked", "review_ready"]}, + "project_id": {"type": "string", "minLength": 1}, + "script_id": {"type": "string", "minLength": 1}, + "creative_batch_id": {"type": "string", "minLength": 1}, + "brief_version_id": {"type": "string", "minLength": 1}, + "context_snapshot_id": {"type": "string", "minLength": 1}, + "based_on_version_id": {"type": "string"}, + "resolved_comment_ids": {"type": "array", "uniqueItems": true, "items": {"type": "string"}}, + "change_summary": {"type": "string"}, + "direction": {"$ref": "#/$defs/direction"}, + "title": {"type": "string", "minLength": 1, "maxLength": 200}, + "channel": {"type": "string", "minLength": 1}, + "duration_ms": {"type": "integer", "minimum": 1, "maximum": 600000}, + "aspect_ratio": {"enum": ["9:16", "16:9", "1:1", "4:5"]}, + "cover": {"$ref": "#/$defs/cover"}, + "narrative_structure": {"type": "array", "items": {"$ref": "#/$defs/narrative_segment"}}, + "shots": {"type": "array", "maxItems": 100, "items": {"$ref": "#/$defs/shot"}}, + "citations": {"type": "array", "items": {"$ref": "#/$defs/citation"}}, + "asset_requirements": {"type": "array", "items": {"$ref": "#/$defs/asset_requirement"}}, + "experiment": {"$ref": "#/$defs/experiment"}, + "global_constraints": {"$ref": "#/$defs/global_constraints"}, + "blocked_reasons": {"type": "array", "items": {"$ref": "#/$defs/block_reason"}}, + "missing_inputs": {"type": "array", "items": {"type": "string"}}, + "validation_declarations": {"$ref": "#/$defs/validation_declarations"} + }, + "allOf": [ + {"if": {"properties": {"deliverability": {"const": "blocked"}}}, "then": {"properties": {"status": {"const": "blocked"}, "blocked_reasons": {"minItems": 1}}}}, + {"if": {"properties": {"deliverability": {"const": "review_ready"}}}, "then": {"properties": {"status": {"const": "candidate"}, "shots": {"minItems": 1}, "blocked_reasons": {"maxItems": 0}, "missing_inputs": {"maxItems": 0}}}} + ], + "$defs": { + "direction": { + "type": "object", + "additionalProperties": false, + "required": ["id", "title", "angle", "hook_type", "visual_motif", "narrative", "tone", "target_emotion", "risk_refs", "status"], + "properties": { + "id": {"type": "string", "minLength": 1}, "title": {"type": "string", "minLength": 1}, "angle": {"type": "string", "minLength": 1}, "hook_type": {"type": "string", "minLength": 1}, "visual_motif": {"type": "string", "minLength": 1}, + "narrative": {"type": "array", "minItems": 1, "items": {"type": "string"}}, "tone": {"type": "string"}, "target_emotion": {"type": "string"}, "risk_refs": {"type": "array", "items": {"type": "string"}}, "status": {"enum": ["candidate", "selected", "rejected"]} + } + }, + "cover": { + "type": "object", "additionalProperties": false, + "required": ["title", "subtitle", "visual_intent", "first_view_signal", "asset_refs", "rights_refs", "safe_area", "occlusion_guards"], + "properties": {"title": {"type": "string"}, "subtitle": {"type": "string"}, "visual_intent": {"type": "string"}, "first_view_signal": {"type": "string"}, "asset_refs": {"type": "array", "items": {"type": "string"}}, "rights_refs": {"type": "array", "items": {"type": "string"}}, "safe_area": {"type": "string"}, "occlusion_guards": {"type": "array", "items": {"type": "string"}}} + }, + "narrative_segment": { + "type": "object", "additionalProperties": false, + "required": ["role", "purpose", "start_ms", "end_ms", "decision_function", "shot_ids"], + "properties": {"role": {"type": "string"}, "purpose": {"type": "string"}, "start_ms": {"type": "integer", "minimum": 0}, "end_ms": {"type": "integer", "minimum": 1}, "decision_function": {"type": "string"}, "shot_ids": {"type": "array", "items": {"type": "string"}}} + }, + "frame": { + "type": "object", "additionalProperties": false, "required": ["visual_state", "prompt_zh", "asset_refs"], + "properties": {"visual_state": {"type": "string"}, "prompt_zh": {"type": "string"}, "asset_refs": {"type": "array", "items": {"type": "string"}}} + }, + "continuity": { + "type": "object", "additionalProperties": false, "required": ["incoming_state", "outgoing_state", "movement_axis", "lighting_lock", "product_lock", "anchors"], + "properties": {"incoming_state": {"type": "string"}, "outgoing_state": {"type": "string"}, "movement_axis": {"type": "string"}, "lighting_lock": {"type": "string"}, "product_lock": {"type": "string"}, "anchors": {"type": "array", "items": {"type": "string"}}} + }, + "shot": { + "type": "object", "additionalProperties": false, + "required": ["shot_id", "start_ms", "end_ms", "role", "narrative_purpose", "subject", "visual_intent", "subject_action", "composition", "camera_motion", "first_frame", "motion_spec", "end_frame", "voiceover", "on_screen_text", "sound_intent", "production_mode", "knowledge_refs", "claim_refs", "asset_refs", "rights_refs", "product_truth_strategy", "negative_constraints", "continuity", "acceptance_criteria", "plan_b"], + "properties": { + "shot_id": {"type": "string", "pattern": "^[A-Za-z0-9:_-]+$"}, "start_ms": {"type": "integer", "minimum": 0}, "end_ms": {"type": "integer", "minimum": 1}, + "role": {"enum": ["hook", "context", "pain", "product_intro", "product_solution", "usage", "proof", "resolution", "payoff", "cta"]}, + "narrative_purpose": {"type": "string"}, "subject": {"type": "string"}, "visual_intent": {"type": "string"}, "subject_action": {"type": "string"}, "composition": {"type": "string"}, "camera_motion": {"type": "string"}, + "first_frame": {"$ref": "#/$defs/frame"}, "motion_spec": {"type": "string"}, "end_frame": {"$ref": "#/$defs/frame"}, "voiceover": {"type": "string"}, "on_screen_text": {"type": "string"}, "sound_intent": {"type": "string"}, + "production_mode": {"enum": ["real_asset", "asset_guided_generation", "generated_non_product", "composite", "external_capture"]}, + "knowledge_refs": {"type": "array", "items": {"type": "string"}}, "claim_refs": {"type": "array", "items": {"type": "string"}}, "asset_refs": {"type": "array", "items": {"type": "string"}}, "rights_refs": {"type": "array", "items": {"type": "string"}}, + "visualization_plan_id": {"type": "string"}, "product_truth_strategy": {"type": "string"}, "negative_constraints": {"type": "array", "minItems": 1, "items": {"type": "string"}}, "continuity": {"$ref": "#/$defs/continuity"}, "acceptance_criteria": {"type": "array", "minItems": 1, "items": {"type": "string"}}, "plan_b": {"type": "string"} + } + }, + "citation": { + "type": "object", "additionalProperties": false, "required": ["knowledge_id", "shot_id", "usage"], + "properties": {"knowledge_id": {"type": "string"}, "shot_id": {"type": "string"}, "usage": {"enum": ["spoken_claim", "on_screen_text", "visual_fact", "style_rule"]}} + }, + "asset_requirement": { + "type": "object", "additionalProperties": false, "required": ["asset_id", "rights_id", "purpose", "required_truth", "fallback"], + "properties": {"asset_id": {"type": "string"}, "rights_id": {"type": "string"}, "purpose": {"type": "string"}, "required_truth": {"type": "string"}, "fallback": {"type": "string"}} + }, + "experiment": { + "type": "object", "additionalProperties": false, "required": ["primary_variable", "controlled_variables", "hypothesis", "measurement_window", "target_metrics"], + "properties": {"primary_variable": {"enum": ["hook", "audience", "scenario", "visualization", "cta", "duration"]}, "controlled_variables": {"type": "array", "uniqueItems": true, "items": {"type": "string"}}, "hypothesis": {"type": "string"}, "measurement_window": {"type": "string"}, "target_metrics": {"type": "array", "items": {"type": "string"}}} + }, + "global_constraints": { + "type": "object", "additionalProperties": false, "required": ["forbidden_claims", "brand_rules", "product_truth_rules", "continuity_locks", "platform_safe_area_rules"], + "properties": {"forbidden_claims": {"type": "array", "items": {"type": "string"}}, "brand_rules": {"type": "array", "items": {"type": "string"}}, "product_truth_rules": {"type": "array", "items": {"type": "string"}}, "continuity_locks": {"type": "array", "items": {"type": "string"}}, "platform_safe_area_rules": {"type": "array", "items": {"type": "string"}}} + }, + "block_reason": { + "type": "object", "additionalProperties": false, "required": ["code", "message", "owner_role", "next_action"], + "properties": {"code": {"type": "string"}, "object_id": {"type": "string"}, "message": {"type": "string"}, "owner_role": {"type": "string"}, "next_action": {"type": "string"}} + }, + "validation_declarations": { + "type": "object", "additionalProperties": false, "required": ["schema_checked", "knowledge_checked", "rights_checked", "continuity_checked", "experiment_checked"], + "properties": {"schema_checked": {"type": "boolean"}, "knowledge_checked": {"type": "boolean"}, "rights_checked": {"type": "boolean"}, "continuity_checked": {"type": "boolean"}, "experiment_checked": {"type": "boolean"}} + } + } +} diff --git a/deploy/systemd/README.md b/deploy/systemd/README.md new file mode 100644 index 0000000..7fd688f --- /dev/null +++ b/deploy/systemd/README.md @@ -0,0 +1,36 @@ +# ContentCloud systemd 部署 + +该目录用于无 Docker 的 Linux 部署,目录和服务管理方式与 `sub2api-admin-plus` 保持一致: + +```text +/opt/contentcloud/releases// 版本化发布目录 +/opt/contentcloud/current 当前版本软链接 +/etc/contentcloud/contentcloud.env root-only 环境配置 +/var/lib/contentcloud 本地对象数据 +contentcloud-server.service Web/API 服务 +contentcloud-worker.service 确定性 Worker +``` + +Server 只监听 `127.0.0.1:18082`,生产流量由宝塔原生反向代理接入。PostgreSQL 不对公网开放,Server 与 Worker 使用同一个独立数据库账号。 + +Worker 当前仅需要以下 OCR 系统依赖: + +```bash +apt-get install tesseract-ocr tesseract-ocr-chi-sim tesseract-ocr-eng +``` + +默认配置不安装 ClamAV,并设置 `CONTENTCLOUD_REQUIRE_MALWARE_SCAN=0`。开放不可信文件上传前,应另行评估并启用恶意文件扫描。 + +安装发布产物后: + +```bash +install -d -o contentcloud -g contentcloud -m 0750 /var/lib/contentcloud +install -d -o root -g contentcloud -m 0750 /etc/contentcloud +install -m 0644 deploy/systemd/contentcloud-server.service /etc/systemd/system/ +install -m 0644 deploy/systemd/contentcloud-worker.service /etc/systemd/system/ +install -m 0640 deploy/systemd/contentcloud.env.example /etc/contentcloud/contentcloud.env +systemctl daemon-reload +systemctl enable --now contentcloud-server contentcloud-worker +``` + +上线前必须替换数据库密码,并确认 `/etc/contentcloud/contentcloud.env` 不对其他用户可读。 diff --git a/deploy/systemd/contentcloud-server.service b/deploy/systemd/contentcloud-server.service new file mode 100644 index 0000000..2562c6b --- /dev/null +++ b/deploy/systemd/contentcloud-server.service @@ -0,0 +1,38 @@ +[Unit] +Description=ContentCloud Server +Documentation=https://github.com/limecloud/contentcloud +Wants=network-online.target +After=network-online.target + +[Service] +Type=simple +User=contentcloud +Group=contentcloud +WorkingDirectory=/opt/contentcloud/current +EnvironmentFile=/etc/contentcloud/contentcloud.env +ExecStart=/opt/contentcloud/current/bin/contentcloud-server +Restart=always +RestartSec=5s +TimeoutStopSec=15s +UMask=0077 +StandardOutput=journal +StandardError=journal +SyslogIdentifier=contentcloud-server + +NoNewPrivileges=true +ProtectSystem=strict +ProtectHome=true +PrivateTmp=true +PrivateDevices=true +ProtectKernelTunables=true +ProtectKernelModules=true +ProtectControlGroups=true +RestrictRealtime=true +LockPersonality=true +RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 +ReadWritePaths=/var/lib/contentcloud +MemoryMax=768M +TasksMax=256 + +[Install] +WantedBy=multi-user.target diff --git a/deploy/systemd/contentcloud-worker.service b/deploy/systemd/contentcloud-worker.service new file mode 100644 index 0000000..0b10798 --- /dev/null +++ b/deploy/systemd/contentcloud-worker.service @@ -0,0 +1,38 @@ +[Unit] +Description=ContentCloud Deterministic Worker +Documentation=https://github.com/limecloud/contentcloud +Wants=network-online.target +After=network-online.target contentcloud-server.service + +[Service] +Type=simple +User=contentcloud +Group=contentcloud +WorkingDirectory=/opt/contentcloud/current +EnvironmentFile=/etc/contentcloud/contentcloud.env +ExecStart=/opt/contentcloud/current/bin/contentcloud-worker +Restart=always +RestartSec=5s +TimeoutStopSec=15s +UMask=0077 +StandardOutput=journal +StandardError=journal +SyslogIdentifier=contentcloud-worker + +NoNewPrivileges=true +ProtectSystem=strict +ProtectHome=true +PrivateTmp=true +PrivateDevices=true +ProtectKernelTunables=true +ProtectKernelModules=true +ProtectControlGroups=true +RestrictRealtime=true +LockPersonality=true +RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6 +ReadWritePaths=/var/lib/contentcloud +MemoryMax=1G +TasksMax=128 + +[Install] +WantedBy=multi-user.target diff --git a/deploy/systemd/contentcloud.env.example b/deploy/systemd/contentcloud.env.example new file mode 100644 index 0000000..866c97a --- /dev/null +++ b/deploy/systemd/contentcloud.env.example @@ -0,0 +1,7 @@ +CONTENTCLOUD_DATABASE_URL=postgres://contentcloud:change_me@127.0.0.1:5432/contentcloud?sslmode=disable +CONTENTCLOUD_DATA_DIR=/var/lib/contentcloud +CONTENTCLOUD_ADDR=127.0.0.1:18082 +CONTENTCLOUD_WEB_DIST=/opt/contentcloud/current/web +CONTENTCLOUD_DEV_MODE=0 +CONTENTCLOUD_AUTO_MIGRATE=1 +CONTENTCLOUD_REQUIRE_MALWARE_SCAN=0 diff --git a/docs/roadmap/v2/01-prd.md b/docs/roadmap/v2/01-prd.md index f5e785c..e61d24e 100644 --- a/docs/roadmap/v2/01-prd.md +++ b/docs/roadmap/v2/01-prd.md @@ -62,7 +62,7 @@ Agent、Daemon 和 Worker 是工具或系统参与者,不进入业务责任矩 ## 5. 核心业务规则 1. 业务对象版本与 Agent Run 使用独立状态机。 -2. 任何审批必须绑定不可变对象版本和内容哈希,不能审批“最新版本”。 +2. 任何审批必须绑定不可变 SubmissionRevision 和其内容哈希,不能审批”最新版本”;云端不存在第二条平行审批轨道。 3. 正式内容只能使用 verified Fact、approved Claim、valid Rights 和允许等级的 Asset。 4. 候选知识不足时可以生成 CreativeDraft,但必须 `publishable=false` 并列明阻断原因。 5. 正式剧本必须先有卖点可视化方案,再生成镜头和话术。 @@ -105,6 +105,7 @@ Agent、Daemon 和 Worker 是工具或系统参与者,不进入业务责任矩 - 管理 Audience、Scenario、PainPoint、DemandMoment、SellingPoint 和 VisualizationPlan。 - 卖点排序必须记录目标人群、适用场景、支持知识和风险。 - VisualizationPlan 包含主体、场景、道具、实施方式、真实性策略、Plan B 和验收条件。 +- 以上选择组合为不可变 StrategyVersion,经 publish strategy 检查点审批后才能被 Brief 引用。 - 策略人员可比较候选方案;审核员批准后才能进入正式 Brief。 ### FR-05 内容策划 @@ -124,15 +125,15 @@ Agent、Daemon 和 Worker 是工具或系统参与者,不进入业务责任矩 ### FR-07 审核与客户协作 -- Brief、ScriptVersion 和交付版本分别使用 ReviewCycle。 +- 审核主体统一为 SubmissionRevision:knowledge、strategy、brief、script、delivery 各自的 Submission 分别开 ReviewCycle。 - 批注支持对象级、镜头级、字段级定位,区分内部与客户可见性。 - 未解决阻断批注时不得进入下一审批阶段。 -- 客户审批链接绑定 tenant、project、subject type、subject version、email 和有效期。 +- 客户审批链接绑定 tenant、project、具体 SubmissionRevision、email 和有效期;出现新 revision 后旧链接自动失效。 - 客户作出最终决策前使用一次性邮件验证码;撤销立即生效。 ### FR-08 交付与外部制作 -- 将客户批准的 ScriptVersion 组成 DeliveryPackage。 +- 将客户批准的 script ApprovedSnapshot 组成 DeliveryPackage。 - 支持 canonical JSON、Markdown 和 XLSX 导出,并记录内容哈希。 - ProductionHandoff 包含素材清单、缺失素材、镜头制作方式、生成工具建议、权利边界和验收清单。 - V2 记录外部制作状态和成片关联,不在系统内自动生成视频。 @@ -140,7 +141,7 @@ Agent、Daemon 和 Worker 是工具或系统参与者,不进入业务责任矩 ### FR-09 投放结果与学习 - 支持 CSV/XLSX/人工方式导入平台或门店结果,保存不可变 ImportBatch 和 Observation。 -- 指标必须绑定内容版本、渠道、统计窗口、单位、分母、自然/付费来源和定义版本。 +- 指标必须绑定内容版本(script ApprovedSnapshot)、渠道、统计窗口、单位、分母、自然/付费来源和定义版本。 - 系统可计算派生指标并生成候选评级建议;因果归因和策略采纳必须人工确认。 - Learning 可回到 Framework、ShotPattern、SellingPoint、VisualizationPlan 和 Experiment。 @@ -164,7 +165,7 @@ Agent、Daemon 和 Worker 是工具或系统参与者,不进入业务责任矩 - 核心业务对象使用服务端原生结构化视图。 - 扩展产物按 `cloud_native -> safe_projection -> safe_rendition -> local_open -> metadata_only` 降级。 - Preview 不可用时必须显示可理解占位和本机打开动作,不得出现空白 iframe。 -- Hosted Preview 属于第三波最后能力,不替代 ScriptVersion 的审批和哈希。 +- Hosted Preview 属于第三波最后能力,不替代 SubmissionRevision 的审批和哈希。 ### FR-13 本地工作区、Skills、MCP 与发布 @@ -217,7 +218,7 @@ Agent、Daemon 和 Worker 是工具或系统参与者,不进入业务责任矩 ### 8.4 内容学习 - 每个正式实验只声明一个主要变化变量。 -- 导入结果能定位到 ScriptVersion、CreativeDirection、卖点、框架和镜头模式。 +- 导入结果能定位到 script ApprovedSnapshot 及其中的 ScriptVersion、CreativeDirection、卖点、框架和镜头模式。 - 自动评级建议不能直接改写正式策略;人工采纳率和拒绝原因可统计。 ## 9. V2 总体验收 diff --git a/docs/roadmap/v2/02-business-capability-map.md b/docs/roadmap/v2/02-business-capability-map.md index 70990c4..259b58f 100644 --- a/docs/roadmap/v2/02-business-capability-map.md +++ b/docs/roadmap/v2/02-business-capability-map.md @@ -179,10 +179,11 @@ mindmap ### UC-04 锁定策略和 Brief -1. 选择 Audience、DemandMoment 和 SellingPoint。 +1. 选择 Audience、DemandMoment 和 SellingPoint,组合为 StrategyVersion 候选。 2. 创建至少一个 VisualizationPlan,声明真实性策略和 Plan B。 -3. 生成 Brief 草稿并执行确定性校验。 -4. Reviewer 批准不可变 BriefVersion。 +3. publish strategy 检查点,Reviewer 批准后 pull strategy ApprovedSnapshot。 +4. 引用已批准的 `strategy_version_id` 和 `visualization_plan_ids` 生成 Brief 草稿并执行确定性校验。 +5. publish brief 检查点,Reviewer 批准对应 SubmissionRevision,生成 brief ApprovedSnapshot。 ### UC-05 生成 AI 视频就绪剧本批次 diff --git a/docs/roadmap/v2/03-domain-and-data-model.md b/docs/roadmap/v2/03-domain-and-data-model.md index a176392..b8f0442 100644 --- a/docs/roadmap/v2/03-domain-and-data-model.md +++ b/docs/roadmap/v2/03-domain-and-data-model.md @@ -53,14 +53,14 @@ erDiagram CREATIVE_BATCH ||--o{ SCRIPT : creates SCRIPT ||--o{ SCRIPT_VERSION : versions SCRIPT_VERSION ||--o{ SHOT : contains - SCRIPT_VERSION ||--o{ DELIVERY_PACKAGE : packages - DELIVERY_PACKAGE ||--o{ PRODUCTION_HANDOFF : hands_off - SCRIPT_VERSION ||--o{ REVIEW_CYCLE : reviewed_by REVIEW_CYCLE ||--o{ REVIEW_COMMENT : contains - SCRIPT_VERSION ||--o{ APPROVAL_DECISION : binds - SCRIPT_VERSION ||--o{ PERFORMANCE_OBSERVATION : measures - PERFORMANCE_OBSERVATION ||--o{ LEARNING : informs + SUBMISSION_REVISION ||--o{ APPROVAL_DECISION : binds + SUBMISSION_REVISION ||--o{ REVIEW_GRANT : shares + APPROVED_SNAPSHOT }o--o{ DELIVERY_PACKAGE : packages + DELIVERY_PACKAGE ||--o{ PRODUCTION_HANDOFF : hands_off + APPROVED_SNAPSHOT ||--o{ PERFORMANCE_OBSERVATION : measures + PERFORMANCE_OBSERVATION }o--o{ LEARNING : informs AUTOMATION_PLAN ||--o{ AUTOMATION_PLAN_VERSION : versions AUTOMATION_PLAN_VERSION ||--o{ TASK_RUN : triggers @@ -68,6 +68,38 @@ erDiagram RUN_ATTEMPT ||--o{ RUN_OUTPUT : produces ``` +### 2.1 单轨审批:SubmissionRevision 是唯一云端审批主体 + +V2 只保留一条云端治理轨道。所有需要人工决定的对象都以 `SubmissionRevision` 进入审核、以 `ApprovedSnapshot` 固化批准结果,不存在第二条"批准后再物化成 ScriptVersion/BriefVersion"的平行路径。 + +| 环节 | 主体 | 说明 | +| --- | --- | --- | +| 内部批注与退回 | `SubmissionRevision` | ReviewCycle、ReviewComment 挂在 revision 上,`subject_path` 定位到镜头或字段 | +| 内部与客户批准 | `SubmissionRevision` | ApprovalDecision 绑定 `subject_type=submission_revision` 和 `subject_hash=content_hash` | +| 客户安全链接 | `SubmissionRevision` | ReviewGrant 绑定具体 revision,不绑定"最新版本" | +| 交付与导出 | `ApprovedSnapshot` | DeliveryPackage 引用一个或多个 client approved 的 script ApprovedSnapshot | +| 投放结果绑定 | `ApprovedSnapshot` | PerformanceObservation 的内容版本即 ApprovedSnapshot ID | +| 下游影响传播 | `ApprovedSnapshot` | lineage edge 的终点是快照,不是本地文件 | + +内容身份和批准资格分离: + +- `BriefVersion`、`Script`、`ScriptVersion`、`Shot` 是**本地产生的内容身份**,ID 在工作区创建、随 publish 原样带入 revision 正文。 +- **批准资格由云端授予**:某个 `brief_version_id` 是否"已批准",取决于它是否出现在对应 brief `ApprovedSnapshot` 的 `eligible_ids` 中;`script_id` 同理。 +- 因此本地 lint 判断"上游是否已批准"时,读取 `.contentcloud/cache/approved/` 中已 pull 的快照,而不是查询云端是否存在同名 ScriptVersion 记录。 + +这一设计使 `contracts/script-package-2.0.schema.json` 的 `brief_version_id`、`script_id` 等字段语义不变,无需重命名。 + +### 2.2 V1 ScriptVersion 轨道的退役 + +V1 的 `ScriptVersion` 曾同时是内容载体和审批主体。V2 保留它的**只读历史**语义,并停止在它上面开新审批: + +1. 已存在的 V1 `ScriptVersion`、`ReviewCycle`、`ApprovalDecision`、`ReviewGrant`、导出记录和 `PerformanceObservation` 全部保持可读,历史决定不改写。 +2. 迁移时为每条 V1 已批准 ScriptVersion 回填一条 `origin=v1_import` 的只读 `ApprovedSnapshot` 影子记录,`external_ref` 保留原 ScriptVersion ID,使交付、结果和 lineage 查询在新旧数据上形态一致。 +3. 回填不重算 hash:影子快照沿用 V1 `content_hash`,`schema_version` 标记为 `1.x`。 +4. 切换完成并观察稳定后,停止创建新的 ScriptVersion 记录及其 ReviewCycle;读路径按 `12-migration-and-delivery-plan.md` 的退役节奏保留至少一个稳定版本周期。 + +> 实现现状:`internal/app/review_cycles.go` 与 `internal/app/review_export.go` 目前仍以 `script_version` 为 subject,改挂 `submission_revision` 是波次一的 P0 改造项,见 `14-implementation-status.md`。 + ## 3. 通用字段与版本规则 可变聚合通用字段: @@ -158,8 +190,12 @@ stateDiagram-v2 submitted --> withdrawn ``` +`rejected` 是终态:该 Submission 不再接受新 revision,需要另建 Submission 重新走流程。内审或客户的"退回修改"一律进入 `changes_requested`,不使用 `rejected`。 + 批准时服务端生成 `ApprovedSnapshot`,包含批准的 canonical 内容、subject hash、决定、允许后续本地使用的 eligible IDs 和下载 manifest。`DecisionDelta`/`ReviewFeedbackBundle` 是客户端 pull 的不可变反馈包。 +一个 Submission 的审批分内部与客户两个阶段,但绑定同一个 revision:内部批准记录 `decision_stage=internal`,客户 OTP 批准记录 `decision_stage=client`。只有两个阶段都通过的 revision 才生成可交付的 `ApprovedSnapshot`。 + `SourceDisclosure` 对每个来源记录 `metadata_only|evidence_pack|full_source`。默认 evidence_pack;高风险 Claim/Rights 若证据等级不满足租户策略则不能远程批准。 ### 5.1 项目与治理 @@ -228,7 +264,7 @@ stateDiagram-v2 `ContentPlan` 是周期和渠道计划;`Campaign` 是一个业务主题;`ExperimentPlan` 是单变量测试;`BriefVersion` 是创意生产的不可变输入。 -Brief 必须引用 approved StrategyVersion 和至少一个 approved VisualizationPlan。V1 Brief 记录迁移为默认 Campaign 下的 BriefVersion,不改变原 ID。 +Brief 必须引用 approved StrategyVersion 和至少一个 approved VisualizationPlan,该约束从波次一起生效:`contracts/brief-2.0.schema.json` 中 `strategy_version_id` 与 `visualization_plan_ids` 均为必填,本地 Brief lint 校验二者都落在已 pull 的 strategy ApprovedSnapshot 的 eligible IDs 内。V1 Brief 记录迁移为默认 Campaign 下的 BriefVersion,不改变原 ID。 ### 5.6 创意生产 @@ -236,17 +272,19 @@ Brief 必须引用 approved StrategyVersion 和至少一个 approved Visualizati `CreativeBatch` 首先是本地批次 manifest:brief snapshot、direction IDs、count、variant dimension、output schema 和本地 status。publish 后云端以 SubmissionRevision 保存候选集合;远程 Automation 才额外关联 TaskRun。 -`Script` 是稳定身份;`ScriptVersion` 是不可变内容;`Shot` 可作为 JSON 子对象和读优化表投影,不允许两个事实源分别编辑。 +`Script` 是稳定内容身份,`ScriptVersion` 是不可变稿件,`Shot` 是版本内的 JSON 子对象。三者都在本地工作区产生,publish 后作为 SubmissionRevision 正文的一部分进入云端;云端可以建读优化投影表用于列表和比较,但不允许在投影上编辑,也不再为它们单独开审批流(见 §2.1)。 ### 5.7 审核与客户协作 -沿用 ReviewCycle、ReviewComment、ReviewGrant、ApprovalDecision。新增字段定位 `subject_path`,例如 `/shots/shot-03/voiceover`。 +沿用 ReviewCycle、ReviewComment、ReviewGrant、ApprovalDecision,全部以 `SubmissionRevision` 为主体。新增字段定位 `subject_path`,例如 `/objects/0/shots/shot-03/voiceover`。 -ApprovalDecision 绑定 subject_type、subject_id、subject_hash、actor、decision、reason、previous_state、resulting_state。 +ApprovalDecision 绑定 subject_type、subject_id、subject_hash、decision_stage、actor、decision、reason、previous_state、resulting_state。V2 新记录的 `subject_type` 固定为 `submission_revision`,`subject_hash` 取 revision 的 `content_hash`;`script_version` 仅出现在 V1 历史记录中。 + +ReviewGrant 绑定 tenant、project、`submission_revision_id`、客户邮箱和有效期。revision 被新 revision 取代后,旧 grant 自动失效,客户须使用新链接。 ### 5.8 交付与外部制作 -`DeliveryPackage`:批准的 ScriptVersion 集合、格式、manifest、hash、recipient、delivery status。 +`DeliveryPackage`:一组 client approved 的 script `ApprovedSnapshot`(多对多)、格式、manifest、hash、recipient、delivery status。 `ProductionHandoff`:shot production method、asset checklist、missing inputs、tool suggestion、rights boundary、acceptance checklist、external status 和 final media refs。 @@ -254,7 +292,7 @@ V2 只记录外部制作,不存供应商密钥,不自动提交视频生成 ### 5.9 投放结果与学习 -沿用 ImportBatch、PerformanceObservation、RatingDecision 和 Memory/Lineage 设计。新增 `Learning` 作为候选结论:target_type、target_id、observation_ids、statement、confidence、sample warning、recommended action、adoption decision。 +沿用 ImportBatch、PerformanceObservation、RatingDecision 和 Memory/Lineage 设计。`PerformanceObservation` 的内容版本引用改为 `approved_snapshot_id`;V1 历史观察通过 §2.2 的影子快照获得同一形态。新增 `Learning` 作为候选结论:target_type、target_id、observation_ids、statement、confidence、sample warning、recommended action、adoption decision。一条 Learning 可引用多条 Observation,一条 Observation 也可支撑多条 Learning。 `Learning=adopted` 也不能自动修改 StrategyVersion;采纳动作必须创建新策略/Brief 或显式 ImpactAction。 @@ -282,9 +320,11 @@ RunOutput 不得直接批准、发布或覆盖业务对象。普通本地操作 local_source -> local evidence/knowledge -> knowledge submission -> approved snapshot approved knowledge -> local strategy/brief -> brief submission -> approved brief approved brief -> local script -> script submission -> approved script -script_version -> delivery_package -> performance_observation -> learning +approved script -> delivery_package -> performance_observation -> learning ``` +edge 的两端只能是本地内容身份或云端快照,不再出现 `script_version` 作为独立审批节点。 + 上游变化只执行两步: 1. 确定性计算受影响对象和影响严重度。 @@ -295,11 +335,11 @@ script_version -> delivery_package -> performance_observation -> learning ## 8. 数据一致性约束 - tenant/project 外键必须一致,跨项目引用默认拒绝。 -- approved BriefVersion 的 StrategyVersion 必须 approved 且未失效。 -- review_ready ScriptVersion 必须来自通过本地 preflight 和服务端 manifest 复核的 SubmissionRevision。 -- client approved ScriptVersion 必须先 internal approved 且无 unresolved blocking comment。 -- DeliveryPackage 只能引用 client approved ScriptVersion。 -- PerformanceObservation 必须引用具体内容版本和统计窗口。 +- 已批准 Brief 引用的 `strategy_version_id` 必须落在某个 approved 且未失效的 strategy ApprovedSnapshot 的 eligible IDs 中(波次一起生效)。 +- 进入 review 的 script SubmissionRevision 必须通过本地 preflight 和服务端 manifest 复核。 +- `decision_stage=client` 的批准必须先有同一 revision 上 `decision_stage=internal` 的批准,且无 unresolved blocking comment。 +- DeliveryPackage 只能引用两阶段均已批准的 script ApprovedSnapshot。 +- PerformanceObservation 必须引用具体 ApprovedSnapshot 和统计窗口。 - schedule trigger 只能用于模板声明的 automation type。 - 同一业务幂等键在 tenant + operation 范围内唯一。 @@ -312,8 +352,9 @@ script_version -> delivery_package -> performance_observation -> learning | script generation TaskRun | 保留为 V1 远程执行历史;V2 普通生成迁移为 Submission,不伪造 AutomationPlan | | ScriptPackage 1.x | 只读兼容;修订时显式升级为 2.0 | | Artifact | 继续保存二进制/扩展产物;新增 RunOutput 负责业务投影关系 | -| PerformanceObservation | 原位保留,补 Campaign/Experiment/CreativeDirection lineage | +| PerformanceObservation | 原位保留,内容版本引用改指影子 ApprovedSnapshot,补 Campaign/Experiment/CreativeDirection lineage | +| ScriptVersion 及其 ReviewCycle/Approval/Grant | 只读历史;按 §2.2 回填 `origin=v1_import` 影子 ApprovedSnapshot,不再开新审批 | -V1 云端 TaskRun 生成的 ScriptVersion 保持可读。迁移后新的普通创作默认由本地 publish 创建;只有明确 Automation 来源的 ScriptVersion 才要求 TaskRun/RunAttempt lineage。 +V1 云端 TaskRun 生成的 ScriptVersion 保持可读。迁移后新的普通创作默认由本地 publish 创建,审批统一走 Submission 轨;只有明确 Automation 来源的产出才要求 TaskRun/RunAttempt lineage。 数据库迁移必须可回滚结构变更,不回滚已产生的业务决定;任何数据回填先 dry-run 输出数量、冲突和不可映射项。 diff --git a/docs/roadmap/v2/04-business-workflows.md b/docs/roadmap/v2/04-business-workflows.md index bc23f8a..2e388b4 100644 --- a/docs/roadmap/v2/04-business-workflows.md +++ b/docs/roadmap/v2/04-business-workflows.md @@ -131,8 +131,11 @@ flowchart TB D --> E{画面能证明且可实现?} E -- 否 --> F[换主体/场景/道具/Plan B] F --> D - E -- 是 --> G[创建 Campaign/Experiment] - G --> H[生成 BriefVersion] + E -- 是 --> S1[组合不可变 StrategyVersion] + S1 --> S2[publish Strategy Submission] + S2 --> S3[云端审批后本地 pull strategy ApprovedSnapshot] + S3 --> G[创建 Campaign/Experiment] + G --> H[生成引用已批准策略的 BriefVersion] H --> I[本地引用/权利/单变量校验] I -- 失败 --> H I -- 通过 --> J[内部审核] @@ -175,18 +178,18 @@ sequenceDiagram ```mermaid flowchart TB - A[ScriptVersion review_ready] --> B[内部 ReviewCycle] + A[script SubmissionRevision 待审] --> B[内部 ReviewCycle] B --> C{阻断批注?} C -- 是 --> D[本地pull反馈并创建revise LocalRunContext] D --> E[新不可变版本 + 结构化 diff] E --> P[publish新SubmissionRevision] P --> B - C -- 否 --> F[内部批准] - F --> G[生成客户审批 Grant] + C -- 否 --> F[内部批准 stage=internal] + F --> G[对该 revision 生成客户 ReviewGrant] G --> H[邮件 OTP 验证] H --> I{客户决策} I -- 退回 --> D - I -- 批准 --> J[锁定批准 hash] + I -- 批准 --> J[客户批准 stage=client 并生成 ApprovedSnapshot] J --> K[Gate 4 ready] ``` diff --git a/docs/roadmap/v2/05-script-production-system.md b/docs/roadmap/v2/05-script-production-system.md index e04f58c..7bb7df9 100644 --- a/docs/roadmap/v2/05-script-production-system.md +++ b/docs/roadmap/v2/05-script-production-system.md @@ -22,7 +22,8 @@ ContentPlan - CreativeDirection 是可比较的创意方向,不是完整剧本。 - CreativeBatch 是本地批次 manifest 和候选集合,不等于 TaskRun;只有 Automation 远程触发时才同时存在云端 TaskRun。 -- Script 是稳定内容身份,ScriptVersion 是不可变稿件。 +- Script 是稳定内容身份,ScriptVersion 是不可变稿件;二者的 ID 在本地产生,批准资格由云端 ApprovedSnapshot 授予(见 `03-domain-and-data-model.md` §2.1)。 +- "approved BriefVersion" 指该 `brief_version_id` 已出现在某个 brief ApprovedSnapshot 的 eligible IDs 中,本地 lint 从 `.contentcloud/cache/approved/` 判定。 - Shot 同时承担叙事功能、视觉实现、生成约束、证据和验收。 ## 3. 完整生产流程 @@ -57,7 +58,7 @@ flowchart TB | --- | --- | | 业务 | channel、objective、campaign、experiment、duration range | | 用户 | audience、scenario、demand moment、pain point | -| 策略 | primary selling point、support points、positioning | +| 策略 | strategy_version_id(已批准)、primary selling point、support points、positioning | | 画面 | approved visualization plans、assets、truth strategy、Plan B | | 表达 | tone、brand rules、approved claims、forbidden claims | | 结构 | hook expectation、narrative constraints、single CTA | diff --git a/docs/roadmap/v2/06-local-workspace-and-publishing.md b/docs/roadmap/v2/06-local-workspace-and-publishing.md index 59e8646..7fe5718 100644 --- a/docs/roadmap/v2/06-local-workspace-and-publishing.md +++ b/docs/roadmap/v2/06-local-workspace-and-publishing.md @@ -90,6 +90,7 @@ project-root/ │ ├── properties.yaml │ ├── rules/ │ └── vocabularies/ +├── schemas/ ├── knowledge/ │ ├── index/ │ ├── sources/ @@ -98,6 +99,7 @@ project-root/ │ ├── claims/ │ ├── assets/ │ ├── rights/ +│ ├── conflicts/ │ └── packs/ ├── raw/ │ ├── inbox/ @@ -114,7 +116,8 @@ project-root/ ├── briefs/ ├── scripts/ ├── storyboards/ - └── reports/ + ├── reports/ + └── delivery/ ``` `raw/` 默认加入项目忽略规则;模板不强制创建 Git 仓库。用户选择版本控制时,原始资料和本地敏感缓存仍保持忽略。 @@ -171,15 +174,15 @@ contentcloud skills install contentcloud-marketing-video-script --target codex ## 7. MCP -`contentcloud-local` MCP 通过 `contentcloud mcp serve` 在本机以 stdio 运行。当前已实现工具: +`contentcloud-local` MCP 通过 `contentcloud mcp serve` 在本机以 stdio 运行。当前已实现 24 个工具,与 `09-cli-mcp-and-contracts.md` §4 的清单一致: -- `workspace_status`、`workspace_doctor` -- `publish_preflight` -- `submission_status` -- `review_feedback_list` -- `approved_snapshot_list` +- 工作区:`workspace_status`、`workspace_doctor` +- 本地来源:`source_register`、`source_list`、`source_ingest`、`source_verify` +- 本地运行与知识:`local_run_init`、`local_run_show`、`knowledge_import_candidates`、`knowledge_lint`、`knowledge_query`、`knowledge_diagnose`、`knowledge_pack` +- Brief 与剧本:`brief_lint`、`creative_batch_init`、`creative_batch_lint`、`creative_batch_finalize`、`script_lint`、`script_diff`、`script_export` +- 云端治理:`publish_preflight`、`submission_status`、`review_feedback_list`、`approved_snapshot_list` -`source_register`、`source_list`、`local_run_*`、`knowledge_query` 和 `lint_run` 属于后续本地业务工具。MCP 本身不直接调用私有 HTTP;需要云端数据时复用 CLI 的 Workspace Credential 和统一 dispatch 客户端。它不返回 token、不自动上传资料、不启动后台 Daemon。 +研究、策略编译和反馈应用相关工具属于后续波次。MCP 本身不直接调用私有 HTTP;需要云端数据时复用 CLI 的 Workspace Credential 和统一 dispatch 客户端。它不返回 token、不自动上传资料、不启动后台 Daemon。 ```bash contentcloud mcp status @@ -258,7 +261,9 @@ contentcloud publish script --review | `evidence_pack` | 以上 + 精确摘录/安全预览 | 默认;可审核普通事实,受租户风险策略限制 | | `full_source` | 以上 + 加密原件 | 允许授权审核人检查完整上下文 | -当前 CLI 接受显式 disclosures JSON;未提供时不上传来源正文。产品默认 evidence_pack 和交互式逐来源选择仍待实现。 +产品规则是默认 `evidence_pack` 并支持逐来源交互选择。 + +> 实现现状:CLI 目前接受显式 disclosures JSON,未提供时不上传任何来源正文(等价于 `metadata_only`),交互式逐来源选择待实现。以 `14-implementation-status.md` 为准。 高风险 Claim、权利和合规事实如果证据等级不足: @@ -288,7 +293,7 @@ contentcloud pull approved - 批准当前 SubmissionRevision 并创建 ApprovedSnapshot。 - 查看 revision 摘要、结构化正文、来源披露、hash 和审核记录。 -独立批注编辑、责任人/截止时间、审批链接和字段级 diff 属于后续审核协作增强。 +独立批注编辑、责任人/截止时间和字段级 diff 属于后续审核协作增强。客户审批链接(ReviewGrant + OTP)目标是绑定具体 SubmissionRevision,属于波次一的单轨收敛改造,见 `03-domain-and-data-model.md` §2.1。 Web 不允许直接改 Submission 正文、知识值、Brief 内容、口播或镜头文本。所有内容修改回到本地,形成新的 SubmissionRevision。 diff --git a/docs/roadmap/v2/09-cli-mcp-and-contracts.md b/docs/roadmap/v2/09-cli-mcp-and-contracts.md index 9940eef..d75f800 100644 --- a/docs/roadmap/v2/09-cli-mcp-and-contracts.md +++ b/docs/roadmap/v2/09-cli-mcp-and-contracts.md @@ -62,10 +62,12 @@ contentcloud device list|show|revoke ### 本地工作流与云端九域资源 ```text -contentcloud local-run init|show|resume|validate -contentcloud source register|list|show -contentcloud lint knowledge|content|all -contentcloud knowledge query +contentcloud local source register|list|show|ingest|verify +contentcloud local run init|show|record|check|advance|resume|fail|validate +contentcloud local knowledge import|lint|query|diagnose|pack +contentcloud local brief lint +contentcloud local script batch init|lint|finalize +contentcloud local script lint|diff|export contentcloud publish knowledge|research|strategy|brief|script|delivery|performance contentcloud pull feedback|decisions|approved @@ -74,7 +76,7 @@ contentcloud review show contentcloud delivery download contentcloud performance import contentcloud impact show ``` -上面 publish/pull/submission 命令已实现。`local-run`、本地 source register、通用 lint、delivery download 和独立 impact 命令是目标命令面,当前分别由项目 Skill、既有云端资源命令或 lineage 命令承接。 +上述 `local source/run/knowledge/brief/script`、publish/pull/submission 命令已经实现。普通本地命令只读写工作区,不创建云端 `TaskRun`;只有显式 publish/pull/init 等云端动作才通过 CLI Gateway 通信。`delivery download` 和独立 impact 命令仍属于后续命令面。 云端内容正文没有通用 update 命令。CLI 只发布不可变 Submission、拉取反馈/批准快照和执行领域允许的状态动作,不提供 `resource patch status=approved`。 @@ -98,7 +100,13 @@ contentcloud skills list|read|status|install contentcloud mcp status|serve ``` -`init` 默认安装项目级 Skill/MCP;修改项目 Agent 配置必须使用 `--accept-project-config`。当前 MCP 暴露 `workspace_status`、`workspace_doctor`、`publish_preflight`、`submission_status`、`review_feedback_list` 和 `approved_snapshot_list`。 +`init` 默认安装项目级 Skill/MCP;修改项目 Agent 配置必须使用 `--accept-project-config`。当前 MCP 复用同一套 `localworkspace` 与 CLI 网关逻辑,已暴露: + +- 工作区:`workspace_status`、`workspace_doctor`。 +- 本地来源:`source_register`、`source_list`、`source_ingest`、`source_verify`。 +- 本地运行与知识:`local_run_init`、`local_run_show`、`knowledge_import_candidates`、`knowledge_lint`、`knowledge_query`、`knowledge_diagnose`、`knowledge_pack`。 +- Brief 与剧本:`brief_lint`、`creative_batch_init`、`script_lint`、`creative_batch_lint`、`creative_batch_finalize`、`script_diff`、`script_export`。 +- 云端治理:`publish_preflight`、`submission_status`、`review_feedback_list`、`approved_snapshot_list`。 ### 产物 @@ -194,7 +202,7 @@ JSON 成功 envelope 写 stdout,结构化错误写 stderr;当前未提供 `- } ``` -当前 publish preflight 显示对象数量、blocked 数、各披露等级、上传字节数、基线 ID 和审核可见范围,并验证工作区文件边界、JSON、类型字段和大小。字段级基线 diff 与完整 Schema registry 是后续加固项。服务端复算 canonical hash,并复核基线、tenant/project、权限和幂等键后创建 SubmissionRevision。 +当前 publish preflight 显示对象数量、blocked 数、各披露等级、上传字节数、基线 ID 和审核可见范围,并验证工作区文件边界、JSON、类型字段和大小。Brief publish 强制复用 Brief V2 lint;Script publish 会递归识别 `outputs/scripts//` 下真正的 `script_package`,排除 batch/context 文件,并强制复用 ScriptPackage V2 完整本地 lint。只发现一个剧本时可自动选择;存在多个候选时必须用重复 `--file` 明确本次审核范围。修订字段漂移由 `contentcloud local script diff` 检查。服务端复算 canonical hash,并复核基线、tenant/project、权限和幂等键后创建 SubmissionRevision。 ## 9. Pull Bundle @@ -250,6 +258,17 @@ Capability Manifest 只在用户启用 Automation 时注册。服务端匹配 ca Task Contract 只服务 Automation。不同 task type 使用不同最小字段组合,并增加 workspace ID、required local source hashes 和 output submission policy。普通本地 `knowledge_extract`/`script_revise` 使用 LocalRunContext,不生成 Task Contract。 +仓库当前只有 `contracts/task-contract-1.0.schema.json`。1.1 是波次三 Automation 启用时的目标版本,相对 1.0 的增量为: + +| 变更 | 内容 | 兼容性 | +| --- | --- | --- | +| 新增 `workspace_id` | 声明 Automation 使用的隔离工作区 | 可选字段,向后兼容 | +| 新增 `required_local_source_hashes` | 客户端据此校验本地来源一致性 | 可选字段,向后兼容 | +| 新增 `output_submission_policy` | 声明 RunOutput 自动创建 SubmissionRevision 的类型和披露等级 | 可选字段,向后兼容 | +| `output_schema` 收紧 | 只允许 capability manifest 中声明过的 schema ID | 收紧规则,需 1.1 | + +1.0 保持只读兼容,服务端不得向只支持 1.0 的客户端投递需要上述字段的任务。 + ## 12. Poll 与租约 ```json diff --git a/docs/roadmap/v2/12-migration-and-delivery-plan.md b/docs/roadmap/v2/12-migration-and-delivery-plan.md index e22c7c4..1429cc0 100644 --- a/docs/roadmap/v2/12-migration-and-delivery-plan.md +++ b/docs/roadmap/v2/12-migration-and-delivery-plan.md @@ -28,10 +28,12 @@ V2 不推倒 V1,但会把普通创作从云端 TaskRun 迁到本地工作区 ### 已知缺口 +- **审批单轨收敛**:ReviewCycle/ApprovalDecision/ReviewGrant/OTP/导出/DeliveryPackage/PerformanceObservation 仍以 V1 `script_version` 为 subject(`internal/app/review_cycles.go`、`internal/app/review_export.go`),Submission 轨与之无连接,Golden Journey 第 8-10 步因此跑不通。 - Client/Brand/Product 分层和四层上下文继承。 -- 市场研究、内容计划、创意方向/批次、交付交接的正式聚合。 -- ScriptPackage V2 和完整剧本工作台。 -- 完整 LocalRunContext、15 维诊断、七层知识包转换和跨阶段本地命令。 +- StrategyVersion/VisualizationPlan 的版本化审批与 Brief 策略血缘(波次一)。 +- 市场研究、ContentPlan/Campaign 和交付交接的正式 V2 聚合(波次二)。 +- 资产、权利、冲突对象的正式本地导入/迁移命令,以及金陵古都香全量数据转换。 +- ScriptPackage V2 的 Web 业务工作台和真实业务 UAT;客户端剧本工程已实现。 - 远程签名 WorkspaceTemplate、模板升级/diff 和更多领域 Skills/MCP 工具。 - Automation Plan、schedule/event trigger、PlanChangeRequest、RunOutput。 - 通用 Run 详情和九域导航。 @@ -44,6 +46,10 @@ V2 不推倒 V1,但会把普通创作从云端 TaskRun 迁到本地工作区 - feedback/decision/approved pull,进入 inbox 或只读 cache,不改业务正文。 - Submission Web 列表、版本查看、来源披露、批注、修改要求、批准和 ApprovedSnapshot。 - PostgreSQL migration `00013_v2_workspace_submissions.sql`、内存测试和可选 PostgreSQL 事务/RLS 集成测试。 +- 本地 source register/list/show/ingest/verify、可恢复 LocalRunContext,以及来源 hash/MIME/证据 locator 与 quote 精确校验。 +- `knowledge-candidates/1.0` 严格导入、15 维诊断、七层 KnowledgePack、eligible/blocked/informational 查询和 evidence-pack disclosures。 +- Brief V2 lint、CreativeDirection/CreativeBatch、冻结 context、ScriptPackage V2 逐镜头 lint、blocked/review_ready、JSON Pointer diff、JSON/Markdown/XLSX 导出。 +- 金陵古都香一个真实 DOCX 已自动走通 register -> ingest -> candidate import -> lint -> 15 维 -> 七层 pack -> knowledge publish preflight,且 `raw_files_upload=false`。 ## 3. 波次一:本地工作区、知识发布与剧本工程 @@ -56,7 +62,9 @@ V2 不推倒 V1,但会把普通创作从云端 TaskRun 迁到本地工作区 - 增加 ClientAccount、Brand、Product 和项目引用。 - 增加 WorkspaceBinding、WorkspaceTemplateManifest/Lock、Submission/Revision、SourceDisclosure、DecisionDelta 和 ApprovedSnapshot。 - 增加 Methodology、TenantServiceTemplate、BrandKnowledgePack 和 ProjectContextSnapshot。 +- 增加 StrategyVersion 与 VisualizationPlan 审批(在 V1 SellingPoint/VisualizationPlan 之上补版本化封装),使 Brief 的策略血缘从波次一起成立。 - 增加 ContentPlan、Campaign、ExperimentPlan、CreativeDirection 和 CreativeBatch。 +- 把审批主体收敛为 SubmissionRevision:ReviewCycle/ApprovalDecision/ReviewGrant/DeliveryPackage/PerformanceObservation 改挂 revision 与 ApprovedSnapshot,V1 ScriptVersion 回填只读影子快照。 - 升级 ScriptPackage 2.0;现有 1.x 保持只读和导出兼容。 - LocalRunContext 留在本地;Automation RunOutput 延后到波次三。 @@ -64,10 +72,12 @@ V2 不推倒 V1,但会把普通创作从云端 TaskRun 迁到本地工作区 - [部分完成] 项目创建已有一次性 init code;Client/Brand/Product/服务模板分层待补。 - [部分完成] `contentcloud init`、workspace status/doctor 和项目级 Skills/MCP 已完成;upgrade/diff 待补。 -- [部分完成] publish/pull 已完成;本地素材诊断、七层知识包和 LocalRunContext 命令待补。 -- [部分完成] 云端 Submission 审阅、revision 查看和结构化对象展示已完成;本地候选比较、逐镜头 lint 和字段级 diff 待补。 +- [已实现,待 UAT] publish/pull、本地来源处理、LocalRunContext、15 维诊断、七层知识包和证据披露已完成。 +- [已实现,待 UAT] CreativeBatch、ScriptPackage V2、逐镜头 lint、blocked/review_ready、字段级 diff 和三格式导出已完成;云端 Submission 审阅保持只读正文。 +- [待实现] 审批单轨收敛:ReviewCycle/ApprovalDecision/ReviewGrant 改挂 SubmissionRevision,客户 OTP 审批与三格式导出改由 ApprovedSnapshot 驱动。这是波次一其余验收项的前置条件。 +- [待实现] StrategyVersion 最小可用审批,以及 Brief 的 `strategy_version_id` 必填校验与策略血缘。 - 普通生成不需要 capability;`script.generate@2.x` capability 留给波次三 Automation。 -- 客户审批和三格式导出使用 ScriptPackage V2。 +- 客户审批和三格式导出使用 ScriptPackage V2 的 canonical 内容。 ### 迁移 @@ -75,14 +85,18 @@ V2 不推倒 V1,但会把普通创作从云端 TaskRun 迁到本地工作区 2. 先生成 dry-run 报告:数量、重复 ID、无法映射状态、缺失 locator 和 hash。 3. 在本地迁移候选状态,不自动提升 verified/approved/valid,也不默认上传 raw。 4. 将首批十条脚本保留为本地 CreativeBatch;原 blocked 状态和原因不变。 -5. 分批 publish Knowledge/Script Submission,由真实审核员决定后生成 ApprovedSnapshot。 +5. 分批 publish Knowledge/Strategy/Brief/Script Submission,由真实审核员决定后生成 ApprovedSnapshot。 +6. 为 V1 已批准 ScriptVersion 回填只读影子 ApprovedSnapshot,核对导出内容与 hash 不变。 + +当前只完成了单个真实 DOCX 的自动化纵向验证。现有 source registry、232 个可映射对象、首批十条旧稿、真实 publish/人工审批/pull,以及从 Approved Brief 生成三候选的 Golden Journey 尚未完成,不能标记为 `accepted`。 ### 波次验收 - 金陵古都香 Golden Journey 通过。 -- V1 现有 TaskRun、ScriptPackage、审批历史和导出不回归。 +- V1 现有 TaskRun、ScriptPackage、审批历史和导出不回归;影子 ApprovedSnapshot 回填后历史导出内容与 hash 不变。 - 普通本地 ingest/generate/revise 全程不创建云端 TaskRun。 -- 客户审批固定 hash;修改上游后新稿必须重审。 +- 客户审批固定 SubmissionRevision hash;修改上游后新稿必须重审,旧 ReviewGrant 自动失效。 +- 已批准 Brief 均可追溯到某个 approved StrategyVersion。 ## 4. 波次二:九域业务工作台 @@ -94,7 +108,7 @@ V2 不推倒 V1,但会把普通创作从云端 TaskRun 迁到本地工作区 - 项目总览重构为 Gate、Workspace 状态、Submission、阻断、负责人和交付状态。 - 上线 ResearchTask、BenchmarkCase、MarketInsight 和情报采纳。 -- 上线 StrategyVersion、ContentPlan 和跨域 lineage。 +- 扩展 StrategyVersion 的比较、采纳与跨域 lineage(最小可用版本已在波次一交付)。 - 上线 DeliveryPackage、ProductionHandoff 和外部制作状态。 - 完成九域导航、全局待办、风险、审批和项目组合视图。 - 补齐各域 publish/pull、BFF、权限、审计和云端治理页面,不建设在线正文编辑器。 @@ -130,7 +144,7 @@ V2 不推倒 V1,但会把普通创作从云端 TaskRun 迁到本地工作区 ### 波次验收 -- monitor、generate、review 三种类型完成端到端验证。 +- 先完成 monitor、review、maintain 三种类型的端到端验证;远程 generate 在本地交互闭环稳定后于同波次后段验收,顺序与 `07-automation-and-run-model.md` §3 一致。 - schedule 不能用于正式生成、审批和交付模板。 - late report、租约过期、重复触发和通知失败不重复创建业务产物。 - Hosted Preview 失败可降级且不影响审批。 @@ -147,6 +161,7 @@ V2 不推倒 V1,但会把普通创作从云端 TaskRun 迁到本地工作区 - 后台批次按 tenant/project 处理,记录 checkpoint、数量、错误和不可映射项。 - Client/Brand/Product 从现有项目字段回填,无法唯一判断时进入人工映射清单。 - 旧 Brief 创建默认 ContentPlan/Campaign;旧 script Run 创建 one-off CreativeBatch。 +- 每条 V1 已批准 ScriptVersion 回填一条 `origin=v1_import` 只读 ApprovedSnapshot 影子记录,沿用原 `content_hash`,`external_ref` 保留原 ScriptVersion ID;历史 ApprovalDecision 与 ReviewGrant 不改写。 ### Verify @@ -173,6 +188,8 @@ V2 不推倒 V1,但会把普通创作从云端 TaskRun 迁到本地工作区 | Flag | 波次 | 回退行为 | | --- | --- | --- | +| `submission_single_track` | 1 | 审批仍走 V1 ScriptVersion 轨;关闭期间不得同时开启双轨写入 | +| `strategy_versions` | 1 | Brief 的 `strategy_version_id` 降级为可选,不校验策略血缘 | | `v2_client_context` | 1 | 使用现有项目字段和 V1 快照 | | `script_package_v2` | 1 | 继续生成/读取 1.x | | `creative_batches` | 1 | 使用单次 script run | @@ -194,7 +211,7 @@ Feature Flag 只切入口和行为,不允许形成两个并行事实源。 - 需求、领域、CLI/OpenAPI/Schema、Web 和审计语义一致。 - 正常、blocked、权限、离线、超时、重试和影响路径有自动化测试。 - 数据迁移有 dry-run、真实 PostgreSQL 证据、核对报告和回退步骤。 -- 没有服务端 LLM/Agent 依赖,没有程序化直连私有 API 的新入口。 +- 没有服务端 LLM/Agent 依赖;所有程序化云端访问复用 CLI 的 dispatch 客户端与凭据层,没有新增自建 HTTP 客户端。 - 文档、实现状态和真实 Web 行为同步更新。 - 客户业务门禁由有责任的试点人员签署,不由开发者代签。 diff --git a/docs/roadmap/v2/13-acceptance-and-traceability.md b/docs/roadmap/v2/13-acceptance-and-traceability.md index b84a3c3..25a4635 100644 --- a/docs/roadmap/v2/13-acceptance-and-traceability.md +++ b/docs/roadmap/v2/13-acceptance-and-traceability.md @@ -19,14 +19,14 @@ | FR-01 项目治理 | Project/成员/设备/审计 | Client/Brand/Product、Gate、Risk、Impact | 1-2 | 多客户项目与角色 UAT | | FR-02 可信知识 | 来源、证据、知识、冲突、权利 | 本地15维诊断、Knowledge Submission/批准快照 | 1 | 金陵古都香本地迁移/publish/决策 | | FR-03 市场情报 | Benchmark/Framework/ShotPattern | ResearchTask、Insight、监控 | 2-3 | 公网+企业资料研究及采纳 | -| FR-04 营销策略 | SellingPoint/VisualizationPlan | StrategyVersion、Audience/Scenario 组合 | 1-2 | 策略到 Brief lineage | +| FR-04 营销策略 | SellingPoint/VisualizationPlan | StrategyVersion、Audience/Scenario 组合 | 1-2 | 波次一:最小可用 StrategyVersion 审批与策略到 Brief lineage;波次二:候选比较与采纳 | | FR-05 内容策划 | Brief/Experiment 基础 | ContentPlan/Campaign/完整 Brief | 1-2 | 单变量 Brief 审批 | | FR-06 创意生产 | 云端script run、Package 1.x | 本地CreativeBatch、Package 2.0、Script Submission | 1 | 无TaskRun三候选、publish、修订 | | FR-07 审核协作 | Review/Comment/Grant/Approval | 字段定位、客户安全投影完善 | 1 | 内审、OTP、固定 hash | | FR-08 交付制作 | JSON/MD/XLSX Artifact | DeliveryPackage/Handoff/外部状态 | 1-2 | 三格式一致与交接清单 | | FR-09 结果学习 | Import/Observation/Rating/Memory | Learning 和跨域回流 | 2-3 | 人工采纳/拒绝与新实验 | | FR-10 Automation | TaskRun/Attempt/lease/heartbeat | Plan/remote/event/schedule/Submission | 3 | 隔离工作区与故障矩阵 | -| FR-11 多客户上下文 | 单项目快照 | 四层继承、rebase、七层知识包 | 1 | 两客户隔离与模板复用 | +| FR-11 多客户上下文 | 单项目快照 | 四层继承、rebase、七层知识包 | 1-2 | 波次一:四层继承与 rebase;波次二:两客户隔离与模板复用 | | FR-12 产物展示 | 原生核心和基础降级 | 安全投影、Run详情、Hosted Preview | 2-3 | 降级、隔离、无空白视图 | | FR-13 本地工作区 | CLI/Daemon/embedded skills基础 | init、模板锁、Skills/MCP、publish/pull | 1 | 空/非空目录、披露、冲突 UAT | @@ -39,8 +39,12 @@ | publish/pull 与 Submission 数据层 | `implemented` | 7 类 publish、3 类 pull、canonical hash、幂等、披露门禁、`00013` 与 Store 测试 | | Submission Web 审核切片 | `implemented` | 列表/详情、revision 切换、批注定位、修改要求、批准快照和正文不可编辑测试 | | PostgreSQL V2 真实运行证据 | `partial` | 集成测试已覆盖 token/不可变/事务/RLS;本次未设置测试数据库,尚无新执行记录 | -| 15 维诊断到七层知识包 | `partial` | 工作区目录和知识提取 Skill 已有;完整 LocalRun/领域命令和金陵数据迁移未完成 | -| ScriptPackage V2 全流程 | `partial` | 营销视频 Skill 和 script Submission 已有;CreativeBatch、2.0 Schema/工作台/三格式闭环未完成 | +| 本地来源、LocalRun 与证据链 | `implemented` | source register/list/show/ingest/verify、100MB/MIME/SHA-256、EvidenceBundle、阶段门禁和恢复测试 | +| 15 维诊断到七层知识包 | `implemented` | strict candidate import、locator/quote 精确匹配、eligible/blocked 查询、15 维、七层 pack 和 disclosures 测试;尚未业务 UAT | +| ScriptPackage V2 客户端闭环 | `implemented` | Brief lint、CreativeBatch/context 冻结、blocked/review_ready、逐镜头 lint、JSON Pointer diff、JSON/MD/XLSX 与 publish lint 测试 | +| 审批单轨收敛(Submission 承接审批/交付/结果) | `planned` | 契约与领域模型已定义(03 §2.1/§2.2);ReviewCycle/Grant/导出/交付仍挂 V1 `script_version`,代码未改造 | +| Brief 策略血缘(strategy_version_id) | `partial` | `contracts/brief-2.0.schema.json` 已列为必填;`LocalBrief` 结构体与 Brief lint 的必填校验待补 | +| 金陵古都香 Golden Journey | `partial` | 单个真实 DOCX 已到 knowledge publish preflight;232 对象、十条旧稿、真实审批/pull、Brief 到三候选未完成 | | 九域、四层上下文与 Automation Plan | `planned` | V1 对象可继承;V2 新聚合、页面和计划调度尚未实现 | | Hosted Preview | `deferred` | 不进入当前优先级,且不影响原生 Submission 审核 | @@ -65,20 +69,23 @@ - 保留 stable external ref、source locator、状态和 blocked 原因。 - 在本地保留首批十条 CreativeDraft 为一个 CreativeBatch,不提升发布资格、不默认上传 raw。 +当前自动化证据只覆盖一个真实 DOCX 到 knowledge publish preflight。以下清单仍是试点验收目标,不是已完成事实。 + ### 业务验收 1. Web 完成客户、品牌、产品、项目和服务模板,生成 init code。 2. 在空目录执行 init,验证模板、Skills、MCP、doctor 和默认不开启 Daemon。 3. 本地完成 15 维覆盖、七层知识包和 lint,明确缺口与冲突。 4. 选择来源披露等级并 publish;审核员按 ID 决定 Fact、Claim 和 Rights。 -5. 本地 pull ApprovedSnapshot,创建抖音 Campaign、单变量 Experiment 和 Brief,再 publish 审批。 -6. 本地选择至少两个 CreativeDirection,生成至少三条 ScriptPackage V2,证明没有云端 TaskRun。 -7. 验证一条 blocked、一条本地 review_ready,并准确解释差异。 -8. publish 剧本;云端对具体镜头和口播批注,本地 pull 后按基线修订并 republish。 -9. 完成内部批准和客户 OTP 批准,本地 pull 最终 ApprovedSnapshot。 -10. 导出 JSON、Markdown、XLSX,内容和 hash 一致。 -11. 导入结果、生成 candidate Learning,由策略人员明确采纳或拒绝。 -12. 修改一个来源或权利,验证受影响 Strategy/Brief/Script 进入 review_required。 +5. 本地 pull knowledge ApprovedSnapshot,完成受众、场景、卖点排序和可视化方案,publish strategy 检查点并由审核员批准。 +6. pull strategy ApprovedSnapshot,创建抖音 Campaign、单变量 Experiment 和引用 `strategy_version_id` 的 Brief,再 publish 审批。 +7. 本地选择至少两个 CreativeDirection,生成至少三条 ScriptPackage V2,证明没有云端 TaskRun。 +8. 验证一条 blocked、一条本地 review_ready,并准确解释差异。 +9. publish 剧本;云端对具体镜头和口播批注,本地 pull 后按基线修订并 republish。 +10. 完成内部批准(stage=internal)和客户 OTP 批准(stage=client),二者绑定同一 SubmissionRevision,本地 pull 最终 ApprovedSnapshot。 +11. 从该 ApprovedSnapshot 导出 JSON、Markdown、XLSX,内容和 hash 一致。 +12. 导入结果、生成 candidate Learning,由策略人员明确采纳或拒绝。 +13. 修改一个来源或权利,验证受影响 Strategy/Brief/Script 进入 review_required。 ### 责任签署 diff --git a/docs/roadmap/v2/14-implementation-status.md b/docs/roadmap/v2/14-implementation-status.md index a73082d..1cd97bc 100644 --- a/docs/roadmap/v2/14-implementation-status.md +++ b/docs/roadmap/v2/14-implementation-status.md @@ -8,14 +8,18 @@ flowchart LR W[Web 创建项目/init code] --> I[contentcloud init] I --> L[本地模板 + Skills + MCP] - L --> P[publish preflight] - P --> S[不可变 SubmissionRevision] + L --> X[local source register/ingest] + X --> K[knowledge import/lint/diagnose/pack] + K --> P[knowledge publish preflight] + P --> S[不可变 SubmissionRevision + 人工审核] S --> R[Web 审核] R -->|修改要求| F[pull feedback/decisions] R -->|批准| A[ApprovedSnapshot] A --> C[pull approved 只读缓存] - F --> L - C --> L + F --> K + C --> B[local Brief/CreativeBatch/ScriptPackage V2] + B --> SP[script lint/diff/publish] + SP --> R ``` 该路径以客户端为主。服务端只接收显式提交、保存治理事实并提供人工审核;普通本地操作不会创建 `TaskRun`。 @@ -27,7 +31,10 @@ flowchart LR | 初始化 | `contentcloud init --connect `;空目录初始化、未知非空目录拒绝、已有工作区幂等、完全离线 dry-run | | 本地模板 | `.contentcloud/project.yaml`、`template.lock`、`sync-state.json`、知识/ontology/raw/work/outputs 目录和受管文件 hash | | Agent 接入 | 项目级 Codex/Claude 配置;内置 `contentcloud-knowledge-extraction` 与 `contentcloud-marketing-video-script` Skills | -| MCP | `workspace_status`、`workspace_doctor`、`publish_preflight`、`submission_status`、`review_feedback_list`、`approved_snapshot_list` | +| 本地来源与运行 | source register/list/show/ingest/verify;copy/reference;SHA-256/MIME/100MB;EvidenceBundle;可恢复 LocalRun 阶段门禁 | +| 本地知识 | strict `knowledge-candidates/1.0` 导入、精确证据校验、eligible/blocked/informational、15 维诊断、七层 KnowledgePack 和披露清单 | +| 本地剧本 | Brief V2 lint、CreativeDirection、CreativeBatch、冻结 context、ScriptPackage V2、逐镜头 lint、blocked/review_ready、修订 diff、JSON/MD/XLSX | +| MCP | 工作区、source、LocalRun、knowledge、Brief、CreativeBatch、script、publish/submission/pull 共 24 个客户端工具,复用 CLI/领域逻辑 | | 凭据 | `wt_` Workspace Credential 用于 publish/pull;macOS Keychain;`dt_` 继续供兼容 Runtime/可选 Automation 使用 | | 发布 | knowledge/research/strategy/brief/script/delivery/performance;本地 JSON/lint/hash/大小/路径/preflight;`--review`/`--yes` 确认 | | 拉取 | feedback/decisions 写 inbox,ApprovedSnapshot 写只读 cache,不覆盖业务正文 | @@ -55,15 +62,28 @@ flowchart LR - Workspace Credential 只能访问绑定工作区;Web 用户角色负责 approve/request-changes。 - 普通 publish 不创建 TaskRun;服务端没有 LLM、Agent、Skill、MCP 或 Renderer 执行入口。 - Web 审核动作不改写 Revision 正文。 +- Brief publish 会强制执行 Brief V2 lint;Script publish 会识别批次目录中的 `script_package` 并强制执行完整 ScriptPackage V2 lint,batch/context 不会被误发布;多个候选必须通过重复 `--file` 明确范围。 +- 阻断剧本允许没有镜头,但必须使用 blocked 状态并给出结构化 blocked reasons;review_ready 必须有连续镜头、必要角色、引用、权利和实验声明。 +- 金陵古都香一个真实 DOCX 已自动走通来源登记、DOCX ingest、知识候选导入、lint、15 维诊断、七层 pack 和 publish preflight,且不上传 raw。 + +以下规则**尚未成立**,属于 §4 的 P0 缺口,不要据 §2 的"已实现"推断: + +- 云端审核目前只覆盖内部决定。客户 OTP 审批、导出、DeliveryPackage 和 PerformanceObservation 仍绑定 V1 `script_version`(`internal/app/review_cycles.go:36,135`、`internal/app/review_export.go:105,232`),与 Submission 轨没有连接。 +- 因此从 publish 到"客户批准并三格式交付"这一段目前跑不通,Golden Journey 第 9-11 步无法执行。 ## 4. 尚未完成 | 优先级 | 缺口 | 完成条件 | | --- | --- | --- | -| P0 | 完整知识生产命令 | 从 raw registry 到 evidence/fact/claim/asset/rights、冲突、缺口、15 维诊断和七层知识包可重复执行 | -| P0 | AI 视频剧本全流程 | Strategy/Brief/CreativeDirection/CreativeBatch/ScriptPackage V2、逐镜头 lint、修订和三格式交付走通 | -| P0 | 金陵古都香迁移/UAT | 现有资料保留 stable ref/status/locator,完成 knowledge -> brief -> script -> approval Golden Journey | +| P0 | **审批单轨收敛(代码缺口,非 UAT 缺口)** | ReviewCycle/ApprovalDecision/ReviewGrant 的 subject 从 `script_version` 改为 `submission_revision`;DeliveryPackage 与 PerformanceObservation 改引用 ApprovedSnapshot;V1 记录回填 `origin=v1_import` 影子快照。见 `03-domain-and-data-model.md` §2.1/§2.2 | +| P0 | 客户 OTP 审批链接接入 Submission | ReviewGrant 绑定具体 SubmissionRevision,新 revision 使旧链接失效;内部/客户两阶段决定写入同一 revision | +| P0 | 三格式导出改由 ApprovedSnapshot 驱动 | 导出与 DeliveryPackage 从批准快照的 canonical 内容生成,hash 与 revision 一致 | +| P0 | Brief 策略血缘 | `LocalBrief` 增加 `StrategyVersionID` 字段并加入 `internal/localworkspace/script.go` 的必填校验;校验其落在已 pull 的 strategy ApprovedSnapshot 的 eligible IDs 内(契约已改,实现待补) | +| P0 | 金陵古都香迁移/UAT | 现有资料保留 stable ref/status/locator,完成 knowledge -> strategy -> brief -> script -> approval Golden Journey | +| P0 | 金陵全量客户端迁移 | 迁移 source registry、232 个对象、冲突/权利候选和十条旧稿;输出 dry-run 与核对报告 | +| P0 | 真实审批剧本闭环 | 上述单轨改造完成后,真实 publish/人工审批/pull,从 Approved Brief 生成至少三候选、修订、批准并三格式交付 | | P1 | Client/Brand/Product 与四层上下文 | 正式聚合、版本继承、override/rebase 和第二客户隔离验收 | +| P1 | StrategyVersion 扩展能力 | 候选比较、采纳与跨域 lineage;最小可用审批已随波次一的 Brief 策略血缘一起交付 | | P1 | 九域工作台 | 研究、策略、内容计划、创意、交付、学习等真实对象和页面,不建设在线正文编辑器 | | P1 | 模板分发升级 | 服务端签名 manifest、workspace diff/upgrade、冲突和回滚 | | P2 | Automation Plan | remote/event/schedule、隔离工作区、RunOutput;仅在用户显式启用后使用 Device Credential | @@ -78,8 +98,24 @@ contentcloud workspace doctor contentcloud mcp status contentcloud mcp serve -contentcloud publish knowledge --dry-run -contentcloud publish script --review +contentcloud local source register --id +contentcloud local source ingest +contentcloud local source verify +contentcloud local run init --id --intent content +contentcloud local knowledge import work/candidates.json --run +contentcloud local knowledge lint +contentcloud local knowledge diagnose --channel douyin +contentcloud local knowledge pack --name + +contentcloud local brief lint outputs/briefs/.json +contentcloud local script batch init --brief --directions work/directions.json --count 3 --variant hook +contentcloud local script lint outputs/scripts//