# Gitea Adapter — 写模式(Write Patterns) > Gitea adapter 参考实现(Increment 3,自 dogfood 源 > `_shared/gitea-write-patterns.md` 拆分上提)。本目录承载后端绑定的 > API 形态(REST 端点 + swagger 契约,curl 形态);页名规范与寻址语义 > 是后端中立契约,见 `core/rules/artifact-addressing.md`(本文不重复)。 > 实例基址由实例配置提供(下文 ``),见 > `core/adapters/TERMINOLOGY.md`。认证一律 `-H "Authorization: token "`; > 端点契约以 `/swagger.v1.json` 为唯一正典(按需 jq 提取, > 见 `core/skills/gitea-rest/`)。 Owner/repo 固定为 `Octopus/octopus`。所有 wiki 页名遵循 Core 契约 `{slug}/{type}-{seq:02d}-{title}`(例外页全枚举见 artifact-addressing.md)。 ## Wiki URL 构造 — html_url 规则 **黄金规则:绝不手工拼接 wiki URL。** `POST .../wiki/new`、 `GET .../wiki/page/{mangled-name}`、`GET .../wiki/pages` 响应中的 `html_url` 字段是唯一权威链接,发布时捕获并原样复用。页名 → `html_url` 的变换不可推导(`/`→`%2F`、含斜杠页名带 `.-` 尾缀、CJK 百分号编码),必须读 API。 **两种标识符勿混淆**: | 标识符 | 是什么 | 用途 | | ------ | ------ | ---- | | 逻辑页名(`title`) | 原始页标识;字面 `/`、无主机、无编码 | 写侧:`POST .../wiki/new` 的 `title`;`PATCH` 省略 `title` 保名 | | mangled `pageName`(`sub_url`) | 后端改写的存储名(含 `%2F` 编码与 `.-` 尾缀) | 读侧/改侧路径参数:先 `GET .../wiki/pages` 拿 `sub_url`,**原样**用于 `wiki/page/{pageName}`,绝不手工拼 | | `html_url` | 后端生成的完整可点击 URL | markdown 链接、`target_url`、PR 正文 | **去向**: - **工件索引位置列** — 一个单元格同时存两者: ``[`{page_name}`]({html_url})``。链接文本供读侧回读该页,href 供人 点击(见 Pattern 10)。 - **commit-status `target_url`** — 终报页的 `html_url`(见 Pattern 8)。 - **页内交叉链接** — 用 `html_url`。 无 API 响应可用时(静态源串)用 ``,且仅此一处来源。 ## Pattern 1: create-wiki-page ```bash curl -fsS -X POST "/api/v1/repos/Octopus/octopus/wiki/new" \ -H "Authorization: token " -H "Content-Type: application/json" \ -d '{ "title": "{slug}/{type}-{seq:02d}-{title}", "content_base64": "", "message": "{可选 commit message}" }' ``` **base64 陷阱(强制注明)**:wiki 写接口只认 `content_base64`;传 `content` 会被静默忽略(无报错、返回 2xx,页面存成 0 字节)。正文必须 先 base64 编码,且发布后回读确认非空。 **发布→验证(强制)**:发布后立刻回读确认存在且内容一致: ```bash # 1. 列页拿 mangled sub_url(绝不手工拼 mangled name) curl -fsS -H "Authorization: token " \ "/api/v1/repos/Octopus/octopus/wiki/pages" # 2. 用返回的 sub_url 原样读页 curl -fsS -H "Authorization: token " \ "/api/v1/repos/Octopus/octopus/wiki/page/{sub_url}" ``` 404 / 内容为空或不一致 → 修复后重发。页名冲突(409)→ 该页已存在, 改用 Pattern 2 update,绝不另发新页。响应的 `html_url` 立即捕获复用。 ## Pattern 2: update-wiki-page ```bash curl -fsS -X PATCH "/api/v1/repos/Octopus/octopus/wiki/page/{sub_url}" \ -H "Authorization: token " -H "Content-Type: application/json" \ -d '{ "content_base64": "", "message": "{commit message}" }' ``` 路径参数用 mangled `sub_url`(先 `GET .../wiki/pages` 获取,原样使用)。 **省略 `title` 保持页名不变**——只发 `content_base64`+`message`。 同样只认 `content_base64`(`content` 会静默存 0 字节页)。更新后再回读 验证;409 冲突 → 拉最新内容手工合并后重试。 ## Pattern 3: create-issue ```bash curl -fsS -X POST "/api/v1/repos/Octopus/octopus/issues" \ -H "Authorization: token " -H "Content-Type: application/json" \ -d '{"title":"{标题}","body":"{正文}","labels":[{label_id}]}' ``` ### 工单交叉链接(强制) 父子工单组必须双向链接:父工单 task list 引用 `#`;子工单正文 带 `## 父级 / Parent` 节引用父 `#`。 ### 衍生工单创建 - 技术债(verify Phase 5.5):`TD-NNN` 经分配台账取号后升票,`## Parent` 指回登记册源工单。 - 基线失败(Phase 5.55):label `baseline-failure` + `BF-NNN`(族伞签, 按失败签名去重)。 - 不稳定测试(Phase 5.56):label `flaky-test` + `FT-NNN`(同上)。 ## Pattern 4: update-issue ```bash curl -fsS -X PATCH "/api/v1/repos/Octopus/octopus/issues/{index}" \ -H "Authorization: token " -H "Content-Type: application/json" \ -d '{"body":"{正文}","state":"{open|closed}"}' ``` 原位更新正文(checklist 勾选、live 状态表维护);关闭工单即触发 归档动作(见 artifact-addressing.md §4.3 + Pattern 10)。 ## Pattern 5: add-issue-comment ```bash curl -fsS -X POST "/api/v1/repos/Octopus/octopus/issues/{index}/comments" \ -H "Authorization: token " -H "Content-Type: application/json" \ -d '{"body":"{评论正文}"}' ``` 首次评论后捕获返回的 `comment_id`——后续对同一逻辑评论的更新必须走 Pattern 6 原位 edit,绝不再 create。用于:评审综合(Synthesis)、 状态备注、TD 登记、claim 认领。 ## Pattern 6: edit-issue-comment ```bash curl -fsS -X PATCH "/api/v1/repos/Octopus/octopus/issues/comments/{comment_id}" \ -H "Authorization: token " -H "Content-Type: application/json" \ -d '{"body":"{新正文}"}' ``` 单评论聚合不变量(工件索引、当前状态表等)的执行手段。 ## Pattern 7: move-issue-to-column 看板列迁移(Todo → In Progress → Review → Done)。projects/columns 发现用 REST;「把工单移到列」的操作端点语义复杂,经 swagger 查询后 调用,不臆造路径: ```bash # 列出 repo 级 projects(projects 仅 repo 级,无 org/user 级端点) curl -fsS -H "Authorization: token " \ "/api/v1/repos/Octopus/octopus/projects" # 列某 project 的 columns curl -fsS -H "Authorization: token " \ "/api/v1/repos/Octopus/octopus/projects/{project_id}/columns" # 移动工单到列:先经 swagger.v1.json 查询 projects 相关端点后调用 curl -s /swagger.v1.json -o /tmp/gitea-sw.json jq -r '.paths | keys[]' /tmp/gitea-sw.json | grep projects ``` ## Pattern 7.5: move-issue-to-pipeline-stage 管线阶段板列(Pipeline Stages board column)承载阶段迁移——阶段转移 落到板列,**不落** `## 当前状态` 行(该表只承载 PR / 评审 / CI 行与 非阶段阻塞项)。列序列按管线阶段定义;移动与 Pattern 7 同款 (projects/columns 经 REST + swagger 发现,同上)。 ## Pattern 8: post-commit-status commit status 直发 REST(评审综合的 Tier-2 落点): ```bash curl -X POST "/api/v1/repos/Octopus/octopus/statuses/{sha}" \ -H "Authorization: token " \ -H "Content-Type: application/json" \ -d '{ "context": "pipeline/review-{stage}", "state": "{success|failure|pending|error}", "target_url": "{html_url}", "description": "{≤140 chars 摘要}" }' ``` context 公式:`pipeline/review-{stage}`(`code` / `review-dag` / `audit-process`)。token 从实例配置读取(此处 `` 占位)。 merge 前读回验证:`GET /commits/{PR_SHA}/status`。 ## Pattern 9: create-iteration-board ```bash # 建 project(board):先经 swagger 查询 create 端点契约 curl -s /swagger.v1.json -o /tmp/gitea-sw.json jq '.paths["/repos/{owner}/{repo}/projects"].post' /tmp/gitea-sw.json # 随后 POST 建板(title="{slug} — Iteration {N}", description="…") # 再对每列(Todo / In Progress / Review / Done): jq '.paths["/repos/{owner}/{repo}/projects/{project_id}/columns"].post' /tmp/gitea-sw.json # 随后 POST 建列 ``` projects 仅 repo 级。DAG 聚合 agent 在单门 PASS 后建板;工单正文模板带 `## Node Reference`(指向 `{epic-slug}/dag`)、`## Acceptance Criteria`、 `## Parent`。 ## Pattern 10: artifact-index(工单 ↔ 工件索引) 技能发布工件后,在源工单维护 **`## 工件索引` 评论**——单一原位编辑的 索引(反向链接 + compaction 恢复主路径;语义不变量见 `core/rules/artifact-addressing.md` §4): ```bash # 1. 找源工单(PR body / commit 的 Closes #N,或 DAG 父映射);无则跳过 # 2. 评论已存在? curl -fsS -H "Authorization: token " \ "/api/v1/repos/Octopus/octopus/issues/{index}/comments" # → 扫 body 以 "## 工件索引" 开头的评论(遗留前缀 "## Pipeline 工件追踪表" # 原位升级,不重复发) # 3a. 不存在 → POST .../issues/{index}/comments 初始化 # 3b. 存在 → PATCH .../issues/comments/{comment_id} 原位编辑(复用 comment_id) ``` **索引表模板**(每工件一行;技能只增改自己的行,绝不删他技的行): ## 工件索引 slug: `{slug}` — source issue #{N} | 工件 | 类型 | 版本 | 位置 | 重读 | |------|------|------|------|------| | DAG | 任务图 | v1 (frozen) | [`{epic-slug}/dag`]({html_url}) | CORE | **位置列填充规则**:单元格 = markdown 链接 ``[`{page_name}`]({html_url})``; 链接文本(逻辑页名,字面 `/`)供读侧先 `GET .../wiki/pages` 拿 `sub_url` 后回读该页;href(html_url)供人点击,必须取自 API 响应,严禁拼接。 **重读优先级**:`CORE` = compaction 后必读(重读集 = 全部 CORE 行); `ON-DEMAND` = 按需;`ARCHIVE` = 已归档不读。 **归档动作(archive-at-close)**:工单关闭时由关闭方 agent 原位 edit 本评论——表格上方加归档横幅(`> **状态**: ✅ 已归档 — issue #{N} 关闭于 {date}`)+ 全部行 重读 置 `ARCHIVE`;不删行、不改位置列、不发第二条 评论。主路径 verify Phase 5.6;跳过 verify 的路由由关闭 agent 补执行。 **各技能行映射**: | 技能 | 工件 ID | 位置 | | ------------------------- | ---------------------------- | ------------------------------------------------------------ | | `analyze-dag` | `DAG` | `{epic-slug}/dag` | | `review-artifact` | `REVIEW-{stage}` | `{slug}/reviews/{stage}/final/report` | | `review-code` | `REVIEW-code` | `{slug}/reviews/code/final/report` | | `review-code`(DAG task) | `REVIEW-code-task-{node-id}` | `{epic-slug}/reviews/code/final/report-task-{node-id}` | | `verify` | `VERIFY-{N}` | `{slug}/05-verify-iteration-{N}` | | `verify`(milestone) | `VERIFY-M-{M-id}` | `{epic-slug}/05-verify-milestone-{M-id}`(重读 `ON-DEMAND`) | | `verify`(DAG task) | `VERIFY-TASK-{node-id}` | `{epic-slug}/05-verify-task-{node-id}`(重读 `ON-DEMAND`) |