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
+1 -1
View File
@@ -90,7 +90,7 @@ G0G4 批次已完成全量迁移(14 规则、17 技能整目录、\_shared
- **HARD**(全 core 生效,含 adapters):实例主机名、内网 IP 段、实例 - **HARD**(全 core 生效,含 adapters):实例主机名、内网 IP 段、实例
配置路径——实例机密与实例专有地址任何 core 文件不得出现。 配置路径——实例机密与实例专有地址任何 core 文件不得出现。
- **SOFT**`core/adapters/**` 豁免):`.octopus/``packages/octopus` - **SOFT**`core/adapters/**` 豁免):`.octopus/``packages/octopus`
MCP 工具名、工作树路径等实例绑定内容——adapter 目录是 Gitea 参考 后端绑定内容(REST 端点、工作树路径等——adapter 目录是 Gitea 参考
实现,允许承载;其余 core 文件不得出现。 实现,允许承载;其余 core 文件不得出现。
G0 三份规则是从实例规则**逐字复制**的,规则正文里出现实例路径字样属 G0 三份规则是从实例规则**逐字复制**的,规则正文里出现实例路径字样属
+4 -4
View File
@@ -9,10 +9,10 @@
| --- | --- | | --- | --- |
| 工单后端(ticket backend | Gitea | | 工单后端(ticket backend | Gitea |
| Tier-2 工件库 | Gitea wiki | | Tier-2 工件库 | Gitea wiki |
| 工单评论 APIticket backend 评论接口) | `gitea_issue_comment__*` | | 工单评论 APIticket backend 评论接口) | Gitea REST 评论端点族:`GET/POST /repos/{owner}/{repo}/issues/{index}/comments``PATCH /repos/{owner}/{repo}/issues/comments/{id}` |
| wiki 读写 APITier-2 工件库读写接口) | `gitea_wiki__*` | | wiki 读写 APITier-2 工件库读写接口) | Gitea REST wiki 端点族:`wiki/pages` GET、`wiki/page/{pageName}` GET/PATCH、`wiki/new` POST(路径前缀 `/api/v1/repos/{owner}/{repo}/` |
| 看板移动 API(工单看板列迁移接口) | `gitea_column__move_issue` | | 看板移动 API(工单看板列迁移接口) | Gitea REST projects 相关端点(经 `swagger.v1.json` 查询后调用,勿臆造路径) |
| 项目看板 API(工单面板管理接口) | `gitea_project__*` | | 项目看板 API(工单面板管理接口) | Gitea REST projects/columns 端点族:`GET /repos/{owner}/{repo}/projects``GET /repos/{owner}/{repo}/projects/{project_id}/columns` |
| 路由解析工具(Step 0 路由门依赖) | `route_resolver` | | 路由解析工具(Step 0 路由门依赖) | `route_resolver` |
| 会话工作树路径(并发分支工作区根) | `<worktrees-root>` | | 会话工作树路径(并发分支工作区根) | `<worktrees-root>` |
| 测试命令·变更集(本地前置检查) | `bun run test:changed` | | 测试命令·变更集(本地前置检查) | `bun run test:changed` |
+99 -63
View File
@@ -2,34 +2,37 @@
> Gitea adapter 参考实现(Increment 3,自 dogfood 源 > Gitea adapter 参考实现(Increment 3,自 dogfood 源
> `_shared/gitea-write-patterns.md` 拆分上提)。本目录承载后端绑定的 > `_shared/gitea-write-patterns.md` 拆分上提)。本目录承载后端绑定的
> API 形态(MCP 工具名、REST 端点、curl 形态);页名规范与寻址语义 > API 形态(REST 端点 + swagger 契约,curl 形态);页名规范与寻址语义
> 是后端中立契约,见 `core/rules/artifact-addressing.md`(本文不重复)。 > 是后端中立契约,见 `core/rules/artifact-addressing.md`(本文不重复)。
> 实例基址由实例配置提供(下文 `<gitea-base-url>`),见 > 实例基址由实例配置提供(下文 `<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 契约 Owner/repo 固定为 `Octopus/octopus`。所有 wiki 页名遵循 Core 契约
`{slug}/{type}-{seq:02d}-{title}`(例外页全枚举见 artifact-addressing.md)。 `{slug}/{type}-{seq:02d}-{title}`(例外页全枚举见 artifact-addressing.md)。
## Wiki URL 构造 — html_url 规则 ## Wiki URL 构造 — html_url 规则
**黄金规则:绝不手工拼接 wiki URL。** `gitea_wiki__create_page` / **黄金规则:绝不手工拼接 wiki URL。** `POST .../wiki/new`
`gitea_wiki__get_page` / `gitea_wiki__list_pages` 响应中的 `html_url` `GET .../wiki/page/{mangled-name}``GET .../wiki/pages` 响应中的
字段是唯一权威链接,发布时捕获并原样复用。`page_name``html_url` `html_url` 字段是唯一权威链接,发布时捕获并原样复用。页名 →
的变换不可推导(`/``%2F`、含斜杠页名带 `.-` 尾缀、CJK 百分号编码), `html_url` 的变换不可推导(`/``%2F`、含斜杠页名带 `.-` 尾缀、CJK
必须读 API。 百分号编码),必须读 API。
**两种标识符勿混淆** **两种标识符勿混淆**
| 标识符 | 是什么 | 用途 | | 标识符 | 是什么 | 用途 |
| ----------- | ------------------------------------ | ---------------------------------------- | | ------ | ------ | ---- |
| `page_name` | 原始页标识;字面 `/`、无主机、无编码 | wiki MCP 工具的 `page_name`/`title` 参数 | | 逻辑页名(`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 正文 | | `html_url` | 后端生成的完整可点击 URL | markdown 链接、`target_url`、PR 正文 |
**去向** **去向**
- **工件索引位置列** — 一个单元格同时存两者: - **工件索引位置列** — 一个单元格同时存两者:
``[`{page_name}`]({html_url})``。链接文本供读侧 ``[`{page_name}`]({html_url})``。链接文本供读侧回读该页,href 供人
`gitea_wiki__get_page`href 供人点击(见 Pattern 10)。 点击(见 Pattern 10)。
- **commit-status `target_url`** — 终报页的 `html_url`(见 Pattern 8)。 - **commit-status `target_url`** — 终报页的 `html_url`(见 Pattern 8)。
- **页内交叉链接** — 用 `html_url`。 - **页内交叉链接** — 用 `html_url`。
@@ -37,38 +40,56 @@ Owner/repo 固定为 `Octopus/octopus`。所有 wiki 页名遵循 Core 契约
## Pattern 1: create-wiki-page ## 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}", **base64 陷阱(强制注明)**wiki 写接口只认 `content_base64`;传
content="{内容}", `content` 会被静默忽略(无报错、返回 2xx,页面存成 0 字节)。正文必须
message="{可选 commit message}") 先 base64 编码,且发布后回读确认非空。
```
**发布→验证(强制)**:发布后立刻回读确认存在且内容一致: **发布→验证(强制)**:发布后立刻回读确认存在且内容一致:
``` ```bash
gitea_wiki__get_page(owner="Octopus", repo="octopus", page_name="{同 title}") # 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)→ 该页已存在,改用 404 / 内容为空或不一致 → 修复后重发。页名冲突(409)→ 该页已存在,
Pattern 2 update,绝不另发新页。响应的 `html_url` 立即捕获复用。 改用 Pattern 2 update,绝不另发新页。响应的 `html_url` 立即捕获复用。
## Pattern 2: update-wiki-page ## Pattern 2: update-wiki-page
``` ```bash
gitea_wiki__update_page(owner="Octopus", repo="octopus", curl -fsS -X PATCH "<gitea-base-url>/api/v1/repos/Octopus/octopus/wiki/page/{sub_url}" \
page_name="{页名}", -H "Authorization: token <token>" -H "Content-Type: application/json" \
content="{新内容}", -d '{
message="{commit message}") "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 ## Pattern 3: create-issue
``` ```bash
gitea_issue__create(owner="Octopus", repo="octopus", curl -fsS -X POST "<gitea-base-url>/api/v1/repos/Octopus/octopus/issues" \
title="{标题}", body="{正文}", labels=["{label}"]) -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 ## Pattern 4: update-issue
``` ```bash
gitea_issue__update(owner="Octopus", repo="octopus", curl -fsS -X PATCH "<gitea-base-url>/api/v1/repos/Octopus/octopus/issues/{index}" \
index={issue_number}, body="{正文}", state="{open|closed}", ...) -H "Authorization: token <token>" -H "Content-Type: application/json" \
-d '{"body":"{正文}","state":"{open|closed}"}'
``` ```
原位更新正文(checklist 勾选、live 状态表维护);关闭工单即触发 原位更新正文(checklist 勾选、live 状态表维护);关闭工单即触发
@@ -96,9 +118,10 @@ gitea_issue__update(owner="Octopus", repo="octopus",
## Pattern 5: add-issue-comment ## Pattern 5: add-issue-comment
``` ```bash
gitea_issue_comment__create(owner="Octopus", repo="octopus", curl -fsS -X POST "<gitea-base-url>/api/v1/repos/Octopus/octopus/issues/{index}/comments" \
index={issue_number}, body="{评论正文}") -H "Authorization: token <token>" -H "Content-Type: application/json" \
-d '{"body":"{评论正文}"}'
``` ```
首次评论后捕获返回的 `comment_id`——后续对同一逻辑评论的更新必须走 首次评论后捕获返回的 `comment_id`——后续对同一逻辑评论的更新必须走
@@ -107,33 +130,42 @@ Pattern 6 原位 edit,绝不再 create。用于:评审综合(Synthesis
## Pattern 6: edit-issue-comment ## Pattern 6: edit-issue-comment
``` ```bash
gitea_issue_comment__edit(owner="Octopus", repo="octopus", curl -fsS -X PATCH "<gitea-base-url>/api/v1/repos/Octopus/octopus/issues/comments/{comment_id}" \
comment_id={comment_id}, body="{新正文}") -H "Authorization: token <token>" -H "Content-Type: application/json" \
-d '{"body":"{新正文}"}'
``` ```
单评论聚合不变量(工件索引、当前状态表等)的执行手段。 单评论聚合不变量(工件索引、当前状态表等)的执行手段。
## Pattern 7: move-issue-to-column ## Pattern 7: move-issue-to-column
``` 看板列迁移(Todo → In Progress → Review → Done)。projects/columns
gitea_column__move_issue(owner="Octopus", repo="octopus", 发现用 REST;「把工单移到列」的操作端点语义复杂,经 swagger 查询后
project_id={project_id}, column_id={column_id}, index={issue_number}) 调用,不臆造路径:
```
看板列迁移(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 ## Pattern 7.5: move-issue-to-pipeline-stage
管线阶段板列(Pipeline Stages board column)承载阶段迁移——阶段转移 管线阶段板列(Pipeline Stages board column)承载阶段迁移——阶段转移
落到板列,**不落** `## 当前状态` 行(该表只承载 PR / 评审 / CI 行与 落到板列,**不落** `## 当前状态` 行(该表只承载 PR / 评审 / CI 行与
非阶段阻塞项)。列序列按管线阶段定义;移动 Pattern 7 同款 非阶段阻塞项)。列序列按管线阶段定义;移动 Pattern 7 同款
`gitea_column__move_issue`column 由 `gitea_project__list` / projects/columns 经 REST + swagger 发现,同上)。
`gitea_column__list` 发现。
## Pattern 8: post-commit-statusREST 回退) ## Pattern 8: post-commit-status
MCP 工具缺席时用 REST 直发 commit status(评审综合的 Tier-2 落点): commit status 直发 REST(评审综合的 Tier-2 落点):
```bash ```bash
curl -X POST "<gitea-base-url>/api/v1/repos/Octopus/octopus/statuses/{sha}" \ 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 ## Pattern 9: create-iteration-board
``` ```bash
gitea_project__create(owner="Octopus", repo="octopus", # 建 projectboard):先经 swagger 查询 create 端点契约
title="{slug} — Iteration {N}", description="…") curl -s <gitea-base-url>/swagger.v1.json -o /tmp/gitea-sw.json
gitea_column__create(owner="Octopus", repo="octopus", jq '.paths["/repos/{owner}/{repo}/projects"].post' /tmp/gitea-sw.json
project_id={project_id}, title="Todo") # 随后 POST 建板(title="{slug} — Iteration {N}", description="…"
# In Progress / Review / Done 同款 # 再对每列(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` projects 仅 repo 级。DAG 聚合 agent 在单门 PASS 后建板;工单正文模板带
(指向 `{epic-slug}/dag`)、`## Acceptance Criteria`、`## Parent`。 `## Node Reference`(指向 `{epic-slug}/dag`)、`## Acceptance Criteria`、
`## Parent`。
## Pattern 10: artifact-index(工单 ↔ 工件索引) ## Pattern 10: artifact-index(工单 ↔ 工件索引)
@@ -170,14 +205,15 @@ DAG 聚合 agent 在单门 PASS 后建板;工单正文模板带 `## Node Refer
索引(反向链接 + compaction 恢复主路径;语义不变量见 索引(反向链接 + compaction 恢复主路径;语义不变量见
`core/rules/artifact-addressing.md` §4): `core/rules/artifact-addressing.md` §4):
``` ```bash
# 1. 找源工单(PR body / commit 的 Closes #N,或 DAG 父映射);无则跳过 # 1. 找源工单(PR body / commit 的 Closes #N,或 DAG 父映射);无则跳过
# 2. 评论已存在? # 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 工件追踪表" # → 扫 body 以 "## 工件索引" 开头的评论(遗留前缀 "## Pipeline 工件追踪表"
# 原位升级,不重复发) # 原位升级,不重复发)
# 3a. 不存在 → gitea_issue_comment__create 初始化 # 3a. 不存在 → POST .../issues/{index}/comments 初始化
# 3b. 存在 → gitea_issue_comment__edit 原位编辑(复用 comment_id # 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 | | DAG | 任务图 | v1 (frozen) | [`{epic-slug}/dag`]({html_url}) | CORE |
**位置列填充规则**:单元格 = markdown 链接 ``[`{page_name}`]({html_url})`` **位置列填充规则**:单元格 = markdown 链接 ``[`{page_name}`]({html_url})``
链接文本(page_name,字面 `/`)供读侧 `gitea_wiki__get_page`href 链接文本(逻辑页名,字面 `/`)供读侧 `GET .../wiki/pages` 拿 `sub_url`
(html_url)供人点击,必须取自 API 响应,严禁拼接。 后回读该页;href(html_url)供人点击,必须取自 API 响应,严禁拼接。
**重读优先级**`CORE` = compaction 后必读(重读集 = 全部 CORE 行); **重读优先级**`CORE` = compaction 后必读(重读集 = 全部 CORE 行);
`ON-Demand` → `ON-DEMAND` = 按需;`ARCHIVE` = 已归档不读。 `ON-DEMAND` = 按需;`ARCHIVE` = 已归档不读。
**归档动作(archive-at-close**:工单关闭时由关闭方 agent 原位 edit **归档动作(archive-at-close**:工单关闭时由关闭方 agent 原位 edit
本评论——表格上方加归档横幅(`> **状态**: ✅ 已归档 — issue #{N} 关闭于 本评论——表格上方加归档横幅(`> **状态**: ✅ 已归档 — issue #{N} 关闭于
+34 -13
View File
@@ -12,7 +12,12 @@ Owner/repo 固定 `Octopus/octopus`。compaction 恢复从 Pattern 6
## Pattern 1: read-wiki-page ## 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): 常用页名(全量文法 + 例外枚举见 `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}/roadmap/{page}``{slug}/shared/{file}`
`{slug}/reviews/{roadmap|design-space|plan}/final/report` `{slug}/reviews/{roadmap|design-space|plan}/final/report`
**本地回退(wiki MCP 工具不可用**:读本地 wiki 克隆(实例路径见 **本地回退(REST 不可达时**:读本地 wiki 克隆(实例路径见
TERMINOLOGY.md)。wiki 文件名 URL 编码(`/``%2F``.md` 前可能带 TERMINOLOGY.md)。wiki 文件名 URL 编码(`/``%2F``.md` 前可能带
`.-`),用 glob 而非固定路径解析: `.-`),用 glob 而非固定路径解析:
@@ -56,7 +61,8 @@ glob(pattern="*{slug}*{page-title}*.md", path="<local-wiki-clone>/{repo}-wiki/")
## Pattern 2: read-issue ## 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)。 读工单详情、正文、元数据(label、assignee、milestone、state)。
@@ -64,20 +70,28 @@ gitea_issue__get(owner="Octopus", repo="octopus", index={issue_number})
## Pattern 3: read-issue-comments ## 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 ## Pattern 4: read-project-column-issues
看板列工单队列。project/column 发现:
``` ```
gitea_column__list_issues(owner="Octopus", repo="octopus", curl -fsS -H "Authorization: token <token>" \
project_id={project_id}, column_id={column_id}) "<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` + 「把工单移到列」等复杂操作:经 `swagger.v1.json`
`gitea_column__list` `jq '.paths | keys[]' | grep projects`)查询 projects 相关端点后调用,
不要臆造具体端点路径。projects 仅 repo 级(无 org/user 级端点)。
## Pattern 5: read-review-statusCLI ## Pattern 5: read-review-statusCLI
@@ -103,17 +117,24 @@ success1 = 有 failure/error。
而非猜测页名路径: 而非猜测页名路径:
``` ```
gitea_issue__get(owner="Octopus", repo="octopus", index={issue_number}) # 1. 读源工单
gitea_issue_comment__list(owner="Octopus", repo="octopus", index={issue_number}) curl -fsS -H "Authorization: token <token>" \
# 找 body 以 "## 工件索引"(遗留 "## Pipeline 工件追踪表")开头的评论 "<gitea-base-url>/api/v1/repos/Octopus/octopus/issues/{issue_number}"
gitea_wiki__get_page(owner="Octopus", repo="octopus", page_name=page_name) # 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}`]({html_url})`` ——
取**链接文本**(首个 `[` 与 `]` 之间、剥反引号)作为 page_name,不是 取**链接文本**(首个 `[` 与 `]` 之间、剥反引号)作为 page_name,不是
href(含 `%2F`/`.-`API 不认)。裸 page_name 单元格(无链接包裹)也 href(含 `%2F`/`.-`API 不认)。裸 page_name 单元格(无链接包裹)也
接受:整格即 page_name。形如 `#comment-{id}` 的值改用 接受:整格即 page_name。形如 `#comment-{id}` 的值改用
`gitea_issue_comment__list` 读,不得喂给 `gitea_wiki__get_page`。 `GET .../issues/{index}/comments` 拉工单评论列表后按 id 取,不得当作
wiki 页名去读 wiki 端点。
**重读规则**:重读集 = 全部 `CORE` 行;`ON-DEMAND` 行仅当前任务需要 **重读规则**:重读集 = 全部 `CORE` 行;`ON-DEMAND` 行仅当前任务需要
时读;绝不读 `ARCHIVE` 行。索引缺失(历史工单)→ 回退 issue body + 时读;绝不读 `ARCHIVE` 行。索引缺失(历史工单)→ 回退 issue body +
+4 -2
View File
@@ -40,7 +40,8 @@ error, returns empty, or you cannot otherwise access a required resource, you
MUST report the failure honestly. NEVER fabricate, invent, or hallucinate a MUST report the failure honestly. NEVER fabricate, invent, or hallucinate a
plausible-looking result to fill the gap. plausible-looking result to fill the gap.
- An honest "I could not call `gitea_wiki__get_page`, so I cannot review - An honest "I could not reach the wiki read API (`GET
<gitea-base-url>/api/v1/repos/{owner}/{repo}/wiki/pages`), so I cannot review
dimension Y" is always correct. An honest failure beats a confident fiction. dimension Y" is always correct. An honest failure beats a confident fiction.
- Producing a complete, specific-looking structured result — review JSON with - Producing a complete, specific-looking structured result — review JSON with
findings, severity levels, and file references, all invented — without a findings, severity levels, and file references, all invented — without a
@@ -52,5 +53,6 @@ plausible-looking result to fill the gap.
it, you do not know it — say so. it, you do not know it — say so.
Origin: [org-internal #2115] root cause 5 / [org-internal #2118] — deepseek-v4-flash reviewers fabricated Origin: [org-internal #2115] root cause 5 / [org-internal #2118] — deepseek-v4-flash reviewers fabricated
full review JSON when the `gitea_wiki__*` tools were unavailable to subagents. full review JSON when the wiki tool surface (MCP tooling at the time, now
retired) was unavailable to subagents.
+14 -10
View File
@@ -68,11 +68,12 @@ in `_shared/gitea-write-patterns.md`.
in the Orchestrator instead of letting {DIMENSION_COUNT} parallel subagents in the Orchestrator instead of letting {DIMENSION_COUNT} parallel subagents
each rediscover the gap: each rediscover the gap:
- **Primary path**: confirm `gitea_wiki__*` tools are registered (the - **Primary path**: confirm the Gitea REST API is reachable — probe
Orchestrator checks its own tool catalog; these tools are forwarded to `GET <gitea-base-url>/api/v1/repos/{owner}/{repo}/wiki/pages` with
reviewer subagents when registered — [org-internal #2116] raised the tool cap 50→100 so `-H "Authorization: token <token>"` (a 200 or 403 response means
they are present in normal runs). If available → continue to step 2. reachable; anything else — timeout, connection refused, 5xx — is
- **Fallback path** (when `gitea_wiki__*` is NOT registered, e.g. a unreachable). If reachable → continue to step 2.
- **Fallback path** (when REST is unreachable, e.g. a
cross-repo scenario [org-internal #2115]): verify the **local wiki clone** at cross-repo scenario [org-internal #2115]): verify the **local wiki clone** at
`<wiki-clone-root>/{repo}-wiki/` is readable — `<wiki-clone-root>/{repo}-wiki/` is readable —
`glob(pattern="*.md", path="<wiki-clone-root>/{repo}-wiki/")`. If the clone `glob(pattern="*.md", path="<wiki-clone-root>/{repo}-wiki/")`. If the clone
@@ -82,12 +83,13 @@ in `_shared/gitea-write-patterns.md`.
- **Both unavailable → FAIL FAST**: post a `failure` commit status (state - **Both unavailable → FAIL FAST**: post a `failure` commit status (state
`"failure"`, context `pipeline/{REVIEW_TYPE}`, description naming the `"failure"`, context `pipeline/{REVIEW_TYPE}`, description naming the
missing capability) and abort — do NOT enter parallel dispatch. Report the missing capability) and abort — do NOT enter parallel dispatch. Report the
gap so the environment can be fixed (register the wiki MCP server, or gap so the environment can be fixed (restore Gitea REST reachability —
token validity, network route — or
populate `<wiki-clone-root>/{repo}-wiki/`). populate `<wiki-clone-root>/{repo}-wiki/`).
This probe is belt-and-suspenders: RC1 (tool cap, [org-internal #2116]) already fixed the This probe is belt-and-suspenders: it exists so a future
primary cause of missing `gitea_wiki__*` in subagents. It exists so a future environment regression (REST unreachable, stale token) fails gracefully
environment regression fails gracefully with one clear message instead of N with one clear message instead of N
silent subagent aborts. silent subagent aborts.
2. Post an initial pending commit status via REST: 2. Post an initial pending commit status via REST:
@@ -471,7 +473,9 @@ not the round-1 full set.
3.5. **Gitea approval timing (mandatory when the PR needs a human approval 3.5. **Gitea approval timing (mandatory when the PR needs a human approval
gate)**: if the flow requests approval from a human reviewer on Gitea, gate)**: if the flow requests approval from a human reviewer on Gitea,
register the reviewer FIRST via `gitea_pull__add_reviewer`, wait for the register the reviewer FIRST via
`POST <gitea-base-url>/api/v1/repos/{owner}/{repo}/pulls/{index}/requested_reviewers`,
wait for the
`REQUEST_REVIEW` registration to be recorded, and only THEN ask the human `REQUEST_REVIEW` registration to be recorded, and only THEN ask the human
to approve. Approving before the reviewer is registered produces to approve. Approving before the reviewer is registered produces
`official: false` on the approval — the gate treats the PR as unapproved `official: false` on the approval — the gate treats the PR as unapproved
@@ -65,7 +65,8 @@ Label: {DIMENSION_NAME}
## What You Must NOT Do ## What You Must NOT Do
- Do NOT fabricate results. If a tool you need (e.g. `gitea_wiki__get_page`, - Do NOT fabricate results. If a tool you need (e.g. the Gitea REST call
`GET <gitea-base-url>/api/v1/repos/{owner}/{repo}/wiki/pages`,
`read`, `codegraph_*`) is unavailable, errors, or returns empty, report the `read`, `codegraph_*`) is unavailable, errors, or returns empty, report the
failure honestly — emit a finding stating the dimension could not be failure honestly — emit a finding stating the dimension could not be
reviewed due to the missing tool. NEVER invent findings, severities, or reviewed due to the missing tool. NEVER invent findings, severities, or
@@ -65,8 +65,8 @@ model**.
and coordinate on the issue before proceeding. and coordinate on the issue before proceeding.
**c. Open-PR check.** List open PRs referencing `#N` — **c. Open-PR check.** List open PRs referencing `#N` —
`工单 API(见 TERMINOLOGYsearch(q: "#N", type: "pulls", state: "open")` (API source) `GET <gitea-base-url>/api/v1/repos/issues/search?q="#N"&type=pulls&state=open`
or `gitea_pull__list` — and **ABORT** if an open PR already covers the ticket. (API source) — and **ABORT** if an open PR already covers the ticket.
**d. Remote branch check ([org-internal #2297]).** A local branch is invisible to other **d. Remote branch check ([org-internal #2297]).** A local branch is invisible to other
sessions — check the remote too: sessions — check the remote too:
@@ -104,15 +104,15 @@ Phase 2 and Phase 4, not after the fix:
[Worker: developer] Phase 1 reproduce + Phase 2 root cause [Worker: developer] Phase 1 reproduce + Phase 2 root cause
→ write repro-notes (reproduction steps, root cause, bug path, → write repro-notes (reproduction steps, root cause, bug path,
expected behavior) expected behavior)
↓ persist: wiki page `{slug}/repro-notes` (gitea_wiki__create_page) ↓ persist: wiki page `{slug}/repro-notes` (`POST .../wiki/new`, body `content_base64`)
[Worker: tester] read repro-notes → Phase 3 write failing regression test [Worker: tester] read repro-notes → Phase 3 write failing regression test
(confirms FAIL before fix) (confirms FAIL before fix)
↓ persist: wiki page `{slug}/test-report` (gitea_wiki__create_page, failing test confirmed) ↓ persist: wiki page `{slug}/test-report` (`POST .../wiki/new`, body `content_base64`, failing test confirmed)
[Worker: developer] Phase 4 fix → run test:changed to green [Worker: developer] Phase 4 fix → run test:changed to green
→ write impl-notes (post-fix behavior contract) → write impl-notes (post-fix behavior contract)
↓ persist: wiki page `{slug}/impl-notes` (gitea_wiki__create_page, post-fix behavior contract) ↓ persist: wiki page `{slug}/impl-notes` (`POST .../wiki/new`, body `content_base64`, post-fix behavior contract)
[Worker: tester] supplement boundary regression tests → run test:changed [Worker: tester] supplement boundary regression tests → run test:changed
↓ persist: update wiki page `{slug}/test-report` (gitea_wiki__update_page, final) ↓ persist: update wiki page `{slug}/test-report` (`PATCH .../wiki/page/{mangled-name}`, omit `title`, body `content_base64` — final)
— consumed by the human stakeholder / next iteration planning for DoD regression-test evidence — consumed by the human stakeholder / next iteration planning for DoD regression-test evidence
``` ```
@@ -499,8 +499,8 @@ issue exists.
retired — each one re-triggered the full CI surface for near-zero re-tested retired — each one re-triggered the full CI surface for near-zero re-tested
risk. risk.
> **Kanban column lifecycle**: automated (`工单 API(见 TERMINOLOGYcreate` → Backlog, > **Kanban column lifecycle**: automated (`POST <gitea-base-url>/api/v1/repos/{owner}/{repo}/issues` → Backlog,
> `gitea_pull__create` → Review; no manual moves). Single shared reference: > `POST .../pulls` → Review; no manual moves). Single shared reference:
> `_shared/gitea-write-patterns.md` Pattern 7.5; column semantics: wiki > `_shared/gitea-write-patterns.md` Pattern 7.5; column semantics: wiki
> `kanban-lifecycle`. > `kanban-lifecycle`.
@@ -234,7 +234,7 @@ After all steps are complete:
{If the refactoring changes the internal architecture in a way that merits an {If the refactoring changes the internal architecture in a way that merits an
ADR, reference the ADR. **Deprecated:** `.artifacts/{slug}/design/adr/{NNNN}-*.md` ADR, reference the ADR. **Deprecated:** `.artifacts/{slug}/design/adr/{NNNN}-*.md`
→ ADRs now live on the Gitea wiki at page `{slug}/03-adr-{NNNN}-{title}`, → ADRs now live on the Gitea wiki at page `{slug}/03-adr-{NNNN}-{title}`,
readable via `gitea_wiki__get_page`. Or "None".} readable via `GET <gitea-base-url>/api/v1/repos/{owner}/{repo}/wiki/page/{mangled-name}`. Or "None".}
--- ---
+10 -7
View File
@@ -25,19 +25,22 @@ role: Reviewer
## Tool Availability & Path Resolution ## Tool Availability & Path Resolution
This skill's reviewers (parallel Explorer subagents) read two kinds of files. This skill's reviewers (parallel Explorer subagents) read two kinds of files.
Some runs lack the `gitea_wiki__*` MCP tools (cross-repo scenario, [org-internal #2115]); the Some runs cannot reach the Gitea REST API (cross-repo scenario, [org-internal #2115]); the
fallbacks below keep reviews working without them. fallbacks below keep reviews working without it.
The Orchestrator probes availability **before dispatching reviewers** (Phase A The Orchestrator probes REST reachability **before dispatching reviewers** (Phase A
step 1, `_shared/review-pipeline-phases.md`) and fails fast if neither path is step 1, `_shared/review-pipeline-phases.md`) and fails fast if neither path is
usable, so every reviewer has a working read path by the time it runs. usable, so every reviewer has a working read path by the time it runs.
### Wiki artifacts (DAG artifact / review reports) ### Wiki artifacts (DAG artifact / review reports)
- **Primary**: `wiki 读写 API(见 TERMINOLOGY(owner, repo, page_name)` — use when the - **Primary**: `GET <gitea-base-url>/api/v1/repos/{owner}/{repo}/wiki/page/{mangled-name}`
tool is available. `page_name` construction is in — use when REST is reachable. Resolve `{mangled-name}` from
`GET .../wiki/pages` (`sub_url` — mangled name with `%2F` encoding and
possible `.-` suffix); never construct it by hand. Page-name resolution
context is in
`_shared/gitea-read-patterns.md` Pattern 1. `_shared/gitea-read-patterns.md` Pattern 1.
- **Fallback** (when `gitea_wiki__*` is unavailable): read the **local wiki - **Fallback** (when REST is unreachable): read the **local wiki
clone** at `<wiki-clone-root>/{repo}-wiki/` — a git checkout of the wiki repo, clone** at `<wiki-clone-root>/{repo}-wiki/` — a git checkout of the wiki repo,
kept current. Wiki filenames are URL-encoded (`/``%2F`, with a possible kept current. Wiki filenames are URL-encoded (`/``%2F`, with a possible
`.-` before `.md`), so resolve the file with `glob` rather than constructing `.-` before `.md`), so resolve the file with `glob` rather than constructing
@@ -393,7 +396,7 @@ file (concurrent write race):
- `{BASELINE_SECTION}`: - `{BASELINE_SECTION}`:
``` ```
## Baseline ## Baseline
The artifact under review is the DAG at wiki page `{epic-slug}/dag` (read via `gitea_wiki__get_page`). Read it first for orientation. Its `dag_metrics.review_depth` (D1D4) sets this review's depth and round cap. The artifact under review is the DAG at wiki page `{epic-slug}/dag` (read via `GET <gitea-base-url>/api/v1/repos/{owner}/{repo}/wiki/page/{mangled-name}`, `sub_url` resolved from `GET .../wiki/pages` first). Read it first for orientation. Its `dag_metrics.review_depth` (D1D4) sets this review's depth and round cap.
``` ```
- `{READ_FIRST_STEPS}`: - `{READ_FIRST_STEPS}`:
1. Read the DAG artifact at `{epic-slug}/dag` first for orientation. 1. Read the DAG artifact at `{epic-slug}/dag` first for orientation.
+3 -2
View File
@@ -974,7 +974,8 @@ After the Developer completes revision:
`question` tool, passing `stage: "review-code"`: `question` tool, passing `stage: "review-code"`:
> **Gitea approval timing (mandatory when the PR needs a human approval > **Gitea approval timing (mandatory when the PR needs a human approval
> gate)**: register the reviewer FIRST via `gitea_pull__add_reviewer`, > gate)**: register the reviewer FIRST via
> `POST <gitea-base-url>/api/v1/repos/{owner}/{repo}/pulls/{index}/requested_reviewers`,
> wait for the `REQUEST_REVIEW` registration to be recorded, and only > wait for the `REQUEST_REVIEW` registration to be recorded, and only
> THEN ask the human to approve. Approving before the reviewer is > THEN ask the human to approve. Approving before the reviewer is
> registered produces `official: false` on the approval — the gate > registered produces `official: false` on the approval — the gate
@@ -987,7 +988,7 @@ After the Developer completes revision:
Final verdict: {PASS | WARN | FAIL} Final verdict: {PASS | WARN | FAIL}
Converged: {true | false} Converged: {true | false}
See full report: wiki page `{slug}/reviews/code/final/report` DAG task mode: `{epic-slug}/reviews/code/final/report-task-{node-id}`)(via `gitea_wiki__get_page``_shared/gitea-read-patterns.md` Pattern 1 See full report: wiki page `{slug}/reviews/code/final/report` DAG task mode: `{epic-slug}/reviews/code/final/report-task-{node-id}`)(via `GET <gitea-base-url>/api/v1/repos/{owner}/{repo}/wiki/page/{mangled-name}` — `sub_url` resolved from `GET .../wiki/pages` first, `_shared/gitea-read-patterns.md` Pattern 1
All review artifacts: ticket #{issue_number} comments + wiki pages under `{slug}/reviews/code/`DAG task mode: under `{epic-slug}/reviews/code/`, Tier 1 round artifacts under `<runs-root>/{epic-slug}-task-{node-id}/reviews/code/` All review artifacts: ticket #{issue_number} comments + wiki pages under `{slug}/reviews/code/`DAG task mode: under `{epic-slug}/reviews/code/`, Tier 1 round artifacts under `<runs-root>/{epic-slug}-task-{node-id}/reviews/code/`
Inline commentsonly when step 2.56 ranomit for no-PR flows: `inline: X posted / Y file-level / Z fallback / W failed` Inline commentsonly when step 2.56 ranomit for no-PR flows: `inline: X posted / Y file-level / Z fallback / W failed`
+14 -16
View File
@@ -732,10 +732,10 @@ originating slug in each row so the trail survives.
independent ticket THEN, carrying the reserved `TD-NNN`: independent ticket THEN, carrying the reserved `TD-NNN`:
``` ```
gitea_issue__create(owner="Octopus", repo="octopus", POST <gitea-base-url>/api/v1/repos/Octopus/octopus/issues
title="[{origin}] Tech debt: {short description} (TD-{NNN})", body: {"title":"[{origin}] Tech debt: {short description} (TD-{NNN})",
body=<promotion template below>, "body":<promotion template below>,
labels=[tech_debt_label_id, severity_label_id]) "labels":[tech_debt_label_id, severity_label_id]}
``` ```
- **Severity → label** (reuse the existing exclusive Priority set): - **Severity → label** (reuse the existing exclusive Priority set):
@@ -820,8 +820,7 @@ AND closed), per `core/rules/testing.md`** (de-duplicate BEFORE filing,
retro [org-internal #2696]). Query `baseline-failure` issues in ALL states: retro [org-internal #2696]). Query `baseline-failure` issues in ALL states:
``` ```
gitea_issue__list(owner="Octopus", repo="octopus", GET <gitea-base-url>/api/v1/repos/Octopus/octopus/issues?labels=baseline-failure&state=all&page=N&limit=50
labels="baseline-failure", state="all")
``` ```
Match each candidate by **failure signature** — the error/assertion signature Match each candidate by **failure signature** — the error/assertion signature
@@ -848,10 +847,10 @@ number `#NNNN` is the tracker handle.
no existing family matches the signature: no existing family matches the signature:
``` ```
gitea_issue__create(owner="Octopus", repo="octopus", POST <gitea-base-url>/api/v1/repos/Octopus/octopus/issues
title="[{origin}] Baseline failure: {test identifier} (BF-{NNN})", body: {"title":"[{origin}] Baseline failure: {test identifier} (BF-{NNN})",
body=<issue body template below>, "body":<issue body template below>,
labels=[baseline_failure_label_id, severity_label_id, kind_bug_label_id]) "labels":[baseline_failure_label_id, severity_label_id, kind_bug_label_id]}
``` ```
- **Title**: `[{origin}] Baseline failure: {signature summary} (BF-{NNN})` - **Title**: `[{origin}] Baseline failure: {signature summary} (BF-{NNN})`
@@ -965,8 +964,7 @@ AND closed), per `core/rules/testing.md`** (de-duplicate BEFORE filing,
retro [org-internal #2696]). Query `flaky-test` issues in ALL states: retro [org-internal #2696]). Query `flaky-test` issues in ALL states:
``` ```
gitea_issue__list(owner="Octopus", repo="octopus", GET <gitea-base-url>/api/v1/repos/Octopus/octopus/issues?labels=flaky-test&state=all&page=N&limit=50
labels="flaky-test", state="all")
``` ```
Match each candidate by **failure signature** — the flakiness mode (timeout / Match each candidate by **failure signature** — the flakiness mode (timeout /
@@ -991,10 +989,10 @@ incl. closed tickets). `FT-NNN` names the FAMILY.
no existing family matches the signature: no existing family matches the signature:
``` ```
gitea_issue__create(owner="Octopus", repo="octopus", POST <gitea-base-url>/api/v1/repos/Octopus/octopus/issues
title="[{origin}] Flaky test: {test identifier} (FT-{NNN})", body: {"title":"[{origin}] Flaky test: {test identifier} (FT-{NNN})",
body=<issue body template below>, "body":<issue body template below>,
labels=[flaky_test_label_id, severity_label_id, kind_testing_label_id]) "labels":[flaky_test_label_id, severity_label_id, kind_testing_label_id]}
``` ```
- **Title**: `[{origin}] Flaky test: {signature summary} (FT-{NNN})` — the - **Title**: `[{origin}] Flaky test: {signature summary} (FT-{NNN})` — the
+2 -2
View File
@@ -1,6 +1,6 @@
{ {
"name": "octopus-workflow", "name": "octopus-workflow",
"version": "0.1.0", "version": "0.1.1",
"description": "Standalone workflow core: provider-neutral agent workflow corpus (skills / rules / checklists / templates / schemas) with drift guards, extracted from the octopus agent platform.", "description": "Standalone workflow core: provider-neutral agent workflow corpus (skills / rules / checklists / templates / schemas) with drift guards, extracted from the octopus agent platform.",
"license": "MIT", "license": "MIT",
"private": false, "private": false,