---
name: agentopia-site-ops
description: Agentopia（原 AI 观象台）的权威运营 Skill。覆盖 tools/assets/news/blog 的 Content API type/channel 映射、字段 schema、选题与内容门禁、自动 content revision 发布、ETag/幂等、常见错误、回滚及跨机器版本同步。运营、值守、审阅、发布或排查 channel/schema/发布错误时必须先读。
---

# Agentopia 运营

把仓库 `skills/site-ops/SKILL.md` 作为运营政策唯一可编辑源，把代码中的 schema 与状态机作为机器契约。若二者冲突，立即停止写入和发布并通知维护者；不要猜测或绕过机器门禁。

内容只写 Content API，媒体只写 R2。不要直接改 D1/R2、`src/content/`、`public/uploads/` 或 `public/img/blog/`；这些目录只是迁移期只读基线。

## 1. 权威版本与跨机器同步

- 仓库源：`skills/site-ops/SKILL.md`。
- 已发布副本：`https://agentopia.cc/skill.md`。
- 已发布摘要：`https://agentopia.cc/skill-manifest.json`，包含 `schemaVersion`、SHA-256、UTF-8 字节数和精确 `skillUrl`。
- 飞书文档、群消息和异机本地文件只作阅读副本，不得反向覆盖仓库。

维护者修改后依次执行 Skill 校验、测试、CI、代码发布审批和 `npm run skill:check-online`。只有线上 manifest 与正文都逐字节匹配仓库源，才通知运营 Agent“已更新”，并附完整 SHA-256 与线上 URL。

运营 Agent 在**每个新任务开始前**无缓存读取 manifest，计算本地副本 SHA-256 与字节数：

1. 完全相同：继续并从头读取本 Skill。
2. 不同：停止当前任务，下载正文到临时文件并核对摘要；不要在当前会话继续运营。
3. 只通过 Agent 平台支持的 Skill 安装器或干净的受管仓库更新副本，然后开启新会话重新读取。
4. 若目标是 symlink、dirty Git 工作区、未受管文件，或无法证明更新不会覆盖用户修改：不要自动替换，交给维护者。
5. 网络、HTTP、JSON、URL、摘要或字节数任一项不可验证：失败关闭；不要使用旧副本或飞书文档兜底。

本地未发布修改只称“候选”，不能称“线上规则”。Windows 必须保持本文件 LF；不要让 `core.autocrlf` 改变摘要。

## 2. 角色、类型和不可变规则

### 2.1 角色

| 角色 | 职责 |
|---|---|
| 运营 Agent / 人类编辑 | 选题、事实核验、写入、查看发布结果；使用 editor 身份 |
| 可信 Worker | 校验通道、Schema、新鲜度、ETag、冲突、digest，生成不可变 revision 并排队 |
| 老板 | 决定内容政策与高风险例外；不逐条批准普通内容 |
| Claude/维护者 | 代码、schema、Skill、CI、事故与严格回滚；不重复正常内容审阅 |

不要把 reviewer/approver/publisher token、actor、role 或 open_id 注入普通 Agent、定时任务、内容或群消息。

### 2.2 type 与 channel

`type` 是条目集合，`channel` 是变更单通道。严格使用：

| 内容 | CLI `--type` | `create-change --channel` | 允许修改 | 默认发布路径 |
|---|---|---|---|---|
| 产品 | `tools` | `product` | tools | 自动 content revision → production |
| 资产 | `assets` | `asset` | assets | 自动 content revision → production |
| 资讯 | `news` | `news` | news；仅可为关联的既有 tool 追加 changelog | 自动 content revision → production |
| 博客 | `blog` | `blog` | blog | 自动 content revision → production |
| 维护 | 对应集合 | `maintenance` | 四类内容 | 职责分离严格路径 |

资产是 `assets / asset`：`assets` 是 type，`asset` 是 channel。不要用 `product` 或 `assets` 作为资产 channel。`submit-entry` 自动映射 `tools→product`、`assets→asset`、`news→news`、`blog→blog`。

### 2.3 四条铁律

1. 先完成内容事实核验，再让服务端执行 schema、新鲜度、通道隔离和安全门禁；校验失败不得绕过。
2. 普通 `tools/assets/news/blog` 保存后生成完整不可变 revision 并直接排队 production；代码、Schema、基础设施和回滚仍走严格工程流程。
3. 不可变 revision 与部署任务绑定完整 64 位 digest；只有线上 production 验收成功才算上线。
4. 所有写请求使用稳定 `Idempotency-Key`；状态写入同时使用最新完整 ETag，禁止 `If-Match: *`。不同条目并发自动合并，同一条目冲突必须人工处理。

## 3. 创建、批量和并发

### 3.1 形成 content revision

单条优先：

```bash
npm run ops -- submit-entry --type <tools|assets|news|blog> --slug <slug> \
  --file <entry.json> --request-id <稳定ID>
```

多条或逐步编辑：

```bash
npm run ops -- create-change --channel <product|asset|news|blog|maintenance> \
  --title "<标题>" --request-id <create-稳定ID>
npm run ops -- put --change-set <cs_xxx> --type <type> --slug <slug> \
  --file <entry.json> --if-match '<最新完整ETag>' --request-id <put-稳定ID>
npm run ops -- publish --change-set <cs_xxx> --if-match '<最新完整ETag>' \
  --request-id <publish-稳定ID>
npm run ops -- status --change-set <cs_xxx>
```

每次从响应 JSON 读取顶层 `_etag` 并原样传给下一次写入；不要用正则截 stdout，也不要去掉 ETag 双引号。`publish` 后 revision 冻结并进入后台队列；后续修改创建新变更单和新 digest。

### 3.2 批量规则

- 一个批次只能对应一个 channel、一个 change set 和一个 revision。
- 同类型多条内容可聚合以减少部署次数：例如 61 个 GitHub 项目放入一个 `asset` change set；61 个产品放入一个 `product` change set。系统允许同一 change set 含多个不同 slug。
- 不同条目可并发提交，服务端会重放到当前 revision head 并按前序串行部署；同一 `type/slug` 从草稿基线后被修改则返回 `content_conflict`，绝不覆盖。
- 不要用 `maintenance` 绕过 channel 限制；普通四类内容继续使用各自 channel。
- Windows/Git Bash 批量脚本使用 `execFileSync('node', ['scripts/ops.mjs', ...args])` 参数数组，不拼 shell 字符串。

### 3.3 队列与失败

- `publish` 返回 `job.id/status` 只是已入队，不是已上线；轮询到 `succeeded` 并核生产页面才算完成。
- 前序失败时，依赖它的后续 revision 会被标记失败，不会永久卡在 queued。
- 当前 MVP 不自动重试失败部署。修复原因后重新编辑/创建变更单，生成新 revision；不要复活终态 job。
- Worker 会用短发送租约、30 分钟回调窗口和每分钟重扫修复派发中断或 workflow 回调丢失；只会重投同一 `queued` job，不会复活终态部署，也不需要运营 Agent 介入。

## 4. 内容政策

### 4.1 资讯

只有以下五门都通过才写：与 AI/Agent 直接相关；达到重大事件阈值；信源新鲜；证据充分；站内有实质新信息。0 条是合法结果，不为班次数量降门槛。

可收：旗舰模型/产品正式发布或 GA、显著能力/价格变化、已宣布或完成且金额 ≥5000 万美元的融资并购、≥10 亿美元且满足高风险双源的洽谈、广泛影响的协议/平台政策、正式监管/判决、已确认重大安全事故、有可核实产物的重要研究或行业里程碑。不要收 coming soon、小修补、营销海报、单一匿名源传闻或不可复现榜单。

来源与风险：

- S1：官网、官方博客/X、GitHub release、模型仓库、论文、监管/法院/交易文件。
- S2：Reuters、Bloomberg、FT、WSJ、TechCrunch 等独立直接报道。
- S3：聚合站、搜索摘要、HN、中文二次媒体和社媒评论；只用于发现线索。
- R1 官方低风险事实：S1 即可；核标题、型号、时间、GA 与绝对词。
- R2 融资、并购、benchmark、事故、官方指标：S1 + S2，或 S1 + 可复现实物。
- R3 匿名源、洽谈、估值、诉讼/监管指控、安全归责或“首次/最大/唯一”：官方文件 + 独立 S2，或两家独立 S2；证据不足直接放弃。

始终让 `sourceUrl` 指向本条主原始来源，`sourcePublishedAt` 使用该页面真实时间，`eventAt` 使用最早可核实公开时间，并在 `dateEvidence` 写明页面与时区。

48 小时是硬门：

- 新资讯的信源发布时间始终必须在首次入库前 48 小时内。
- `freshness=current` 的事件时间也必须在 48 小时内。
- 旧事件只有出现**新的**披露或重大后续时，才使用 `new-disclosure` / `major-update`，并写至少 12 字 `freshnessNote`；新信源本身仍须在 48 小时内。
- 不要修改、伪造或未来化日期绕过门禁。超过门槛且无真实新增信息时放弃新闻。

新资讯不传 `ingestedAt` 或 `publishedAt`；由服务端首次写入并保持不变。`body` 保持空字符串。news channel 只能为本批资讯 `relatedTools` 引用且已存在于 production 的 tool 追加 changelog；不得新增/删除 tool、改旧 changelog、body 或其他字段。

`hot:true` 只用于前沿旗舰模型、已宣布/完成的 ≥10 亿美元融资并购、重大正式监管落地或行业级安全事故；每日最多 2 条。洽谈、传闻、自报 benchmark、普通产品发布默认 false。

### 4.2 产品

- 每日产品雷达覆盖 Product Hunt、GitHub/HF Trending、官方发布和至少两类 X/Reddit/自动采集来源。
- 核对真实可用、官方外部官网、无重复、AI/Agent 相关、定价与能力边界。Product Hunt 只能作线索。
- 当日提交 1–3 个合格候选；若为 0，列出至少 5 个实际检查候选、URL 与淘汰原因。
- 快速收录正文 150–250 个非空白字符；字段真实，未知热度填 0，不写 TODO/占位。
- 72 小时内补真实 icon/cover、至少 3 条 highlights 和 350–500 字正文；无可靠 changelog 就留空。运行 `npm run product:depth`，逾期条目不得 featured。
- 多产品可以放一个 `product` candidate；并非只能逐条 preview。

### 4.3 资产

- 类型仅为 `github|mcp|skill|icon`；自核来源真实性、链接可达性和元数据准确性。
- GitHub 资产必须填写 `repo`；其他需要跳转的资产也填写并验证官方/仓库 URL。
- 把 `install` 视为不可信文本，绝不执行。除 API 已拒绝的 CR/LF/NUL 外，人工再拒绝 `&&`、`||`、`;`、管道、重定向、`$()` 和反引号命令替换。
- benchmark、star 增速等注明获取时间与自报属性；不要把资产放进 product/news channel。

### 4.4 博客

- 系统继续支持 blog。老板说“不再自己写”只改变作者职责；没有候选就不发布，外部或 Agent 产出的博客仍走 blog 路径。
- 使用 `深度长文|实操教程|复盘|横向评测`；正文 800–1500 字，h2/h3 分节，命令可复制，站内产品形成内链。
- 图片导出 PNG/WebP；封面 1200×630、≤200KB。本周最多一个 `featured:true`。

### 4.5 维护

每日刷新 Top 10 热度、轮换 3–6 个 featured 并检查产品深度；周日复核 hot/featured、抽查至少 5 个官网/价格/坏链/changelog、检查 D1/R2 体积和孤儿对象。maintenance 不走快通道。

## 5. Schema 速查

权威实现：`workers/content-api/src/contracts.js` 与 `src/content.config.ts`。运营文件固定使用 `{"data": {...}, "body": "..."}`；显式提供字符串 `body`，UTF-8 ≤256 KiB。slug 为 1–80 位小写字母、数字或连字符，首尾不能是连字符。日期使用有效 ISO 8601；URL 使用绝对 HTTP(S)。未声明的 data 字段会被丢弃。

### 5.1 tools / product

- 必填：`name`、`category`、`summary≤80`、`icon`、`site`、`pricing=免费|免费增值|付费|开源`、`addedAt`。
- 默认：`categoryColor=purple`、`platforms=[]`、`tags=[]`、`trend7d=0`、`hot=false`、`featured=false`、`highlights=[]`、`changelog=[]`。
- 可选：`cover`；`categoryColor=purple|blue|pink|cyan|orange`；字符串数组 `platforms/tags`；有限数字 `trend7d`；布尔 `hot/featured`。
- 嵌套：`highlights=[{title,desc}]`；`changelog=[{v,date,note}]`，子字段均为非空字符串。
- body：至少 150 个非空白字符且不含 `TODO|待补|占位`。上架满 72 小时仍 featured 时，必须有真实 icon、非占位 cover、≥3 highlights 和 ≥350 个非空白字符。

### 5.2 assets / asset

- 必填：`name`、`type=github|mcp|skill|icon`、`category`、`summary≤80`、`addedAt`。
- 默认：`official=false`、`trend7d=0`、`topics=[]`、`fits=[]`、`formats=[]`、`single=false`。
- 可选：`icon`、`install≤500`、`repo`、`stars`（规范化为字符串）、`owner`、`language`、`forks`、`version`、`license`、`trend7d`。
- 类型字段：MCP 可用 `toolsCount`（≥0 整数）和 `transport=stdio|http|sse`；Skill 用 `version/fits`；Icon 用 `formats/license/single`。body 可为空。

### 5.3 news / news

- 新条目调用方必填：`title`、`source`、`sourceUrl`、`dateEvidence≤200`、`sourcePublishedAt`、`eventAt`、`freshness=current|new-disclosure|major-update`、`summary≤120`。
- 默认/可选：`hot=false`、`relatedTools=[]`、`freshnessNote≤160`。
- 不传：`ingestedAt`、`publishedAt`；服务端生成。历史兼容字段可空不代表新条目可以省略。
- `new-disclosure/major-update` 必须提供去空格后至少 12 字的 `freshnessNote`。body 为空。

### 5.4 blog / blog

- 必填：`title`、`kind=深度长文|实操教程|复盘|横向评测`、`minutes`（≥1 整数）、`publishedAt`、`summary≤120`。
- 默认/可选：`featured=false`、`cover` 可选。正文按 §4.4。
- 先用 `ops upload` 上传封面并原样保存响应 `media.url`；不要自拼 digest 路径，不要上传 SVG。

## 6. 发布

普通内容发布只有一条路径：`publish` 返回完整不可变 content revision 和一个 production job。读取并记录 `contentRevision.id/digest`、`job.id/status`，轮询 job；不要再创建 preview、提案或人工审批。

```bash
npm run ops -- publish --change-set <cs_xxx> \
  --if-match '<最新完整ETag>' --request-id <publish-稳定ID>
npm run ops -- job --job <job_xxx>
```

`queued` 只表示已排队；`succeeded` 后再核验生产站的 release ID/digest、页面内容、canonical 和无测试条/noindex。`failed/dispatch_failed` 为终态，记录错误并创建新 revision 重试。

下列 §6.1–6.3 仅用于已经进入旧严格流程的兼容批次，或维护者明确指定的严格操作；不得用于新建的普通内容变更。

### 6.1 旧 news/asset 快通道（兼容）

news 自核 flags：`--source-authenticity --date-freshness --factual-accuracy`。
asset 自核 flags：`--source-authenticity --link-reachability --metadata-accuracy`。

```bash
npm run ops -- publish-reviewed --channel <news|asset> --change-set <cs_xxx> \
  --digest <完整digest> <对应三项flags> --note "<12–500字证据>" \
  --if-match '<最新ETag>' --request-id <稳定ID>
npm run ops -- job --job <job_xxx>
```

执行：

1. 首次响应必须是 `target=staging` 且 `nextAction=wait_for_staging_then_call_publish_reviewed_again`。Worker canonical 群通知成功后才可建 job。
2. 等 staging `succeeded`；核测试条、noindex、canonical、release ID/digest，并核信源或资产链接。
3. 若启用 §3.3 hold，在此暂停。
4. 否则重新 status、换 request ID、使用同一 digest/checklist/note 再调用。
5. `target=production,nextAction=wait_for_production`：等 production；`target=staging` 且 nextAction 与首次相同：staging 被覆盖，等 re-stage 后重复；其他响应停止。
6. production 成功后核无测试条/noindex、生产 canonical、相同 digest 和 release ID。

快通道只取消默认人工等待，不取消 staging、canonical 通知、schema、新鲜度、资产 install 安全审查、ETag、幂等或在线验收。

### 6.2 旧 product/blog 老板验收（兼容）

```bash
npm run ops -- preview-release --change-set <cs_xxx> --digest <完整digest> \
  --if-match '<最新ETag>' --request-id <preview-稳定ID>
npm run ops -- job --job <job_xxx>

npm run ops -- propose-approval --change-set <cs_xxx> \
  --if-match '<staging成功后的最新ETag>' --request-id <propose-稳定ID>
```

首次 preview 必须返回 `target=staging,nextAction=wait_for_staging_then_request_owner_approval`。staging 成功并在线验收后才 propose。提案必须由 Worker 发送，包含 base release、candidate、完整 digest、canonical diff 与测试地址。

老板必须在允许名单内，直接回复未编辑的提案：`通过`。旧格式 `通过 <完整64位digest>` 仍兼容，且所带 digest 必须匹配。Agent 不得代发、转述或修改。下方命令的 `--digest` 是候选校验字段，不代表回复正文必须包含 digest。

```bash
npm run ops -- request-production-release --change-set <cs_xxx> \
  --digest <完整digest> --proposal-message-id <om_xxx> \
  --approval-message-id <om_xxx> --if-match '<最新ETag>' \
  --request-id <production-稳定ID>
npm run ops -- job --job <job_xxx>
```

- `target=production`：允许没有 `nextAction`，等待 production 并验收。
- `target=staging,nextAction=wait_for_staging_then_retry_request_production_release`：指针漂移；等同一 candidate re-stage，刷新 ETag、换 request ID，用同一消息证据重试。
- `propose-approval` 返回 `staging_required`：重新 preview 同一 digest、重新在线验收，再 propose；该错误本身不含可继续的 job。
- candidate/digest 或消息版本变化：旧提案与批准失效，重新 preview 和提案。

### 6.3 maintenance 与回滚（严格）

```bash
npm run ops -- approve --change-set <cs_xxx> --digest <完整digest> \
  --approval-message-id <om_xxx> --if-match '<ETag>' --request-id <approve-ID>
npm run ops -- release --change-set <cs_xxx> --target staging \
  --if-match '<ETag>' --request-id <staging-ID>
npm run ops -- release --change-set <cs_xxx> --target production \
  --if-match '<ETag>' --request-id <production-ID>
```

只让隔离的 reviewer/approver/publisher 执行；运营 Agent 只准备参数。所有构建必须从固定 release export 重算逐条和全量 SHA-256，校验 schema，且 staging/production 使用同一 digest；网络或 hash 不一致时禁止回退文件基线。

终态 job 不得复用。只有同一 HTTP 请求因网络超时而结果未知时复用原 request ID；新逻辑步骤使用新 ID。

## 7. 常见错误

| 错误/现象 | 唯一处理 |
|---|---|
| 未知 channel/type | 使用 `tools/product`、`assets/asset`、`news/news`、`blog/blog` |
| `channel_type_mismatch` | 停止；新建正确 channel，不在原单夹带 |
| `workflow_channel_mismatch` | news/assets 用 publish-reviewed；product/blog 用 preview/owner path |
| `validation_failed` | 按 `details.path` 修字段；不删门禁字段 |
| `if_match_required` | status 后原样传完整 ETag |
| `etag_mismatch` / 412 | 停止写入，重新 status、检查 revision diff，再决定 |
| 其他 409 | 按具体状态/证据/幂等错误停止；不要当成 ETag 冲突 |
| `idempotency_conflict` | 新逻辑请求换 ID；未知结果的同一请求才复用旧 ID |
| `change_set_immutable` | 新建 revision/变更单 |
| `content_conflict` | 当前 production/队列中同一 `type/slug` 已变化；读取现值，人工合并后建新变更单 |
| `content_revision_race` | 另一 revision 先占用后继位置；原草稿未提交，刷新状态后以新 request ID 重试 publish |
| `news_*_stale` | 不改日期；仅真实新披露/重大后续按 §4.1 处理 |
| `news_channel_scope/news_fast_path_scope` | 恢复 production 原值，只保留允许的相关 tool changelog 纯追加 |
| `self_review_checks_required` | 使用该 channel 对应的三项 flags，且都为 true |
| `candidate_digest_mismatch` | status 核 candidate/digest；内容变化则重新 submit |
| `release_in_progress` | 严格发布/回滚与自动队列冲突；等当前自动队列终态 |
| `candidate_base_stale` | 从当前 production 新建 candidate；优先聚合同批条目 |
| `environment_pointer_changed` | 原 job 不可复活；按流程新建 release job |
| `staging_required` | 按 §6.2 重新 preview/验收；不要凭旧截图继续 |
| `approval_*/proposal_*/preview_notice_required` | 回查原消息；证据变化则重新提案 |
| `lark_unavailable/*_misconfigured` | 报基础设施故障；不要用 Agent 消息替代 Worker |
| job 失败/超时或未知 nextAction | 立即停止，记录 job/error；修根因后用新 request ID |
| media/payload 失败 | 压缩或拆分；仅 PNG/JPEG/WebP/GIF，扩展名/MIME/签名一致 |

## 8. 验收、日报和事故

普通内容不切换共享 staging 指针；GitHub workflow 会用隔离 preview 做机器验收。生产环境 `https://agentopia.cc` 不得出现测试条/noindex/测试 canonical，且 release ID/digest 必须匹配。旧严格流程的测试环境仍为 `https://aiwatch-cqr.pages.dev`。

不要把 API 提交成功、change set approved、测试站可访问或 job pending 当成生产成功。以 production 在线验收和审计记录为准。

日报：

```text
[Agentopia·日报 YYYY-MM-DD]
资讯：+N（R1/R2/R3；hot N）｜产品：+N｜资产：+N｜博客：+N
未收资讯/产品候选：<代表候选与未过门原因>
数据维护：热度/featured/hot/产品深度逾期
变更单：<cs>｜revision：<rel>｜digest：<64位>｜状态：draft/queued/production/failed
异常与阻塞：<无或具体事项>
明日计划：
```

部署失败或内容需改：创建新 revision/digest；终态 job 不得复用。

生产内容错误时，由维护者查询上一个已验证 production release 并执行：

```bash
npm run ops -- rollback --release <上一个production-release-id> \
  --reason "<错误与影响>" --if-match '<当前生产ETag>' --request-id <rollback-ID>
```

回滚创建新的可审计 release，不删历史。资讯事实错误先下线/回滚，再在群里说明错误、影响、来源与改进。域名/证书异常时保持当前生产项目，不用测试项目覆盖生产。
