v0.1.1: Gitea transport surface de-MCP — REST+swagger sole canon (adapter recipes, terminology map, review probes)

This commit is contained in:
octopus
2026-09-15 09:59:04 +08:00
parent bb35e661b2
commit 18ffce5d29
15 changed files with 202 additions and 136 deletions
+101 -65
View File
@@ -2,34 +2,37 @@
> Gitea adapter 参考实现(Increment 3,自 dogfood 源
> `_shared/gitea-write-patterns.md` 拆分上提)。本目录承载后端绑定的
> API 形态(MCP 工具名、REST 端点、curl 形态);页名规范与寻址语义
> API 形态(REST 端点 + swagger 契约,curl 形态);页名规范与寻址语义
> 是后端中立契约,见 `core/rules/artifact-addressing.md`(本文不重复)。
> 实例基址由实例配置提供(下文 `<gitea-base-url>`),见
> `core/adapters/TERMINOLOGY.md`。
> `core/adapters/TERMINOLOGY.md`。认证一律 `-H "Authorization: token <token>"`
> 端点契约以 `<gitea-base-url>/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。** `gitea_wiki__create_page` /
`gitea_wiki__get_page` / `gitea_wiki__list_pages` 响应中的 `html_url`
字段是唯一权威链接,发布时捕获并原样复用。`page_name``html_url`
的变换不可推导(`/``%2F`、含斜杠页名带 `.-` 尾缀、CJK 百分号编码),
必须读 API。
**黄金规则:绝不手工拼接 wiki URL。** `POST .../wiki/new`
`GET .../wiki/page/{mangled-name}``GET .../wiki/pages` 响应中的
`html_url` 字段是唯一权威链接,发布时捕获并原样复用。页名 →
`html_url` 的变换不可推导(`/``%2F`、含斜杠页名带 `.-` 尾缀、CJK
百分号编码),必须读 API。
**两种标识符勿混淆**
| 标识符 | 是什么 | 用途 |
| ----------- | ------------------------------------ | ---------------------------------------- |
| `page_name` | 原始页标识;字面 `/`、无主机、无编码 | wiki MCP 工具的 `page_name`/`title` 参数 |
| `html_url` | 后端生成的完整可点击 URL | markdown 链接、`target_url`、PR 正文 |
| 标识符 | 是什么 | 用途 |
| ------ | ------ | ---- |
| 逻辑页名(`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})``。链接文本供读侧
`gitea_wiki__get_page`href 供人点击(见 Pattern 10)。
``[`{page_name}`]({html_url})``。链接文本供读侧回读该页,href 供人
点击(见 Pattern 10)。
- **commit-status `target_url`** — 终报页的 `html_url`(见 Pattern 8)。
- **页内交叉链接** — 用 `html_url`。
@@ -37,38 +40,56 @@ Owner/repo 固定为 `Octopus/octopus`。所有 wiki 页名遵循 Core 契约
## Pattern 1: create-wiki-page
```bash
curl -fsS -X POST "<gitea-base-url>/api/v1/repos/Octopus/octopus/wiki/new" \
-H "Authorization: token <token>" -H "Content-Type: application/json" \
-d '{
"title": "{slug}/{type}-{seq:02d}-{title}",
"content_base64": "<base64(内容)>",
"message": "{可选 commit message}"
}'
```
gitea_wiki__create_page(owner="Octopus", repo="octopus",
title="{slug}/{type}-{seq:02d}-{title}",
content="{内容}",
message="{可选 commit message}")
```
**base64 陷阱(强制注明)**wiki 写接口只认 `content_base64`;传
`content` 会被静默忽略(无报错、返回 2xx,页面存成 0 字节)。正文必须
先 base64 编码,且发布后回读确认非空。
**发布→验证(强制)**:发布后立刻回读确认存在且内容一致:
```
gitea_wiki__get_page(owner="Octopus", repo="octopus", page_name="{同 title}")
```bash
# 1. 列页拿 mangled sub_url(绝不手工拼 mangled name
curl -fsS -H "Authorization: token <token>" \
"<gitea-base-url>/api/v1/repos/Octopus/octopus/wiki/pages"
# 2. 用返回的 sub_url 原样读页
curl -fsS -H "Authorization: token <token>" \
"<gitea-base-url>/api/v1/repos/Octopus/octopus/wiki/page/{sub_url}"
```
404 / 内容不一致 → 修复后重发。页名冲突(409)→ 该页已存在,改用
Pattern 2 update,绝不另发新页。响应的 `html_url` 立即捕获复用。
404 / 内容为空或不一致 → 修复后重发。页名冲突(409)→ 该页已存在,
改用 Pattern 2 update,绝不另发新页。响应的 `html_url` 立即捕获复用。
## Pattern 2: update-wiki-page
```
gitea_wiki__update_page(owner="Octopus", repo="octopus",
page_name="{页名}",
content="{新内容}",
message="{commit message}")
```bash
curl -fsS -X PATCH "<gitea-base-url>/api/v1/repos/Octopus/octopus/wiki/page/{sub_url}" \
-H "Authorization: token <token>" -H "Content-Type: application/json" \
-d '{
"content_base64": "<base64(新内容)>",
"message": "{commit message}"
}'
```
更新后再回读验证;409 冲突 → 拉最新内容手工合并后重试
路径参数用 mangled `sub_url`(先 `GET .../wiki/pages` 获取,原样使用)
**省略 `title` 保持页名不变**——只发 `content_base64`+`message`。
同样只认 `content_base64``content` 会静默存 0 字节页)。更新后再回读
验证;409 冲突 → 拉最新内容手工合并后重试。
## Pattern 3: create-issue
```
gitea_issue__create(owner="Octopus", repo="octopus",
title="{标题}", body="{正文}", labels=["{label}"])
```bash
curl -fsS -X POST "<gitea-base-url>/api/v1/repos/Octopus/octopus/issues" \
-H "Authorization: token <token>" -H "Content-Type: application/json" \
-d '{"title":"{标题}","body":"{正文}","labels":[{label_id}]}'
```
### 工单交叉链接(强制)
@@ -86,9 +107,10 @@ gitea_issue__create(owner="Octopus", repo="octopus",
## Pattern 4: update-issue
```
gitea_issue__update(owner="Octopus", repo="octopus",
index={issue_number}, body="{正文}", state="{open|closed}", ...)
```bash
curl -fsS -X PATCH "<gitea-base-url>/api/v1/repos/Octopus/octopus/issues/{index}" \
-H "Authorization: token <token>" -H "Content-Type: application/json" \
-d '{"body":"{正文}","state":"{open|closed}"}'
```
原位更新正文(checklist 勾选、live 状态表维护);关闭工单即触发
@@ -96,9 +118,10 @@ gitea_issue__update(owner="Octopus", repo="octopus",
## Pattern 5: add-issue-comment
```
gitea_issue_comment__create(owner="Octopus", repo="octopus",
index={issue_number}, body="{评论正文}")
```bash
curl -fsS -X POST "<gitea-base-url>/api/v1/repos/Octopus/octopus/issues/{index}/comments" \
-H "Authorization: token <token>" -H "Content-Type: application/json" \
-d '{"body":"{评论正文}"}'
```
首次评论后捕获返回的 `comment_id`——后续对同一逻辑评论的更新必须走
@@ -107,33 +130,42 @@ Pattern 6 原位 edit,绝不再 create。用于:评审综合(Synthesis
## Pattern 6: edit-issue-comment
```
gitea_issue_comment__edit(owner="Octopus", repo="octopus",
comment_id={comment_id}, body="{新正文}")
```bash
curl -fsS -X PATCH "<gitea-base-url>/api/v1/repos/Octopus/octopus/issues/comments/{comment_id}" \
-H "Authorization: token <token>" -H "Content-Type: application/json" \
-d '{"body":"{新正文}"}'
```
单评论聚合不变量(工件索引、当前状态表等)的执行手段。
## Pattern 7: move-issue-to-column
```
gitea_column__move_issue(owner="Octopus", repo="octopus",
project_id={project_id}, column_id={column_id}, index={issue_number})
```
看板列迁移(Todo → In Progress → Review → Done)。projects/columns
发现用 REST;「把工单移到列」的操作端点语义复杂,经 swagger 查询后
调用,不臆造路径:
看板列迁移(Todo → In Progress → Review → Done)。
```bash
# 列出 repo 级 projectsprojects 仅 repo 级,无 org/user 级端点)
curl -fsS -H "Authorization: token <token>" \
"<gitea-base-url>/api/v1/repos/Octopus/octopus/projects"
# 列某 project 的 columns
curl -fsS -H "Authorization: token <token>" \
"<gitea-base-url>/api/v1/repos/Octopus/octopus/projects/{project_id}/columns"
# 移动工单到列:先经 swagger.v1.json 查询 projects 相关端点后调用
curl -s <gitea-base-url>/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 同款
`gitea_column__move_issue`column 由 `gitea_project__list` /
`gitea_column__list` 发现。
非阶段阻塞项)。列序列按管线阶段定义;移动 Pattern 7 同款
projects/columns 经 REST + swagger 发现,同上)。
## Pattern 8: post-commit-statusREST 回退)
## Pattern 8: post-commit-status
MCP 工具缺席时用 REST 直发 commit status(评审综合的 Tier-2 落点):
commit status 直发 REST(评审综合的 Tier-2 落点):
```bash
curl -X POST "<gitea-base-url>/api/v1/repos/Octopus/octopus/statuses/{sha}" \
@@ -153,16 +185,19 @@ merge 前读回验证:`GET /commits/{PR_SHA}/status`。
## Pattern 9: create-iteration-board
```
gitea_project__create(owner="Octopus", repo="octopus",
title="{slug} — Iteration {N}", description="…")
gitea_column__create(owner="Octopus", repo="octopus",
project_id={project_id}, title="Todo")
# In Progress / Review / Done 同款
```bash
# 建 projectboard):先经 swagger 查询 create 端点契约
curl -s <gitea-base-url>/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 建列
```
DAG 聚合 agent 在单门 PASS 后建板;工单正文模板带 `## Node Reference`
(指向 `{epic-slug}/dag`)、`## Acceptance Criteria`、`## Parent`。
projects 仅 repo 级。DAG 聚合 agent 在单门 PASS 后建板;工单正文模板带
`## Node Reference`(指向 `{epic-slug}/dag`)、`## Acceptance Criteria`、
`## Parent`。
## Pattern 10: artifact-index(工单 ↔ 工件索引)
@@ -170,14 +205,15 @@ DAG 聚合 agent 在单门 PASS 后建板;工单正文模板带 `## Node Refer
索引(反向链接 + compaction 恢复主路径;语义不变量见
`core/rules/artifact-addressing.md` §4):
```
```bash
# 1. 找源工单(PR body / commit 的 Closes #N,或 DAG 父映射);无则跳过
# 2. 评论已存在?
gitea_issue_comment__list(owner="Octopus", repo="octopus", index={issue_number})
curl -fsS -H "Authorization: token <token>" \
"<gitea-base-url>/api/v1/repos/Octopus/octopus/issues/{index}/comments"
# → 扫 body 以 "## 工件索引" 开头的评论(遗留前缀 "## Pipeline 工件追踪表"
# 原位升级,不重复发)
# 3a. 不存在 → gitea_issue_comment__create 初始化
# 3b. 存在 → gitea_issue_comment__edit 原位编辑(复用 comment_id
# 3a. 不存在 → POST .../issues/{index}/comments 初始化
# 3b. 存在 → PATCH .../issues/comments/{comment_id} 原位编辑(复用 comment_id
```
**索引表模板**(每工件一行;技能只增改自己的行,绝不删他技的行):
@@ -191,11 +227,11 @@ gitea_issue_comment__list(owner="Octopus", repo="octopus", index={issue_number})
| DAG | 任务图 | v1 (frozen) | [`{epic-slug}/dag`]({html_url}) | CORE |
**位置列填充规则**:单元格 = markdown 链接 ``[`{page_name}`]({html_url})``
链接文本(page_name,字面 `/`)供读侧 `gitea_wiki__get_page`href
(html_url)供人点击,必须取自 API 响应,严禁拼接。
链接文本(逻辑页名,字面 `/`)供读侧 `GET .../wiki/pages` 拿 `sub_url`
后回读该页;href(html_url)供人点击,必须取自 API 响应,严禁拼接。
**重读优先级**`CORE` = compaction 后必读(重读集 = 全部 CORE 行);
`ON-Demand` → `ON-DEMAND` = 按需;`ARCHIVE` = 已归档不读。
`ON-DEMAND` = 按需;`ARCHIVE` = 已归档不读。
**归档动作(archive-at-close**:工单关闭时由关闭方 agent 原位 edit
本评论——表格上方加归档横幅(`> **状态**: ✅ 已归档 — issue #{N} 关闭于
+34 -13
View File
@@ -12,7 +12,12 @@ Owner/repo 固定 `Octopus/octopus`。compaction 恢复从 Pattern 6
## Pattern 1: read-wiki-page
```
gitea_wiki__get_page(owner="Octopus", repo="octopus", page_name="{slug}/{page_title}")
# 1. 列 wiki 页拿 mangled sub_url(绝不手工拼 mangled name
curl -fsS -H "Authorization: token <token>" \
"<gitea-base-url>/api/v1/repos/Octopus/octopus/wiki/pages"
# 2. 用返回的 sub_url 原样读页(含 %2F 编码与 .- 尾缀)
curl -fsS -H "Authorization: token <token>" \
"<gitea-base-url>/api/v1/repos/Octopus/octopus/wiki/page/{mangled-name}"
```
常用页名(全量文法 + 例外枚举见 `core/rules/artifact-addressing.md` §2):
@@ -45,7 +50,7 @@ gitea_wiki__get_page(owner="Octopus", repo="octopus", page_name="{slug}/{page_ti
`{slug}/roadmap/{page}``{slug}/shared/{file}`
`{slug}/reviews/{roadmap|design-space|plan}/final/report`
**本地回退(wiki MCP 工具不可用**:读本地 wiki 克隆(实例路径见
**本地回退(REST 不可达时**:读本地 wiki 克隆(实例路径见
TERMINOLOGY.md)。wiki 文件名 URL 编码(`/``%2F``.md` 前可能带
`.-`),用 glob 而非固定路径解析:
@@ -56,7 +61,8 @@ glob(pattern="*{slug}*{page-title}*.md", path="<local-wiki-clone>/{repo}-wiki/")
## Pattern 2: read-issue
```
gitea_issue__get(owner="Octopus", repo="octopus", index={issue_number})
curl -fsS -H "Authorization: token <token>" \
"<gitea-base-url>/api/v1/repos/Octopus/octopus/issues/{issue_number}"
```
读工单详情、正文、元数据(label、assignee、milestone、state)。
@@ -64,20 +70,28 @@ gitea_issue__get(owner="Octopus", repo="octopus", index={issue_number})
## Pattern 3: read-issue-comments
```
gitea_issue_comment__list(owner="Octopus", repo="octopus", index={issue_number})
curl -fsS -H "Authorization: token <token>" \
"<gitea-base-url>/api/v1/repos/Octopus/octopus/issues/{issue_number}/comments?page=N&limit=50"
```
分页用 `page`/`limit`(上限 50),响应头 `x-total-count` 给总数。
读工单全部评论(评审结果、状态备注、讨论线程)。
## Pattern 4: read-project-column-issues
看板列工单队列。project/column 发现:
```
gitea_column__list_issues(owner="Octopus", repo="octopus",
project_id={project_id}, column_id={column_id})
curl -fsS -H "Authorization: token <token>" \
"<gitea-base-url>/api/v1/repos/Octopus/octopus/projects"
curl -fsS -H "Authorization: token <token>" \
"<gitea-base-url>/api/v1/repos/Octopus/octopus/projects/{project_id}/columns"
```
看板列工单队列。project/column 发现:`gitea_project__list` +
`gitea_column__list`
「把工单移到列」等复杂操作:经 `swagger.v1.json`
`jq '.paths | keys[]' | grep projects`)查询 projects 相关端点后调用,
不要臆造具体端点路径。projects 仅 repo 级(无 org/user 级端点)。
## Pattern 5: read-review-statusCLI
@@ -103,17 +117,24 @@ success1 = 有 failure/error。
而非猜测页名路径:
```
gitea_issue__get(owner="Octopus", repo="octopus", index={issue_number})
gitea_issue_comment__list(owner="Octopus", repo="octopus", index={issue_number})
# 找 body 以 "## 工件索引"(遗留 "## Pipeline 工件追踪表")开头的评论
gitea_wiki__get_page(owner="Octopus", repo="octopus", page_name=page_name)
# 1. 读源工单
curl -fsS -H "Authorization: token <token>" \
"<gitea-base-url>/api/v1/repos/Octopus/octopus/issues/{issue_number}"
# 2. 拉评论列表,找 body 以 "## 工件索引"(遗留 "## Pipeline 工件追踪表")开头的评论
curl -fsS -H "Authorization: token <token>" \
"<gitea-base-url>/api/v1/repos/Octopus/octopus/issues/{issue_number}/comments?page=N&limit=50"
# 3. 按 CORE 行位置列逐个读工件页:先 GET .../wiki/pages 拿 sub_urlmangled
# name),再原样请求
curl -fsS -H "Authorization: token <token>" \
"<gitea-base-url>/api/v1/repos/Octopus/octopus/wiki/page/{mangled-name}"
```
**位置列提取规则**:单元格形如 ``[`{page_name}`]({html_url})`` ——
取**链接文本**(首个 `[` 与 `]` 之间、剥反引号)作为 page_name,不是
href(含 `%2F`/`.-`API 不认)。裸 page_name 单元格(无链接包裹)也
接受:整格即 page_name。形如 `#comment-{id}` 的值改用
`gitea_issue_comment__list` 读,不得喂给 `gitea_wiki__get_page`。
`GET .../issues/{index}/comments` 拉工单评论列表后按 id 取,不得当作
wiki 页名去读 wiki 端点。
**重读规则**:重读集 = 全部 `CORE` 行;`ON-DEMAND` 行仅当前任务需要
时读;绝不读 `ARCHIVE` 行。索引缺失(历史工单)→ 回退 issue body +