Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4518f41fd4 | ||
|
|
445940ea63 | ||
|
|
18ffce5d29 |
@@ -8,7 +8,7 @@ the corpus internally consistent and publish-ready.
|
|||||||
The corpus does not assume any specific forge (GitHub / Gitea / GitLab),
|
The corpus does not assume any specific forge (GitHub / Gitea / GitLab),
|
||||||
ticketing, or CI system. System-specific behavior lives behind an explicit
|
ticketing, or CI system. System-specific behavior lives behind an explicit
|
||||||
adapter contract (`core/adapters/`), so the same core can be adopted by any
|
adapter contract (`core/adapters/`), so the same core can be adopted by any
|
||||||
octopus instance — or any other agent platform — with its own adapters.
|
octopus instance 鈥?or any other agent platform 鈥?with its own adapters.
|
||||||
|
|
||||||
## Layout
|
## Layout
|
||||||
|
|
||||||
@@ -20,7 +20,7 @@ octopus instance — or any other agent platform — with its own adapters.
|
|||||||
| `core/templates/` | Artifact templates (DAG, iteration docs) |
|
| `core/templates/` | Artifact templates (DAG, iteration docs) |
|
||||||
| `core/schemas/` | JSON schemas under the public namespace `https://eightarms.net/octopus-workflow/schemas/` |
|
| `core/schemas/` | JSON schemas under the public namespace `https://eightarms.net/octopus-workflow/schemas/` |
|
||||||
| `core/adapters/` | Adapter contract: terminology SSOT + Gitea reference implementation |
|
| `core/adapters/` | Adapter contract: terminology SSOT + Gitea reference implementation |
|
||||||
| `core/CORE-MANIFEST.json` | Registry of core↔instance mappings and sync semantics (verbatim / verbatimDir / rewritten / split / core-only) |
|
| `core/CORE-MANIFEST.json` | Registry of core鈫攊nstance mappings and sync semantics (verbatim / verbatimDir / rewritten / split / core-only) |
|
||||||
| `examples/` | Organization-agnostic narrative essays referencing the core |
|
| `examples/` | Organization-agnostic narrative essays referencing the core |
|
||||||
| `guards/` | Standalone drift guards (bun + node stdlib only) |
|
| `guards/` | Standalone drift guards (bun + node stdlib only) |
|
||||||
|
|
||||||
@@ -32,6 +32,7 @@ Runnable inside this repo with [bun](https://bun.sh), no install step needed:
|
|||||||
bun run check:core-cohesion # corpus structural invariants
|
bun run check:core-cohesion # corpus structural invariants
|
||||||
bun run check:core-p1 # publish-readiness neutrality (no org-internal links)
|
bun run check:core-p1 # publish-readiness neutrality (no org-internal links)
|
||||||
bun run delink:core -- --check # no replaceable org-internal refs remain
|
bun run delink:core -- --check # no replaceable org-internal refs remain
|
||||||
|
bun run check:encoding-health # core/ text is clean UTF-8 (no GBK mojibake)
|
||||||
```
|
```
|
||||||
|
|
||||||
The instance-side guards (`check-core-parity`, `check-schema-ids`,
|
The instance-side guards (`check-core-parity`, `check-schema-ids`,
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ Core 与 octopus 实例(dogfood)之间的兼容性契约。随各 Increment
|
|||||||
| core 版本 | 对应 octopus 侧状态 | 兼容说明 |
|
| core 版本 | 对应 octopus 侧状态 | 兼容说明 |
|
||||||
| ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
| ------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| 0.x(Increment 1–4:骨架 + G0–G4 全量迁移) | octopus 实例 `.octopus/` 为消费镜像;core 与 dogfood 内容逐字/改写对齐 | core 是 SSOT 权威副本;dogfood 经 `script/core-sync.sh` 单向同步。schema 尚未迁入 core。 |
|
| 0.x(Increment 1–4:骨架 + G0–G4 全量迁移) | octopus 实例 `.octopus/` 为消费镜像;core 与 dogfood 内容逐字/改写对齐 | core 是 SSOT 权威副本;dogfood 经 `script/core-sync.sh` 单向同步。schema 尚未迁入 core。 |
|
||||||
| 0.x(Increment 5:schema $id 迁移四件套) | dogfood `.octopus/schemas/*.json` 的 `$id` **保持旧命名空间不动**(活系统仍引用);core 侧 `$id` 已迁至 `https://eightarms.net/octopus-workflow/schemas/` | 双轨期:core schema 与 dogfood schema 是**不同 $id 的两份文件**(manifest 登记为 `rewritten`,不逐字同步)。活归档 run 的 index.json 携带旧 $id,读取侧经别名表解析。 |
|
| 0.x(Increment 5:schema $id 迁移四件套) | dogfood `.octopus/schemas/*.json` 的 `$id` 曾**保持旧命名空间不动**(活系统仍引用);core 侧 `$id` 已迁至 `https://eightarms.net/octopus-workflow/schemas/` | 双轨期:core schema 与 dogfood schema 是**不同 $id 的两份文件**(manifest 登记为 `rewritten`,不逐字同步)。活归档 run 的 index.json 携带旧 $id,读取侧经别名表解析。**注记(2026-09):live 迁移已完成——dogfood 侧已切新命名空间,双轨结束**。 |
|
||||||
| 1.0.0(首个 semver 发布,规划中) | 发布仓创建;live 迁移(dogfood 侧切新命名空间)完成 | `id-aliases.json` 置 `frozen: true`——$id 一经发布不可变(v3 §G)。别名保留 ≥2 个 major 版本。 |
|
| 1.0.0(首个 semver 发布) | 发布仓创建;live 迁移(dogfood 侧切新命名空间)已完成(2026-09) | `id-aliases.json` 已置 `frozen: true`(2026-09)——$id 一经发布不可变(v3 §G)。别名保留 ≥2 个 major 版本。 |
|
||||||
|
|
||||||
## builtin overlay 语料兼容
|
## builtin overlay 语料兼容
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -90,7 +90,7 @@ G0–G4 批次已完成全量迁移(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 三份规则是从实例规则**逐字复制**的,规则正文里出现实例路径字样属
|
||||||
|
|||||||
@@ -9,10 +9,10 @@
|
|||||||
| --- | --- |
|
| --- | --- |
|
||||||
| 工单后端(ticket backend) | Gitea |
|
| 工单后端(ticket backend) | Gitea |
|
||||||
| Tier-2 工件库 | Gitea wiki |
|
| Tier-2 工件库 | Gitea wiki |
|
||||||
| 工单评论 API(ticket backend 评论接口) | `gitea_issue_comment__*` |
|
| 工单评论 API(ticket backend 评论接口) | Gitea REST 评论端点族:`GET/POST /repos/{owner}/{repo}/issues/{index}/comments`、`PATCH /repos/{owner}/{repo}/issues/comments/{id}` |
|
||||||
| wiki 读写 API(Tier-2 工件库读写接口) | `gitea_wiki__*` |
|
| wiki 读写 API(Tier-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` |
|
||||||
|
|||||||
@@ -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 级 projects(projects 仅 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-status(REST 回退)
|
## 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",
|
# 建 project(board):先经 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} 关闭于
|
||||||
|
|||||||
@@ -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-status(CLI)
|
## Pattern 5: read-review-status(CLI)
|
||||||
|
|
||||||
@@ -103,17 +117,24 @@ success,1 = 有 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_url(mangled
|
||||||
|
# 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 +
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"namespace": "https://eightarms.net/octopus-workflow/schemas/",
|
"namespace": "https://eightarms.net/octopus-workflow/schemas/",
|
||||||
"frozen": false,
|
"frozen": true,
|
||||||
"note": "发布仓创建时置 frozen: true——$id 一经发布不可变(v3 §G);别名保留 ≥2 个 major 版本",
|
"note": "已置 frozen: true(live 迁移完成,2026-09)——$id 一经发布不可变(v3 §G);别名保留 ≥2 个 major 版本",
|
||||||
"aliases": [
|
"aliases": [
|
||||||
{
|
{
|
||||||
"old": "https://eightarms.net/fourbroad/octopus/.octopus/schemas/port-analysis.schema.json",
|
"old": "https://eightarms.net/fourbroad/octopus/.octopus/schemas/port-analysis.schema.json",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
"$id": "https://eightarms.net/octopus-workflow/schemas/port-analysis.schema.json",
|
"$id": "https://eightarms.net/octopus-workflow/schemas/port-analysis.schema.json",
|
||||||
"title": "Port Analysis Reviewer Output",
|
"title": "Port Analysis Reviewer Output",
|
||||||
"description": "Schema for per-dimension port-analysis reviewer findings. Used by the large-port-pipeline source analysis review (Phase B1.5). The port source-analysis pipeline uses a deliberate three-value severity contract 鈥?BLOCKER / MAJOR / MINOR 鈥?and never emits INFO (matching implement/reference/large-port-pipeline.md and source-analysis.md, which name only those three). This is a documented exception to the canonical four-value set (BLOCKER / MAJOR / MINOR / INFO) in reviewer-output.schema.json and synthesis.schema.json.",
|
"description": "Schema for per-dimension port-analysis reviewer findings. Used by the large-port-pipeline source analysis review (Phase B1.5). The port source-analysis pipeline uses a deliberate three-value severity contract — BLOCKER / MAJOR / MINOR — and never emits INFO (matching implement/reference/large-port-pipeline.md and source-analysis.md, which name only those three). This is a documented exception to the canonical four-value set (BLOCKER / MAJOR / MINOR / INFO) in reviewer-output.schema.json and synthesis.schema.json.",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": ["dimension", "findings"],
|
"required": ["dimension", "findings"],
|
||||||
"properties": {
|
"properties": {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
"max_rounds": {
|
"max_rounds": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"default": 3,
|
"default": 3,
|
||||||
"description": "Per-skill bound: `review-artifact` skill (process target) = 3 (the roadmap/design/plan targets were archived 2026-08-21, [org-internal #3072] phase 3); `review-code` = 2 default / 3 high-risk (`workflow-routing.yaml` `review.max_rounds`, [org-internal #3567]). `review-dag` (the review-artifact skill's DAG single-gate target) binds per depth from `dag_metrics.review_depth`: D1 鈮?, D2 鈮?, D3 鈮?, D4 鈮? 鈥?D4 reaches round 4, exceeding the shared `review-artifact` default, so the DAG per-depth cap takes precedence (see review-artifact/SKILL.md review-dag Phase D MAX_ROUNDS). Set explicitly by the skill; default 3 is a fallback only."
|
"description": "Per-skill bound: `review-artifact` skill (process target) = 3 (the roadmap/design/plan targets were archived 2026-08-21, [org-internal #3072] phase 3); `review-code` = 2 default / 3 high-risk (`workflow-routing.yaml` `review.max_rounds`, [org-internal #3567]). `review-dag` (the review-artifact skill's DAG single-gate target) binds per depth from `dag_metrics.review_depth`: D1 ≤2, D2 ≤3, D3 ≤3, D4 ≤4 — D4 reaches round 4, exceeding the shared `review-artifact` default, so the DAG per-depth cap takes precedence (see review-artifact/SKILL.md review-dag Phase D MAX_ROUNDS). Set explicitly by the skill; default 3 is a fallback only."
|
||||||
},
|
},
|
||||||
"converged": {
|
"converged": {
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
"reviewer_slot": {
|
"reviewer_slot": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"minimum": 1,
|
"minimum": 1,
|
||||||
"description": "1-based slot of this reviewer WITHIN its dimension, when dim_split assigns >1 reviewer to the dimension (e.g. review-dag D3 TOPO:2, D4 TOPO:2 鈥?dim_split per `workflow-routing.yaml` dag.size_derivation.review_depth). Multi-reviewer dimensions write one file per slot: findings-{DIMENSION_CODE}-r{k}.json. Omitted for single-reviewer dimensions (file stays findings-{DIMENSION_CODE}.json). Preserves per-reviewer attribution for the retrospective overlap analysis ([org-internal #2996] step 1a)."
|
"description": "1-based slot of this reviewer WITHIN its dimension, when dim_split assigns >1 reviewer to the dimension (e.g. review-dag D3 TOPO:2, D4 TOPO:2 — dim_split per `workflow-routing.yaml` dag.size_derivation.review_depth). Multi-reviewer dimensions write one file per slot: findings-{DIMENSION_CODE}-r{k}.json. Omitted for single-reviewer dimensions (file stays findings-{DIMENSION_CODE}.json). Preserves per-reviewer attribution for the retrospective overlap analysis ([org-internal #2996] step 1a)."
|
||||||
},
|
},
|
||||||
"reviewer_count": {
|
"reviewer_count": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
@@ -79,7 +79,7 @@
|
|||||||
"checklist_ref": {
|
"checklist_ref": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"pattern": "^[A-Z0-9]{2,6} \\d+(\\.\\d+)+(\\s*\\([^\\)]*\\))?(\\s*/\\s*[A-Z0-9]{2,6} \\d+(\\.\\d+)+(\\s*\\([^\\)]*\\))?)*$",
|
"pattern": "^[A-Z0-9]{2,6} \\d+(\\.\\d+)+(\\s*\\([^\\)]*\\))?(\\s*/\\s*[A-Z0-9]{2,6} \\d+(\\.\\d+)+(\\s*\\([^\\)]*\\))?)*$",
|
||||||
"description": "Checklist item reference, e.g. COR 1.1, INV 1.3, SEC 3.7.1, STD 8.4 鈥?dimension/section code followed by item number(s)."
|
"description": "Checklist item reference, e.g. COR 1.1, INV 1.3, SEC 3.7.1, STD 8.4 — dimension/section code followed by item number(s)."
|
||||||
},
|
},
|
||||||
"location": {
|
"location": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
"index": true,
|
"index": true,
|
||||||
"working_notes": {
|
"working_notes": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Final content of runs/{slug}/working-notes.md ([org-internal #2600]), inlined at archive time when the file exists. The one Tier 1 artifact whose CONTENT (not just digest) rides in the bundle 鈥?the run's narrative memory (decisions, dead ends, reviewer feedback), consumed by later retro probes and the notes-injection degradation path ([org-internal #2601]). Absent when the run kept no working notes."
|
"description": "Final content of runs/{slug}/working-notes.md ([org-internal #2600]), inlined at archive time when the file exists. The one Tier 1 artifact whose CONTENT (not just digest) rides in the bundle — the run's narrative memory (decisions, dead ends, reviewer feedback), consumed by later retro probes and the notes-injection degradation path ([org-internal #2601]). Absent when the run kept no working notes."
|
||||||
},
|
},
|
||||||
"review_history": {
|
"review_history": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
"stage": {
|
"stage": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"minLength": 1,
|
"minLength": 1,
|
||||||
"description": "Review stage identifier matching the reviews/{stage}/ directory (e.g. code, review-dag, audit-process 鈥?the live values; design-space is a retired historical value)."
|
"description": "Review stage identifier matching the reviews/{stage}/ directory (e.g. code, review-dag, audit-process — the live values; design-space is a retired historical value)."
|
||||||
},
|
},
|
||||||
"rounds": { "type": "integer", "minimum": 1, "description": "Number of executed rounds." },
|
"rounds": { "type": "integer", "minimum": 1, "description": "Number of executed rounds." },
|
||||||
"final_verdict": { "type": "string", "enum": ["PASS", "WARN", "FAIL"] },
|
"final_verdict": { "type": "string", "enum": ["PASS", "WARN", "FAIL"] },
|
||||||
@@ -109,7 +109,7 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": true,
|
"additionalProperties": true,
|
||||||
"required": ["slug", "state", "created_at"],
|
"required": ["slug", "state", "created_at"],
|
||||||
"description": "Legacy profile (schema_version 1 or absent): historical bundles AND current live-workspace runs 鈥?runs-meta.schema.json / runs-index.schema.json pin schema_version 1 today. Core fields type-checked when present; extra context properties (route/pr/node/kind/...) tolerated as-is. The v2 strict profile (below) is the migration target: it becomes the profile for new runs once the live-workspace schemas are bumped to emit schema_version 2 with the context bag.",
|
"description": "Legacy profile (schema_version 1 or absent): historical bundles AND current live-workspace runs — runs-meta.schema.json / runs-index.schema.json pin schema_version 1 today. Core fields type-checked when present; extra context properties (route/pr/node/kind/...) tolerated as-is. The v2 strict profile (below) is the migration target: it becomes the profile for new runs once the live-workspace schemas are bumped to emit schema_version 2 with the context bag.",
|
||||||
"properties": {
|
"properties": {
|
||||||
"schema_version": { "type": "integer", "minimum": 1 },
|
"schema_version": { "type": "integer", "minimum": 1 },
|
||||||
"slug": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]*$" },
|
"slug": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]*$" },
|
||||||
@@ -132,7 +132,7 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"required": ["schema_version", "slug", "state", "created_at", "updated_at", "ticket"],
|
"required": ["schema_version", "slug", "state", "created_at", "updated_at", "ticket"],
|
||||||
"description": "Strict profile (schema_version 2): closed core field set. Run-specific context (route/pr/node/task_node/...) goes in the context bag 鈥?the ONLY place for non-core fields.",
|
"description": "Strict profile (schema_version 2): closed core field set. Run-specific context (route/pr/node/task_node/...) goes in the context bag — the ONLY place for non-core fields.",
|
||||||
"allOf": [
|
"allOf": [
|
||||||
{
|
{
|
||||||
"if": { "properties": { "state": { "const": "archived" } }, "required": ["state"] },
|
"if": { "properties": { "state": { "const": "archived" } }, "required": ["state"] },
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
"$id": "https://eightarms.net/octopus-workflow/schemas/runs-index.schema.json",
|
"$id": "https://eightarms.net/octopus-workflow/schemas/runs-index.schema.json",
|
||||||
"title": "Tier 1 Run Artifact Index",
|
"title": "Tier 1 Run Artifact Index",
|
||||||
"description": "Schema for runs/{slug}/index.json 鈥?the artifact manifest for a Tier 1 local structured run. This is the compact-recovery entry point after compaction (compact never deletes local files). Parallel to, not a replacement for, the Tier 2 '## 宸ヤ欢绱㈠紩' issue comment. Authoritative layout: templates/runs-layout.md. Boundary rule: rules/two-tier-artifacts.md.",
|
"description": "Schema for runs/{slug}/index.json — the artifact manifest for a Tier 1 local structured run. This is the compact-recovery entry point after compaction (compact never deletes local files). Parallel to, not a replacement for, the Tier 2 '## 工件索引' issue comment. Authoritative layout: templates/runs-layout.md. Boundary rule: rules/two-tier-artifacts.md.",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"required": ["schema_version", "slug", "artifacts"],
|
"required": ["schema_version", "slug", "artifacts"],
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
"artifacts": {
|
"artifacts": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": { "$ref": "#/$defs/artifact" },
|
"items": { "$ref": "#/$defs/artifact" },
|
||||||
"description": "One entry per Tier 1 artifact in this run. Tier 2 artifacts (Gitea issue comments, wiki pages, commit status, PRs) are NEVER listed here 鈥?this index is Tier 1 only."
|
"description": "One entry per Tier 1 artifact in this run. Tier 2 artifacts (Gitea issue comments, wiki pages, commit status, PRs) are NEVER listed here — this index is Tier 1 only."
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"$defs": {
|
"$defs": {
|
||||||
@@ -55,7 +55,7 @@
|
|||||||
"browser-evidence",
|
"browser-evidence",
|
||||||
"other"
|
"other"
|
||||||
],
|
],
|
||||||
"description": "Artifact category. 'meta'/'index' for the manifest files themselves; stage categories for produced artifacts. 'review-synthesis' marks a synthesis result artifact (round verdict) as distinct from 'synthesis-prompt' (the Phase B task prompt) and 'review-status' (the lifecycle file). 'precondition-gate' marks the review-code mechanical-green gate log. 'working-notes' is the cross-stage narrative memory file ([org-internal #2600]) 鈥?one file per run, appended by every role at close, registered once (stage/produced_by null: multi-stage, multi-role). 'browser-evidence' marks a browser evidence pack manifest ([org-internal #4497] N-03, browser/{session-id}/manifest.json 鈥?shared/pack-manifest-v1)."
|
"description": "Artifact category. 'meta'/'index' for the manifest files themselves; stage categories for produced artifacts. 'review-synthesis' marks a synthesis result artifact (round verdict) as distinct from 'synthesis-prompt' (the Phase B task prompt) and 'review-status' (the lifecycle file). 'precondition-gate' marks the review-code mechanical-green gate log. 'working-notes' is the cross-stage narrative memory file ([org-internal #2600]) — one file per run, appended by every role at close, registered once (stage/produced_by null: multi-stage, multi-role). 'browser-evidence' marks a browser evidence pack manifest ([org-internal #4497] N-03, browser/{session-id}/manifest.json — shared/pack-manifest-v1)."
|
||||||
},
|
},
|
||||||
"schema": {
|
"schema": {
|
||||||
"type": ["string", "null"],
|
"type": ["string", "null"],
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
"$id": "https://eightarms.net/octopus-workflow/schemas/runs-meta.schema.json",
|
"$id": "https://eightarms.net/octopus-workflow/schemas/runs-meta.schema.json",
|
||||||
"title": "Tier 1 Run Metadata",
|
"title": "Tier 1 Run Metadata",
|
||||||
"description": "Schema for runs/{slug}/meta.json 鈥?run metadata for a Tier 1 local structured run. Authoritative layout: templates/runs-layout.md. Boundary rule: rules/two-tier-artifacts.md. State transitions active -> archived at close (archive-at-close).",
|
"description": "Schema for runs/{slug}/meta.json — run metadata for a Tier 1 local structured run. Authoritative layout: templates/runs-layout.md. Boundary rule: rules/two-tier-artifacts.md. State transitions active -> archived at close (archive-at-close).",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"required": [
|
"required": [
|
||||||
@@ -89,12 +89,12 @@
|
|||||||
"created_at": {
|
"created_at": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "date-time",
|
"format": "date-time",
|
||||||
"description": "RFC 3339 timestamp 鈥?run creation."
|
"description": "RFC 3339 timestamp — run creation."
|
||||||
},
|
},
|
||||||
"updated_at": {
|
"updated_at": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "date-time",
|
"format": "date-time",
|
||||||
"description": "RFC 3339 timestamp 鈥?last metadata mutation."
|
"description": "RFC 3339 timestamp — last metadata mutation."
|
||||||
},
|
},
|
||||||
"closed_at": {
|
"closed_at": {
|
||||||
"type": ["string", "null"],
|
"type": ["string", "null"],
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
"$id": "https://eightarms.net/octopus-workflow/schemas/workflow-routing.schema.json",
|
"$id": "https://eightarms.net/octopus-workflow/schemas/workflow-routing.schema.json",
|
||||||
"title": "Workflow Routing Table",
|
"title": "Workflow Routing Table",
|
||||||
"description": "Schema for workflow-routing.yaml 鈥?type-driven (Kind/*) process selection. Referenced by rules/workflow-routing.md and the auto-approve rule. When this schema and the rules markdown disagree, the YAML (validated by this schema) is authoritative.",
|
"description": "Schema for workflow-routing.yaml — type-driven (Kind/*) process selection. Referenced by rules/workflow-routing.md and the auto-approve rule. When this schema and the rules markdown disagree, the YAML (validated by this schema) is authoritative.",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"required": ["version", "default", "routes"],
|
"required": ["version", "default", "routes"],
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
"$defs": {
|
"$defs": {
|
||||||
"stage": {
|
"stage": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "A pipeline stage identifier 鈥?the 5 live gates (post-[org-internal #3072] phase 3, 2026-08-21): review-dag, review-code, verify, audit-process, merge-pr. Production-phase names (requirements, design, roadmap, implement) and the 7 retired stage ids (roadmap-exit, review-roadmap, requirements-exit, design-exit, review-design-space, review-iteration-plan, plan-iterations) are no longer valid anywhere 鈥?the sizing block that consumed the phase names was retired the same ruling.",
|
"description": "A pipeline stage identifier — the 5 live gates (post-[org-internal #3072] phase 3, 2026-08-21): review-dag, review-code, verify, audit-process, merge-pr. Production-phase names (requirements, design, roadmap, implement) and the 7 retired stage ids (roadmap-exit, review-roadmap, requirements-exit, design-exit, review-design-space, review-iteration-plan, plan-iterations) are no longer valid anywhere — the sizing block that consumed the phase names was retired the same ruling.",
|
||||||
"enum": ["review-dag", "review-code", "verify", "audit-process", "merge-pr"]
|
"enum": ["review-dag", "review-code", "verify", "audit-process", "merge-pr"]
|
||||||
},
|
},
|
||||||
"entry": {
|
"entry": {
|
||||||
@@ -56,20 +56,20 @@
|
|||||||
"preflight": {
|
"preflight": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": { "$ref": "#/$defs/preflightItem" },
|
"items": { "$ref": "#/$defs/preflightItem" },
|
||||||
"description": "Evidence-based producer pre-flight checklist ([org-internal #2599]) 鈥?landed by a human from retrospective PRE-FLIGHT PROPOSAL rows. Absent/empty = no injection. Cap is governed by preflight.max_items (policy), not the schema."
|
"description": "Evidence-based producer pre-flight checklist ([org-internal #2599]) — landed by a human from retrospective PRE-FLIGHT PROPOSAL rows. Absent/empty = no injection. Cap is governed by preflight.max_items (policy), not the schema."
|
||||||
},
|
},
|
||||||
"notes": { "type": "string" },
|
"notes": { "type": "string" },
|
||||||
"effort": {
|
"effort": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["low", "high", "max"],
|
"enum": ["low", "high", "max"],
|
||||||
"description": "Reasoning-effort hint for this route (llm-latency phase 2): maps onto the active model's variant table when it exposes one (GLM-5.2+ low/high/max); fail-open otherwise. Reserve `low` for genuinely lightweight routes (direct-edit, no pipeline) 鈥?reasoning dominates GLM-5.3 generation latency."
|
"description": "Reasoning-effort hint for this route (llm-latency phase 2): maps onto the active model's variant table when it exposes one (GLM-5.2+ low/high/max); fail-open otherwise. Reserve `low` for genuinely lightweight routes (direct-edit, no pipeline) — reasoning dominates GLM-5.3 generation latency."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"preflightPolicy": {
|
"preflightPolicy": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"description": "Thresholds for the retro 鈫?routes.{Kind}.preflight evidence loop ([org-internal #2599]). Authoritative values live here; the retrospective consults this block, it does not restate the numbers.",
|
"description": "Thresholds for the retro → routes.{Kind}.preflight evidence loop ([org-internal #2599]). Authoritative values live here; the retrospective consults this block, it does not restate the numbers.",
|
||||||
"properties": {
|
"properties": {
|
||||||
"max_items": { "type": "integer", "minimum": 1 },
|
"max_items": { "type": "integer", "minimum": 1 },
|
||||||
"min_sample": { "type": "integer", "minimum": 0 },
|
"min_sample": { "type": "integer", "minimum": 0 },
|
||||||
@@ -86,7 +86,7 @@
|
|||||||
"item": { "type": "string", "description": "One checklist line the Producer verifies before implementing." },
|
"item": { "type": "string", "description": "One checklist line the Producer verifies before implementing." },
|
||||||
"evidence": {
|
"evidence": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Retro reference justifying the item, e.g. 'retro [org-internal #2599]: COR 脳 unhandled-null, 5/8 first-round FAILs on Kind/Bug'."
|
"description": "Retro reference justifying the item, e.g. 'retro [org-internal #2599]: COR × unhandled-null, 5/8 first-round FAILs on Kind/Bug'."
|
||||||
},
|
},
|
||||||
"added_cycle": {
|
"added_cycle": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@@ -97,7 +97,7 @@
|
|||||||
"filing": {
|
"filing": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"description": "Filing policy (绔嬫/鐧昏绛栫暐). sprint-mode ([org-internal #3007]): when true, verify Phase 5.5 promotes registry rows to tickets in sprint mode only. work-ticket-wip-cap ([org-internal #3005] G3): open non-TD work tickets beyond this count require scheduling evidence, else they stay registry rows.",
|
"description": "Filing policy (立案/登记策略). sprint-mode ([org-internal #3007]): when true, verify Phase 5.5 promotes registry rows to tickets in sprint mode only. work-ticket-wip-cap ([org-internal #3005] G3): open non-TD work tickets beyond this count require scheduling evidence, else they stay registry rows.",
|
||||||
"properties": {
|
"properties": {
|
||||||
"sprint-mode": {
|
"sprint-mode": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
@@ -113,7 +113,7 @@
|
|||||||
"review": {
|
"review": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"additionalProperties": false,
|
"additionalProperties": false,
|
||||||
"description": "Review round budget (process-diet P0b, [org-internal #3567]/TD-564): a tail cut, not the norm 鈥?at budget without convergence, STOP; residual findings flow to the TD registry. High risk = a BLOCKER survived a round or the diff touches a frozen cross-session contract.",
|
"description": "Review round budget (process-diet P0b, [org-internal #3567]/TD-564): a tail cut, not the norm — at budget without convergence, STOP; residual findings flow to the TD registry. High risk = a BLOCKER survived a round or the diff touches a frozen cross-session contract.",
|
||||||
"required": ["max_rounds"],
|
"required": ["max_rounds"],
|
||||||
"properties": {
|
"properties": {
|
||||||
"max_rounds": {
|
"max_rounds": {
|
||||||
@@ -152,7 +152,7 @@
|
|||||||
"preflight": {
|
"preflight": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": { "$ref": "#/$defs/preflightItem" },
|
"items": { "$ref": "#/$defs/preflightItem" },
|
||||||
"description": "Evidence-based pre-flight checklist for the DAG route ([org-internal #2905] 鏂规 3) 鈥?same item shape as routes.{Kind}.preflight ([org-internal #2599]). Landed by a human from retrospective PRE-FLIGHT PROPOSAL rows, including the 'REQ 脳 late-discovery' root cause (requirements found only after the DAG froze: DAG oversize signals, demo-period feedback). Consumed by analyze-dag (the route's entry skill) before decomposition. Absent/empty = no injection. Optional 鈥?deliberately NOT in dagRoute.required (spec-02 搂2.8 frozen required array unchanged). Cap governed by preflight.max_items (policy), not the schema."
|
"description": "Evidence-based pre-flight checklist for the DAG route ([org-internal #2905] 方案 3) — same item shape as routes.{Kind}.preflight ([org-internal #2599]). Landed by a human from retrospective PRE-FLIGHT PROPOSAL rows, including the 'REQ × late-discovery' root cause (requirements found only after the DAG froze: DAG oversize signals, demo-period feedback). Consumed by analyze-dag (the route's entry skill) before decomposition. Absent/empty = no injection. Optional — deliberately NOT in dagRoute.required (spec-02 §2.8 frozen required array unchanged). Cap governed by preflight.max_items (policy), not the schema."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -183,7 +183,7 @@
|
|||||||
"replaces": {
|
"replaces": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"items": { "type": "string" },
|
"items": { "type": "string" },
|
||||||
"description": "Historical documentation of which legacy gates the single gate replaced ([org-internal #3072] phase 3 retired them) 鈥?free-form strings, not the live stage vocabulary."
|
"description": "Historical documentation of which legacy gates the single gate replaced ([org-internal #3072] phase 3 retired them) — free-form strings, not the live stage vocabulary."
|
||||||
},
|
},
|
||||||
"depth_from": { "type": "string", "enum": ["size_derivation"] },
|
"depth_from": { "type": "string", "enum": ["size_derivation"] },
|
||||||
"never_trim": { "type": "boolean" }
|
"never_trim": { "type": "boolean" }
|
||||||
@@ -201,7 +201,7 @@
|
|||||||
"items": { "type": "string", "enum": ["Kind/Epic", "Kind/Feature"] },
|
"items": { "type": "string", "enum": ["Kind/Epic", "Kind/Feature"] },
|
||||||
"uniqueItems": true,
|
"uniqueItems": true,
|
||||||
"minItems": 1,
|
"minItems": 1,
|
||||||
"description": "Ticket kinds whose DAG children reference them as ## 鐖剁骇 / Parent ([org-internal #3061] Phase 2 / TD-388: Kind/Feature added alongside Kind/Epic)."
|
"description": "Ticket kinds whose DAG children reference them as ## 父级 / Parent ([org-internal #3061] Phase 2 / TD-388: Kind/Feature added alongside Kind/Epic)."
|
||||||
},
|
},
|
||||||
"parent_aggregator": { "type": "boolean" }
|
"parent_aggregator": { "type": "boolean" }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
name: gitea-rest
|
name: gitea-rest
|
||||||
description: Use ONLY when an agent must call the Gitea REST API over HTTP (curl / fetch / HttpClient) 鈥?not for other gitea tooling (the retired gitea-mcp wrapper / gitea_* MCP tools). Covers auth schemes + token scopes, pagination, error format, this fork's specifics, copy-paste recipes for the daily loop (issues, PRs, comments, labels, releases, raw files), and the on-demand recipe for extracting any endpoint's definition from the auto-generated swagger spec (`/swagger.v1.json`) instead of keeping an endpoint catalog in context. This is the only supported Gitea API path 鈥?the gitea-mcp wrapper is retired.
|
description: Use ONLY when an agent must call the Gitea REST API over HTTP (curl / fetch / HttpClient) — not for other gitea tooling (the retired gitea-mcp wrapper / gitea_* MCP tools). Covers auth schemes + token scopes, pagination, error format, this fork's specifics, copy-paste recipes for the daily loop (issues, PRs, comments, labels, releases, raw files), and the on-demand recipe for extracting any endpoint's definition from the auto-generated swagger spec (`/swagger.v1.json`) instead of keeping an endpoint catalog in context. This is the only supported Gitea API path — the gitea-mcp wrapper is retired.
|
||||||
triggers:
|
triggers:
|
||||||
# Direct Gitea HTTP/REST. Composite "gitea rest"/"gitea api" to avoid
|
# Direct Gitea HTTP/REST. Composite "gitea rest"/"gitea api" to avoid
|
||||||
# colliding with other gitea tooling.
|
# colliding with other gitea tooling.
|
||||||
@@ -11,20 +11,20 @@ triggers:
|
|||||||
- /api/v1
|
- /api/v1
|
||||||
- gitea swagger
|
- gitea swagger
|
||||||
- swagger.v1.json
|
- swagger.v1.json
|
||||||
- 璋?gitea api
|
- 调gitea api
|
||||||
- gitea 鎺ュ彛
|
- gitea 接口
|
||||||
- gitea rest api
|
- gitea rest api
|
||||||
role: Producer
|
role: Producer
|
||||||
---
|
---
|
||||||
|
|
||||||
> Core 涓珛鐗堬紙Increment 6a 鏀瑰啓锛夈€傛湰鏂囦欢鍚屾椂浣滀负 gitea adapter 鐨?**reference implementation** 绀轰緥锛坒rontmatter 鍘熸牱锛夛紱瀹炰緥鏈瀵圭収 `core/adapters/TERMINOLOGY.md`銆?
|
> Core 中立版(Increment 6a 改写)。本文件同时作为 gitea adapter 的 **reference implementation** 示例(frontmatter 原样);实例术语对照 `core/adapters/TERMINOLOGY.md`。
|
||||||
# Gitea REST 鈥?conventions + on-demand endpoint lookup
|
# Gitea REST — conventions + on-demand endpoint lookup
|
||||||
|
|
||||||
Gitea ships a JSON REST API under **`/api/v1`**. The instance also publishes an
|
Gitea ships a JSON REST API under **`/api/v1`**. The instance also publishes an
|
||||||
**auto-generated OpenAPI spec at `/swagger.v1.json`** (~889KB, 316 endpoints) 鈥?
|
**auto-generated OpenAPI spec at `/swagger.v1.json`** (~889KB, 316 endpoints) —
|
||||||
never load it whole into context; extract single endpoint definitions with jq
|
never load it whole into context; extract single endpoint definitions with jq
|
||||||
(see "Endpoint lookup"). The spec is generated from this fork's code, so it is
|
(see "Endpoint lookup"). The spec is generated from this fork's code, so it is
|
||||||
always current 鈥?no manually maintained catalog can drift.
|
always current — no manually maintained catalog can drift.
|
||||||
|
|
||||||
## Quick start
|
## Quick start
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ curl -fsS -H "$AUTH" $BASE/api/v1/version # => {"version":"1.22.0"}
|
|||||||
|
|
||||||
Inside a workspace container: `WORKSPACE_GIT_TOKEN` already carries the
|
Inside a workspace container: `WORKSPACE_GIT_TOKEN` already carries the
|
||||||
provisioned token, and the instance host resolves to the VPC-internal address
|
provisioned token, and the instance host resolves to the VPC-internal address
|
||||||
(no EIP bandwidth cap) 鈥?use it as `TOKEN` directly.
|
(no EIP bandwidth cap) — use it as `TOKEN` directly.
|
||||||
|
|
||||||
## Authentication
|
## Authentication
|
||||||
|
|
||||||
@@ -47,7 +47,7 @@ provisioned token, and the instance host resolves to the VPC-internal address
|
|||||||
| OAuth2 JWT | `Authorization: bearer <jwt>` | OAuth2-app token. |
|
| OAuth2 JWT | `Authorization: bearer <jwt>` | OAuth2-app token. |
|
||||||
| Basic | `Authorization: Basic <base64(user:pass)>` | Server must enable it; 2FA needs `X-Gitea-OTP`. |
|
| Basic | `Authorization: Basic <base64(user:pass)>` | Server must enable it; 2FA needs `X-Gitea-OTP`. |
|
||||||
|
|
||||||
- Query-param token (`?token=`) is deprecated 鈥?avoid.
|
- Query-param token (`?token=`) is deprecated — avoid.
|
||||||
- `Sudo: <username>` header acts as another user (admin only).
|
- `Sudo: <username>` header acts as another user (admin only).
|
||||||
- Keep tokens out of git and out of process argv where possible: read from a
|
- Keep tokens out of git and out of process argv where possible: read from a
|
||||||
file/env into a shell var at call time, not in a committed script.
|
file/env into a shell var at call time, not in a committed script.
|
||||||
@@ -55,21 +55,21 @@ provisioned token, and the instance host resolves to the VPC-internal address
|
|||||||
## Token scopes (write implies read)
|
## Token scopes (write implies read)
|
||||||
|
|
||||||
Categories: `issue`, `repository`, `organization`, `user`, `notification`,
|
Categories: `issue`, `repository`, `organization`, `user`, `notification`,
|
||||||
`package`, `admin`, `misc`, `activitypub` 鈥?each as `read:X` / `write:X`.
|
`package`, `admin`, `misc`, `activitypub` — each as `read:X` / `write:X`.
|
||||||
HTTP method sets the level: **GET 鈫?read**, **POST/PUT/PATCH/DELETE 鈫?write**.
|
HTTP method sets the level: **GET → read**, **POST/PUT/PATCH/DELETE → write**.
|
||||||
A scope miss returns `403` with `token does not have at least one of required
|
A scope miss returns `403` with `token does not have at least one of required
|
||||||
scope(s)...` 鈥?re-issue the token rather than widening other permissions.
|
scope(s)...` — re-issue the token rather than widening other permissions.
|
||||||
|
|
||||||
The full agent loop (push commits, create/merge PRs, comment) needs at minimum
|
The full agent loop (push commits, create/merge PRs, comment) needs at minimum
|
||||||
**`write:repository` + `write:issue`**. Workspace container tokens are
|
**`write:repository` + `write:issue`**. Workspace container tokens are
|
||||||
provisioned `read:repository` by default 鈥?write operations fail with 403
|
provisioned `read:repository` by default — write operations fail with 403
|
||||||
until the operator widens the scope.
|
until the operator widens the scope.
|
||||||
|
|
||||||
## Pagination
|
## Pagination
|
||||||
|
|
||||||
- `page` (1-based, default 1), `limit` (default 30, **hard cap 50**).
|
- `page` (1-based, default 1), `limit` (default 30, **hard cap 50**).
|
||||||
- `X-Total-Count` response header = total results.
|
- `X-Total-Count` response header = total results.
|
||||||
- `Link: <url?page=2>; rel="next"` 鈥?walk `next` links until absent.
|
- `Link: <url?page=2>; rel="next"` — walk `next` links until absent.
|
||||||
- Some endpoints (commits, pulls) also set `X-Page` / `X-PerPage` /
|
- Some endpoints (commits, pulls) also set `X-Page` / `X-PerPage` /
|
||||||
`X-PageCount` / `X-HasMore`.
|
`X-PageCount` / `X-HasMore`.
|
||||||
|
|
||||||
@@ -78,12 +78,12 @@ until the operator widens the scope.
|
|||||||
Every error is `{"message":"...","url":".../api/swagger"}` with the matching
|
Every error is `{"message":"...","url":".../api/swagger"}` with the matching
|
||||||
status: `400` bad arg, `403` permission/scope, `404` not found or wrong BASE,
|
status: `400` bad arg, `403` permission/scope, `404` not found or wrong BASE,
|
||||||
`409` already exists, `413` too large, `422` malformed JSON / missing field.
|
`409` already exists, `413` too large, `422` malformed JSON / missing field.
|
||||||
There is **no rate limiting** on `/api/v1` 鈥?don't expect `X-RateLimit-*`.
|
There is **no rate limiting** on `/api/v1` — don't expect `X-RateLimit-*`.
|
||||||
|
|
||||||
## Endpoint lookup (swagger, on demand)
|
## Endpoint lookup (swagger, on demand)
|
||||||
|
|
||||||
For any endpoint not covered by the recipes below, pull the spec once per
|
For any endpoint not covered by the recipes below, pull the spec once per
|
||||||
session and jq out just the definition you need (50鈥?00 tokens each):
|
session and jq out just the definition you need (50–200 tokens each):
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
curl -s $BASE/swagger.v1.json -o /tmp/gitea-sw.json
|
curl -s $BASE/swagger.v1.json -o /tmp/gitea-sw.json
|
||||||
@@ -109,7 +109,7 @@ curl -fsS -X POST "$BASE/api/v1/repos/$OWNER/$REPO/issues" -H "$AUTH" -H 'Conten
|
|||||||
-d '{"title":"Bug: X fails","body":"steps...","labels":[12]}'
|
-d '{"title":"Bug: X fails","body":"steps...","labels":[12]}'
|
||||||
```
|
```
|
||||||
|
|
||||||
**Comment on an issue / PR** (same path for both 鈥?PRs are issues by index)
|
**Comment on an issue / PR** (same path for both — PRs are issues by index)
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
curl -fsS -X POST "$BASE/api/v1/repos/$OWNER/$REPO/issues/42/comments" -H "$AUTH" \
|
curl -fsS -X POST "$BASE/api/v1/repos/$OWNER/$REPO/issues/42/comments" -H "$AUTH" \
|
||||||
@@ -130,48 +130,48 @@ curl -fsS -X POST "$BASE/api/v1/repos/$OWNER/$REPO/pulls/7/merge" -H "$AUTH" -H
|
|||||||
-d '{"Do":"merge","MergeTitleField":"feat: add X","MergeMessageField":"closes #7"}'
|
-d '{"Do":"merge","MergeTitleField":"feat: add X","MergeMessageField":"closes #7"}'
|
||||||
```
|
```
|
||||||
|
|
||||||
**Merge returned 405? Read the response body 鈥?two distinct causes:**
|
**Merge returned 405? Read the response body — two distinct causes:**
|
||||||
|
|
||||||
- `{"message":"head branch is behind the base branch"}` 鈥?the keep-mergeable
|
- `{"message":"head branch is behind the base branch"}` — the keep-mergeable
|
||||||
bot advanced main after your push, or Gitea's mergeable cache is stale.
|
bot advanced main after your push, or Gitea's mergeable cache is stale.
|
||||||
Fix: `git fetch origin main && git merge origin/main && git push` the head
|
Fix: `git fetch origin main && git merge origin/main && git push` the head
|
||||||
branch, wait ~2s, retry the merge. (Recurring on this server 鈥?seen 3 PRs
|
branch, wait ~2s, retry the merge. (Recurring on this server — seen 3 PRs
|
||||||
in a row on 2026-08-23.)
|
in a row on 2026-08-23.)
|
||||||
- `{"message":"The PR is already merged"}` 鈥?**treat as success**. The merge
|
- `{"message":"The PR is already merged"}` — **treat as success**. The merge
|
||||||
landed during a previous "failed" attempt (racy async recompute). Confirm
|
landed during a previous "failed" attempt (racy async recompute). Confirm
|
||||||
with `GET .../pulls/7 | jq '.merged, .merge_commit_sha'` before assuming
|
with `GET .../pulls/7 | jq '.merged, .merge_commit_sha'` before assuming
|
||||||
failure. Never re-push/re-create the PR on this signal.
|
failure. Never re-push/re-create the PR on this signal.
|
||||||
|
|
||||||
**Auth hygiene** 鈥?always pass the token via the `Authorization` header
|
**Auth hygiene** — always pass the token via the `Authorization` header
|
||||||
(`-H "Authorization: token $TOKEN"`), never embedded in a remote URL
|
(`-H "Authorization: token $TOKEN"`), never embedded in a remote URL
|
||||||
(`https://user:token@host/...`): it persists into `.git/config` and shell
|
(`https://user:token@host/...`): it persists into `.git/config` and shell
|
||||||
history. If a token leaks into a remote URL, rewrite the remote
|
history. If a token leaks into a remote URL, rewrite the remote
|
||||||
(`git remote set-url`) and rotate.
|
(`git remote set-url`) and rotate.
|
||||||
|
|
||||||
**List PRs / changed files** 鈥?`GET .../pulls?state=open`, `GET .../pulls/7/files`
|
**List PRs / changed files** — `GET .../pulls?state=open`, `GET .../pulls/7/files`
|
||||||
|
|
||||||
**Search issues across repos** 鈥?`GET $BASE/api/v1/repos/issues/search?q=&type=pulls`
|
**Search issues across repos** — `GET $BASE/api/v1/repos/issues/search?q=&type=pulls`
|
||||||
|
|
||||||
**List labels** 鈥?`GET .../labels?limit=50 | jq '.[] | {name,color}'` (label create/update via POST/PATCH on the same path)
|
**List labels** — `GET .../labels?limit=50 | jq '.[] | {name,color}'` (label create/update via POST/PATCH on the same path)
|
||||||
|
|
||||||
**Raw file contents** 鈥?`GET .../raw/README.md` (plain text; the `contents/{path}` variant returns base64 in `.content`)
|
**Raw file contents** — `GET .../raw/README.md` (plain text; the `contents/{path}` variant returns base64 in `.content`)
|
||||||
|
|
||||||
**Create a release** 鈥?`POST .../releases {"tag_name":"v1.2.0","target":"main",...}`
|
**Create a release** — `POST .../releases {"tag_name":"v1.2.0","target":"main",...}`
|
||||||
|
|
||||||
**Create a wiki page** 鈥?`POST .../wiki/new {"title":"My Page","content_base64":"<b64>","message":"add page"}`. **`content_base64` is the ONLY field that writes body text** 鈥?this fork **silently ignores** the upstream-style `content` field on wiki create/update (no error, `201` returned, page saved 0 bytes; evidence [org-internal #3944]: commits f5eaf18/4f0217d/bad5880). `content` is valid only on the file API, never on wiki.
|
**Create a wiki page** — `POST .../wiki/new {"title":"My Page","content_base64":"<b64>","message":"add page"}`. **`content_base64` is the ONLY field that writes body text** — this fork **silently ignores** the upstream-style `content` field on wiki create/update (no error, `201` returned, page saved 0 bytes; evidence [org-internal #3944]: commits f5eaf18/4f0217d/bad5880). `content` is valid only on the file API, never on wiki.
|
||||||
|
|
||||||
**Read / edit / delete a wiki page** 鈥?`GET|PATCH|DELETE .../wiki/page/{pageName}`. **Read by the mangled name, not the logical title**: this fork rewrites stored filenames for titles containing `/`, spaces, or `:` (slash percent-encoded into the filename + a `.-` suffix 鈥?see [org-internal #3218]), so `GET /wiki/page/bugfix-3204/bugfix-report` 404s. Always `GET .../wiki/pages` first and use the returned `sub_url` **verbatim** (it already carries the `%2F` encoding and `.-` suffix). PATCH/DELETE take the same mangled `pageName`. For PATCH, **omit `title` to keep the page name** and send only `content_base64`+`message` (fixed in gitea `dev-421-g7ff56aec08`, [org-internal #3510] 鈥?before that a title-less PATCH silently renamed the page to `unnamed.md` and later edits deleted target pages). Same trap as create: a PATCH carrying `content` instead of `content_base64` returns `200` with a **0-byte page** ([org-internal #3944]) 鈥?always base64-encode the body and verify non-empty via a follow-up `GET`.
|
**Read / edit / delete a wiki page** — `GET|PATCH|DELETE .../wiki/page/{pageName}`. **Read by the mangled name, not the logical title**: this fork rewrites stored filenames for titles containing `/`, spaces, or `:` (slash percent-encoded into the filename + a `.-` suffix — see [org-internal #3218]), so `GET /wiki/page/bugfix-3204/bugfix-report` 404s. Always `GET .../wiki/pages` first and use the returned `sub_url` **verbatim** (it already carries the `%2F` encoding and `.-` suffix). PATCH/DELETE take the same mangled `pageName`. For PATCH, **omit `title` to keep the page name** and send only `content_base64`+`message` (fixed in gitea `dev-421-g7ff56aec08`, [org-internal #3510] — before that a title-less PATCH silently renamed the page to `unnamed.md` and later edits deleted target pages). Same trap as create: a PATCH carrying `content` instead of `content_base64` returns `200` with a **0-byte page** ([org-internal #3944]) — always base64-encode the body and verify non-empty via a follow-up `GET`.
|
||||||
|
|
||||||
## This fork's specifics
|
## This fork's specifics
|
||||||
|
|
||||||
- **Projects are repo-level only** 鈥?no `/orgs/.../projects` or `/users/.../projects` REST endpoints.
|
- **Projects are repo-level only** — no `/orgs/.../projects` or `/users/.../projects` REST endpoints.
|
||||||
- **Aggregated inline review comments**: no single list-all endpoint 鈥?list
|
- **Aggregated inline review comments**: no single list-all endpoint — list
|
||||||
reviews, then fetch each review's `/comments`.
|
reviews, then fetch each review's `/comments`.
|
||||||
- Wiki REST = 6 method脳path combos on 4 paths (`new`, `page/{pageName}`
|
- Wiki REST = 6 method×path combos on 4 paths (`new`, `page/{pageName}`
|
||||||
GET/PATCH/DELETE, `pages`, `revisions/{pageName}`). This fork has **no**
|
GET/PATCH/DELETE, `pages`, `revisions/{pageName}`). This fork has **no**
|
||||||
upstream `raw/{pageName}` or `pages/{pageName}` single-page endpoints, and
|
upstream `raw/{pageName}` or `pages/{pageName}` single-page endpoints, and
|
||||||
`page/{pageName}` only matches the mangled filename (see the wiki recipe
|
`page/{pageName}` only matches the mangled filename (see the wiki recipe
|
||||||
above and [org-internal #3218]) 鈥?clone the wiki git repo (default branch `main`) only if
|
above and [org-internal #3218]) — clone the wiki git repo (default branch `main`) only if
|
||||||
you need history beyond `/wiki/revisions` or bulk filename surgery.
|
you need history beyond `/wiki/revisions` or bulk filename surgery.
|
||||||
- The authoritative route table is `routers/api/v1/api.go` in the Gitea
|
- The authoritative route table is `routers/api/v1/api.go` in the Gitea
|
||||||
source; the human-readable docs UI is `<BASE>/api/swagger`.
|
source; the human-readable docs UI is `<BASE>/api/swagger`.
|
||||||
@@ -180,4 +180,4 @@ history. If a token leaks into a remote URL, rewrite the remote
|
|||||||
|
|
||||||
Prefer `HttpClient` from `@effect/platform` with the same headers (see
|
Prefer `HttpClient` from `@effect/platform` with the same headers (see
|
||||||
`rules/effect-rules` on the wiki). Outside Effect, `curl` / `Bun.fetch` are
|
`rules/effect-rules` on the wiki). Outside Effect, `curl` / `Bun.fetch` are
|
||||||
fine 鈥?the wire format is identical.
|
fine — the wire format is identical.
|
||||||
|
|||||||
@@ -1,94 +1,94 @@
|
|||||||
---
|
---
|
||||||
name: headless-session-ops
|
name: headless-session-ops
|
||||||
description: >
|
description: >
|
||||||
Use ONLY when an agent must autonomously launch a headless main session 鈥?
|
Use ONLY when an agent must autonomously launch a headless main session —
|
||||||
create a session and drive agent generation over the HTTP `prompt_async`
|
create a session and drive agent generation over the HTTP `prompt_async`
|
||||||
endpoint with NO persistent client attached (Web UI / TUI absent) 鈥?OR
|
endpoint with NO persistent client attached (Web UI / TUI absent) — OR
|
||||||
lifecycle-manage such a session afterwards: health-check polling, hung-stream
|
lifecycle-manage such a session afterwards: health-check polling, hung-stream
|
||||||
diagnosis (state=generating + frozen message count), abort + re-wake rescue,
|
diagnosis (state=generating + frozen message count), abort + re-wake rescue,
|
||||||
provider quota-wall recovery (worker killed mid-task by a usage limit 鈥?
|
provider quota-wall recovery (worker killed mid-task by a usage limit —
|
||||||
detect, re-wake on a known-good provider, inject facts), and fact-baseline
|
detect, re-wake on a known-good provider, inject facts), and fact-baseline
|
||||||
injection to correct a stale worldview in a woken worker.
|
injection to correct a stale worldview in a woken worker.
|
||||||
This is the path used by in-session agents spawning sibling sessions,
|
This is the path used by in-session agents spawning sibling sessions,
|
||||||
`octopus run --attach` non-interactive mode, and cron / CI drivers. Covers
|
`octopus run --attach` non-interactive mode, and cron / CI drivers. Covers
|
||||||
the 3-step flow (create session 鈫?POST prompt_async WITH a known-good
|
the 3-step flow (create session → POST prompt_async WITH a known-good
|
||||||
`model` 鈫?poll `/session/:id/message` to verify generation), how to REUSE
|
`model` → poll `/session/:id/message` to verify generation), how to REUSE
|
||||||
the current session's model for the new session, the model-field
|
the current session's model for the new session, the model-field
|
||||||
requirement, the directory default (current session's directory unless
|
requirement, the directory default (current session's directory unless
|
||||||
targeting another repo), and the ticket-driven recipe (launch a session
|
targeting another repo), and the ticket-driven recipe (launch a session
|
||||||
seeded from issue #N). Do NOT use when a persistent client (Web UI / TUI)
|
seeded from issue #N). Do NOT use when a persistent client (Web UI / TUI)
|
||||||
is driving the session 鈥?those use the synchronous `prompt` endpoint and
|
is driving the session — those use the synchronous `prompt` endpoint and
|
||||||
always send `model`.
|
always send `model`.
|
||||||
triggers:
|
triggers:
|
||||||
# English 鈥?the brand phrases for headless launch.
|
# English — the brand phrases for headless launch.
|
||||||
- headless session
|
- headless session
|
||||||
- launch session
|
- launch session
|
||||||
- prompt_async
|
- prompt_async
|
||||||
- prompt async
|
- prompt async
|
||||||
- autonomous session
|
- autonomous session
|
||||||
# English 鈥?the failure mode (agent sees a stalled session and needs to know why).
|
# English — the failure mode (agent sees a stalled session and needs to know why).
|
||||||
- session not generating
|
- session not generating
|
||||||
- session generation stuck
|
- session generation stuck
|
||||||
# English 鈥?ticket-driven launch.
|
# English — ticket-driven launch.
|
||||||
- launch session for issue
|
- launch session for issue
|
||||||
- launch session for ticket
|
- launch session for ticket
|
||||||
- spawn session for ticket
|
- spawn session for ticket
|
||||||
# English 鈥?patrol mode (delayed self-wake polling loops, [org-internal #3937]).
|
# English — patrol mode (delayed self-wake polling loops, [org-internal #3937]).
|
||||||
- patrol loop
|
- patrol loop
|
||||||
- patrol mode
|
- patrol mode
|
||||||
- delayed wake
|
- delayed wake
|
||||||
# Chinese 鈥?bare noun phrases (matches() is a contiguous substring).
|
# Chinese — bare noun phrases (matches() is a contiguous substring).
|
||||||
- 鎷夎捣涓讳細璇?
|
- 拉起主会话
|
||||||
- 鎷夎捣浼氳瘽
|
- 拉起会话
|
||||||
- 鑷不浼氳瘽
|
- 自治会话
|
||||||
- 鏃犲ご浼氳瘽
|
- 无头会话
|
||||||
# Chinese 鈥?patrol mode ([org-internal #3937]).
|
# Chinese — patrol mode ([org-internal #3937]).
|
||||||
- 宸℃妯″紡
|
- 巡检模式
|
||||||
- 寤惰繜鍞ら啋
|
- 延迟唤醒
|
||||||
- 浼氳瘽涓嶇敓鎴?
|
- 会话不生成
|
||||||
- 浼氳瘽鍗℃
|
- 会话卡死
|
||||||
- 涓哄伐鍗曟媺璧蜂細璇?
|
- 为工单拉起会话
|
||||||
- 宸ュ崟鎷夎捣浼氳瘽
|
- 工单拉起会话
|
||||||
# English 鈥?lifecycle (hung rescue, stale-worldview correction, [org-internal #2459]).
|
# English — lifecycle (hung rescue, stale-worldview correction, [org-internal #2459]).
|
||||||
- abort session
|
- abort session
|
||||||
- revive session
|
- revive session
|
||||||
- session rescue
|
- session rescue
|
||||||
- fact baseline
|
- fact baseline
|
||||||
# English 鈥?quota-wall recovery ([org-internal #3669]): worker killed by provider usage limit.
|
# English — quota-wall recovery ([org-internal #3669]): worker killed by provider usage limit.
|
||||||
- quota wall
|
- quota wall
|
||||||
- usage limit reached
|
- usage limit reached
|
||||||
- quota exhausted
|
- quota exhausted
|
||||||
- switch provider
|
- switch provider
|
||||||
# Chinese 鈥?lifecycle ops ([org-internal #2459]).
|
# Chinese — lifecycle ops ([org-internal #2459]).
|
||||||
- 鍞ら啋浼氳瘽
|
- 唤醒会话
|
||||||
- 浼氳瘽鎸傛
|
- 会话挂死
|
||||||
- 浜嬪疄鍩虹嚎
|
- 事实基线
|
||||||
# Chinese 鈥?quota-wall recovery ([org-internal #3669]).
|
# Chinese — quota-wall recovery ([org-internal #3669]).
|
||||||
- 閰嶉澧?
|
- 配额墙
|
||||||
- 閰嶉鑰楀敖
|
- 配额耗尽
|
||||||
- 鐢ㄩ噺涓婇檺
|
- 用量上限
|
||||||
- 鎹緵搴斿晢
|
- 换供应商
|
||||||
role: Producer
|
role: Producer
|
||||||
---
|
---
|
||||||
|
|
||||||
> Core 涓珛鐗堬紙Increment 6a 鏀瑰啓锛屽師 deferHard verbatimDir锛夈€傛満鍒躲€佺粨鏋勪笌 frontmatter 淇濇寔锛涘疄渚嬫湳璇紙宸ュ叿鍚嶃€佽矾寰勩€佸伐鍗曞彿锛夋寜 `core/adapters/TERMINOLOGY.md` 缁戝畾鍒板叿浣撳疄渚嬨€?
|
> Core 中立版(Increment 6a 改写,原 deferHard verbatimDir)。机制、结构与 frontmatter 保持;实例术语(工具名、路径、工单号)按 `core/adapters/TERMINOLOGY.md` 绑定到具体实例。
|
||||||
# Headless Main Session Launch (prompt_async)
|
# Headless Main Session Launch (prompt_async)
|
||||||
|
|
||||||
Launch a **main session for a specific issue / work item from a headless
|
Launch a **main session for a specific issue / work item from a headless
|
||||||
context** 鈥?no Web UI, no TUI, just HTTP. This is the "fire-and-forget" path: an
|
context** — no Web UI, no TUI, just HTTP. This is the "fire-and-forget" path: an
|
||||||
agent inside another session, a cron job, a CI script, or `octopus run --attach`
|
agent inside another session, a cron job, a CI script, or `octopus run --attach`
|
||||||
non-interactive mode all land here when they need to spin up a session that
|
non-interactive mode all land here when they need to spin up a session that
|
||||||
actually generates.
|
actually generates.
|
||||||
|
|
||||||
## Directory convention 鈥?independent session in the current workspace, no worktree
|
## Directory convention — independent session in the current workspace, no worktree
|
||||||
|
|
||||||
The default is an **independent session sharing the current session's
|
The default is an **independent session sharing the current session's
|
||||||
directory** 鈥?the repo you are already working in. No new worktree, no
|
directory** — the repo you are already working in. No new worktree, no
|
||||||
checkout, no extra `bun install` or `.codegraph/` rebuild. Session creation
|
checkout, no extra `bun install` or `.codegraph/` rebuild. Session creation
|
||||||
and worktree discipline are **orthogonal**: this skill only opens a session
|
and worktree discipline are **orthogonal**: this skill only opens a session
|
||||||
record pointed at a directory; whether the launched session later creates its
|
record pointed at a directory; whether the launched session later creates its
|
||||||
own worktree is a _workflow_ decision (per the worktree-discipline rule) it
|
own worktree is a _workflow_ decision (per the worktree-discipline rule) it
|
||||||
makes when it starts actual work 鈥?not something the launcher does. One
|
makes when it starts actual work — not something the launcher does. One
|
||||||
exception: the **ticket-driven recipe** below, where claim-first ([org-internal #2297])
|
exception: the **ticket-driven recipe** below, where claim-first ([org-internal #2297])
|
||||||
requires the launcher to push the workflow branch before launch, making a
|
requires the launcher to push the workflow branch before launch, making a
|
||||||
pre-built worktree the claim carrier + ready workspace (see Pitfall #10
|
pre-built worktree the claim carrier + ready workspace (see Pitfall #10
|
||||||
@@ -100,7 +100,7 @@ carve-out). So, unless a different repo / path is specifically targeted,
|
|||||||
from your own session record via `GET /session/$LAUNCHER_SID`). The new
|
from your own session record via `GET /session/$LAUNCHER_SID`). The new
|
||||||
session runs independently in that same workspace. Pass an explicit
|
session runs independently in that same workspace. Pass an explicit
|
||||||
different `?directory=` only when the new session must work in another
|
different `?directory=` only when the new session must work in another
|
||||||
**repo** 鈥?never a worktree of the same repo. The Web UI sidebar groups
|
**repo** — never a worktree of the same repo. The Web UI sidebar groups
|
||||||
sessions by directory (`sidebar-project.tsx` per-workspace
|
sessions by directory (`sidebar-project.tsx` per-workspace
|
||||||
`workspaceSessions(directory)`, fetched via `session.list({ directory })`),
|
`workspaceSessions(directory)`, fetched via `session.list({ directory })`),
|
||||||
so a session pointed at a worktree disappears from the workspace the creator
|
so a session pointed at a worktree disappears from the workspace the creator
|
||||||
@@ -109,23 +109,23 @@ carve-out). So, unless a different repo / path is specifically targeted,
|
|||||||
- **No current session** (cron / CI / bare script): the server-side fallback
|
- **No current session** (cron / CI / bare script): the server-side fallback
|
||||||
when `?directory=` is omitted is the **server process's `process.cwd()`**
|
when `?directory=` is omitted is the **server process's `process.cwd()`**
|
||||||
(`server/routes/instance/httpapi/middleware/workspace-routing.ts:76`), which
|
(`server/routes/instance/httpapi/middleware/workspace-routing.ts:76`), which
|
||||||
is wherever the server was started 鈥?unreliable. In that context `?directory=`
|
is wherever the server was started — unreliable. In that context `?directory=`
|
||||||
remains effectively required.
|
remains effectively required.
|
||||||
- **Dispatch convention 鈥?directory = code location, not tracker repo.** When
|
- **Dispatch convention — directory = code location, not tracker repo.** When
|
||||||
launching a session for a ticket, set `?directory=` to the **main workspace**
|
launching a session for a ticket, set `?directory=` to the **main workspace**
|
||||||
checkout that holds the **code the ticket edits**, which may differ from the
|
checkout that holds the **code the ticket edits**, which may differ from the
|
||||||
repo where the ticket is tracked. Example: `<owner>/<backend-repo>#<n>` is filed in
|
repo where the ticket is tracked. Example: `<owner>/<backend-repo>#<n>` is filed in
|
||||||
the ticket-tracker repo but edits the main repo's workflow files 鈫?the
|
the ticket-tracker repo but edits the main repo's workflow files → the
|
||||||
session's `?directory=` is the **code checkout root**
|
session's `?directory=` is the **code checkout root**
|
||||||
(`<workspace-root>/<org>/<repo>`), not the tracker repo checkout. Never point
|
(`<workspace-root>/<org>/<repo>`), not the tracker repo checkout. Never point
|
||||||
`?directory=` at a ticket worktree 鈥?the worktree goes in the claim comment +
|
`?directory=` at a ticket worktree — the worktree goes in the claim comment +
|
||||||
seed prompt, not the directory field (sidebar visibility, see bullet 1). The
|
seed prompt, not the directory field (sidebar visibility, see bullet 1). The
|
||||||
owner-check (`GET /session?directory=$DIR`) is scoped per directory, so
|
owner-check (`GET /session?directory=$DIR`) is scoped per directory, so
|
||||||
rooting the session at the correct code checkout is also what makes the
|
rooting the session at the correct code checkout is also what makes the
|
||||||
duplicate-session guard meaningful. Server-side backstop ([org-internal #3190]): creating a
|
duplicate-session guard meaningful. Server-side backstop ([org-internal #3190]): creating a
|
||||||
session whose directory IS a linked git worktree answers with an
|
session whose directory IS a linked git worktree answers with an
|
||||||
`X-Session-Directory-Warning` response header and a server warn log naming
|
`X-Session-Directory-Warning` response header and a server warn log naming
|
||||||
the owning main repo 鈥?if you see that header, the session will be invisible
|
the owning main repo — if you see that header, the session will be invisible
|
||||||
to `GET /session?directory=<mainRepo>` owner-checks; re-check your launch
|
to `GET /session?directory=<mainRepo>` owner-checks; re-check your launch
|
||||||
parameters before proceeding.
|
parameters before proceeding.
|
||||||
|
|
||||||
@@ -135,10 +135,10 @@ carve-out). So, unless a different repo / path is specifically targeted,
|
|||||||
> corrected the misdiagnosed [org-internal #1691], see comment 9205). Code citations below were
|
> corrected the misdiagnosed [org-internal #1691], see comment 9205). Code citations below were
|
||||||
> verified against `<harness-package>` at HEAD.
|
> verified against `<harness-package>` at HEAD.
|
||||||
|
|
||||||
## The `model` field 鈥?always provide a known-good model (read this first)
|
## The `model` field — always provide a known-good model (read this first)
|
||||||
|
|
||||||
`prompt_async` is **fire-and-forget**: after HTTP returns 204 (or 202 when the
|
`prompt_async` is **fire-and-forget**: after HTTP returns 204 (or 202 when the
|
||||||
message is queued behind a wedge/zombie session 鈥?no live runner in that
|
message is queued behind a wedge/zombie session — no live runner in that
|
||||||
process), the server runs the full agent loop in a separate fiber. **Always
|
process), the server runs the full agent loop in a separate fiber. **Always
|
||||||
include an explicit `model`
|
include an explicit `model`
|
||||||
in the payload, and reuse the current session's model for it** (see the next
|
in the payload, and reuse the current session's model for it** (see the next
|
||||||
@@ -154,7 +154,7 @@ section). Two reasons:
|
|||||||
// ^^^^^^^^^ agent ^^^^^^^^^^^^^^^^^ session/default
|
// ^^^^^^^^^ agent ^^^^^^^^^^^^^^^^^ session/default
|
||||||
```
|
```
|
||||||
|
|
||||||
So omission does **not** produce a clean validation error 鈥?it silently
|
So omission does **not** produce a clean validation error — it silently
|
||||||
resolves to the agent's model, the session's last-used model, or the
|
resolves to the agent's model, the session's last-used model, or the
|
||||||
provider default (`currentModel`, `prompt.ts:824-838`).
|
provider default (`currentModel`, `prompt.ts:824-838`).
|
||||||
|
|
||||||
@@ -164,15 +164,15 @@ section). Two reasons:
|
|||||||
(`prompt.ts:1399`, `:804-822`). In the **async** handler that error is caught
|
(`prompt.ts:1399`, `:804-822`). In the **async** handler that error is caught
|
||||||
by the delivery fork's failure handling (`forkPromptDelivery` in
|
by the delivery fork's failure handling (`forkPromptDelivery` in
|
||||||
`handlers/session.ts`) and never reaches the HTTP
|
`handlers/session.ts`) and never reaches the HTTP
|
||||||
caller 鈥?so the session looks healthy (HTTP 204, user message persisted) yet
|
caller — so the session looks healthy (HTTP 204, user message persisted) yet
|
||||||
**never generates**. This is exactly the "silent stall" observed empirically
|
**never generates**. This is exactly the "silent stall" observed empirically
|
||||||
in [org-internal #1695]: a session with a 1-message transcript (user only) and frozen
|
in [org-internal #1695]: a session with a 1-message transcript (user only) and frozen
|
||||||
`time.updated`.
|
`time.updated`.
|
||||||
|
|
||||||
| payload | result |
|
| payload | result |
|
||||||
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
| `{agent, parts}` 鈥?no `model` | 204/202; model filled by fallback; if the resolved model is unusable 鈫?**apparent silent stall** (no error surfaced, no generation). |
|
| `{agent, parts}` — no `model` | 204/202; model filled by fallback; if the resolved model is unusable → **apparent silent stall** (no error surfaced, no generation). |
|
||||||
| `{agent, model:{providerID,modelID}, parts}` | 204/202; deterministic generation within seconds (verified: "1+1 绛変簬 2銆? in ~6s). |
|
| `{agent, model:{providerID,modelID}, parts}` | 204/202; deterministic generation within seconds (verified: "1+1 等于 2。 in ~6s). |
|
||||||
|
|
||||||
> **Iron Law:** every `prompt_async` payload MUST carry a **known-good** `model`,
|
> **Iron Law:** every `prompt_async` payload MUST carry a **known-good** `model`,
|
||||||
> and the safest known-good model is **the one driving this session right now**
|
> and the safest known-good model is **the one driving this session right now**
|
||||||
@@ -181,14 +181,14 @@ section). Two reasons:
|
|||||||
|
|
||||||
**Field-name gotcha when copying a model:** the prompt payload's `ModelRef` uses
|
**Field-name gotcha when copying a model:** the prompt payload's `ModelRef` uses
|
||||||
`{ providerID, modelID }` (`prompt.ts:1852-1855`), but the **session record's**
|
`{ providerID, modelID }` (`prompt.ts:1852-1855`), but the **session record's**
|
||||||
model uses `{ id, providerID, variant }` (`session.ts:256-260`) 鈥?i.e. `modelID`
|
model uses `{ id, providerID, variant }` (`session.ts:256-260`) — i.e. `modelID`
|
||||||
is called `id`. When you reuse a model read from a session record, remap
|
is called `id`. When you reuse a model read from a session record, remap
|
||||||
`model.id 鈫?modelID` (see recipe below). User-message `info.model` already uses
|
`model.id → modelID` (see recipe below). User-message `info.model` already uses
|
||||||
`modelID` (`message.ts:403-407`).
|
`modelID` (`message.ts:403-407`).
|
||||||
|
|
||||||
## Reuse the current session's model (recommended default)
|
## Reuse the current session's model (recommended default)
|
||||||
|
|
||||||
The model powering the launching session is proven to work 鈥?reuse it. Discover
|
The model powering the launching session is proven to work — reuse it. Discover
|
||||||
it by whichever path fits your context:
|
it by whichever path fits your context:
|
||||||
|
|
||||||
- **You are an in-session agent** (most common): read it straight from your own
|
- **You are an in-session agent** (most common): read it straight from your own
|
||||||
@@ -196,27 +196,27 @@ it by whichever path fits your context:
|
|||||||
|
|
||||||
> "You are powered by the model named **{modelID}** (provider: **{providerID}**)."
|
> "You are powered by the model named **{modelID}** (provider: **{providerID}**)."
|
||||||
|
|
||||||
e.g. `modelID = "glm-5.2"`, `providerID = "zai-coding-plan"`锛堟浘鍚?
|
e.g. `modelID = "glm-5.2"`, `providerID = "zai-coding-plan"`(曾名
|
||||||
zhipuai-coding-plan锛?026-08 鏇村悕锛?
|
zhipuai-coding-plan,2026-08 更名)
|
||||||
|
|
||||||
- **Programmatic / no system prompt** (cron, CI, sibling caller): `GET` the
|
- **Programmatic / no system prompt** (cron, CI, sibling caller): `GET` the
|
||||||
launching session's record and read `.model`:
|
launching session's record and read `.model`:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
curl -s "$BASE/session/$LAUNCHER_SID?directory=$DIR" \
|
curl -s "$BASE/session/$LAUNCHER_SID?directory=$DIR" \
|
||||||
| jq '.model' # 鈫?{ "id": "glm-5.2", "providerID": "zai-coding-plan", "variant": ... }
|
| jq '.model' # → { "id": "glm-5.2", "providerID": "zai-coding-plan", "variant": ... }
|
||||||
```
|
```
|
||||||
|
|
||||||
Then **remap** `model.id 鈫?modelID` when building the payload.
|
Then **remap** `model.id → modelID` when building the payload.
|
||||||
|
|
||||||
- **Verify the provider is actually connected** before launching (cheap
|
- **Verify the provider is actually connected** before launching (cheap
|
||||||
insurance 鈥?the model only generates if its provider is live):
|
insurance — the model only generates if its provider is live):
|
||||||
```sh
|
```sh
|
||||||
curl -s "$BASE/provider?directory=$DIR" | jq '.connected | index("<current providerID>")'
|
curl -s "$BASE/provider?directory=$DIR" | jq '.connected | index("<current providerID>")'
|
||||||
# 鈫?null = provider NOT connected (do NOT launch); 0..n = connected
|
# → null = provider NOT connected (do NOT launch); 0..n = connected
|
||||||
```
|
```
|
||||||
or call the `list-models` tool. Note: model-level availability is only
|
or call the `list-models` tool. Note: model-level availability is only
|
||||||
knowable at runtime 鈥?a _configured_ model can still fail (subscription-tier
|
knowable at runtime — a _configured_ model can still fail (subscription-tier
|
||||||
or region errors); `GET /config/providers` lists _configured_ models, not
|
or region errors); `GET /config/providers` lists _configured_ models, not
|
||||||
usable ones. The safest pre-flight is "provider connected" + reusing the
|
usable ones. The safest pre-flight is "provider connected" + reusing the
|
||||||
current session's model, which is generating by definition. There is **no
|
current session's model, which is generating by definition. There is **no
|
||||||
@@ -226,9 +226,9 @@ it by whichever path fits your context:
|
|||||||
|
|
||||||
- A reachable octopus HTTP instance (e.g. `http://127.0.0.1:4096`).
|
- A reachable octopus HTTP instance (e.g. `http://127.0.0.1:4096`).
|
||||||
- The target directory (passed as `?directory=`). Defaults to the current
|
- The target directory (passed as `?directory=`). Defaults to the current
|
||||||
session's directory for in-session launches 鈥?see the Directory convention
|
session's directory for in-session launches — see the Directory convention
|
||||||
above; only specify a different one explicitly when targeting another repo.
|
above; only specify a different one explicitly when targeting another repo.
|
||||||
- A known-good `model` (reuse the current session's 鈥?see above). Model IDs
|
- A known-good `model` (reuse the current session's — see above). Model IDs
|
||||||
must match the provider config in `<config-home>/octopus/octopus.jsonc`;
|
must match the provider config in `<config-home>/octopus/octopus.jsonc`;
|
||||||
credentials live in the octopus account keyring (not env), so an isolated
|
credentials live in the octopus account keyring (not env), so an isolated
|
||||||
HOME cannot reuse them.
|
HOME cannot reuse them.
|
||||||
@@ -242,27 +242,27 @@ BASE=http://127.0.0.1:4096
|
|||||||
DIR=/data/octopus
|
DIR=/data/octopus
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 1 鈥?Create the session
|
### Step 1 — Create the session
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
SID=$(curl -s -X POST "$BASE/session?directory=$DIR" \
|
SID=$(curl -s -X POST "$BASE/session?directory=$DIR" \
|
||||||
-H 'content-type: application/json' \
|
-H 'content-type: application/json' \
|
||||||
-d '{"title":"#N 鈥?<short description>","agent":"builder"}' | jq -r .id)
|
-d '{"title":"#N — <short description>","agent":"builder"}' | jq -r .id)
|
||||||
echo "session=$SID"
|
echo "session=$SID"
|
||||||
```
|
```
|
||||||
|
|
||||||
- `POST /session` returns `Session.Info` with `.id` (`groups/session.ts:207-211`).
|
- `POST /session` returns `Session.Info` with `.id` (`groups/session.ts:207-211`).
|
||||||
- `?directory=` points at the repo root. For in-session launches it defaults to
|
- `?directory=` points at the repo root. For in-session launches it defaults to
|
||||||
the **current session's directory** (Directory convention above) 鈥?keep it
|
the **current session's directory** (Directory convention above) — keep it
|
||||||
that way so the new session stays visible in the creator's sidebar; pass an
|
that way so the new session stays visible in the creator's sidebar; pass an
|
||||||
explicit path only when targeting a different repo (a worktree of the same
|
explicit path only when targeting a different repo (a worktree of the same
|
||||||
repo is NOT a directory target 鈥?hand it over via the seed prompt), and
|
repo is NOT a directory target — hand it over via the seed prompt), and
|
||||||
always pass it from cron / CI where no current session exists (server falls
|
always pass it from cron / CI where no current session exists (server falls
|
||||||
back to its own `process.cwd()` otherwise).
|
back to its own `process.cwd()` otherwise).
|
||||||
- `agent` is `builder` (the main agent). Sub-agents (explorer/worker) are
|
- `agent` is `builder` (the main agent). Sub-agents (explorer/worker) are
|
||||||
spawned within a session 鈥?never named here.
|
spawned within a session — never named here.
|
||||||
|
|
||||||
### Step 2 鈥?Deliver the seed prompt (CRITICAL: include a known-good `model`)
|
### Step 2 — Deliver the seed prompt (CRITICAL: include a known-good `model`)
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
curl -s -X POST "$BASE/session/$SID/prompt_async?directory=$DIR" \
|
curl -s -X POST "$BASE/session/$SID/prompt_async?directory=$DIR" \
|
||||||
@@ -278,17 +278,17 @@ curl -s -X POST "$BASE/session/$SID/prompt_async?directory=$DIR" \
|
|||||||
body** when processing starts now (idle session, live runner, noReply
|
body** when processing starts now (idle session, live runner, noReply
|
||||||
injection, or waiting-question preemption), **202** when the message is
|
injection, or waiting-question preemption), **202** when the message is
|
||||||
queued behind a session that is generating with no live runner in this
|
queued behind a session that is generating with no live runner in this
|
||||||
process (wedge/zombie shape 鈥?stored and re-driven on restart drain or the
|
process (wedge/zombie shape — stored and re-driven on restart drain or the
|
||||||
next runner), **202** when `delay_sec` defers delivery to an in-memory
|
next runner), **202** when `delay_sec` defers delivery to an in-memory
|
||||||
delayed wake ([org-internal #3937] A 鈥?see "宸℃妯″紡 / Patrol mode" below), and **409**
|
delayed wake ([org-internal #3937] A — see "巡检模式 / Patrol mode" below), and **409**
|
||||||
when the session is paused (message NOT stored) or the maintenance-mode
|
when the session is paused (message NOT stored) or the maintenance-mode
|
||||||
hold queue is full.
|
hold queue is full.
|
||||||
- **Global route alternative ([org-internal #4307]):** when the client only knows the
|
- **Global route alternative ([org-internal #4307]):** when the client only knows the
|
||||||
sessionID (no `?directory=` routing), `POST /prompt_async` (no path prefix)
|
sessionID (no `?directory=` routing), `POST /prompt_async` (no path prefix)
|
||||||
resolves the instance from the app database by the body's `sessionID` and
|
resolves the instance from the app database by the body's `sessionID` and
|
||||||
delegates to the same delivery core 鈥?same 204/202/409 semantics, same
|
delegates to the same delivery core — same 204/202/409 semantics, same
|
||||||
`delay_sec` contract, body is the session-scoped payload plus a required
|
`delay_sec` contract, body is the session-scoped payload plus a required
|
||||||
`sessionID` field. Unknown sessionID 鈫?404 naming the session-scoped
|
`sessionID` field. Unknown sessionID → 404 naming the session-scoped
|
||||||
route. This is the natural fit for the completion-report POST (fixed port,
|
route. This is the natural fit for the completion-report POST (fixed port,
|
||||||
no directory context).
|
no directory context).
|
||||||
- `delay_sec` (optional, integer 0..86400, else 400) schedules the prompt for
|
- `delay_sec` (optional, integer 0..86400, else 400) schedules the prompt for
|
||||||
@@ -297,16 +297,16 @@ curl -s -X POST "$BASE/session/$SID/prompt_async?directory=$DIR" \
|
|||||||
at intake. **Precedence ([org-internal #3937] review):** `delay_sec` applies only when the
|
at intake. **Precedence ([org-internal #3937] review):** `delay_sec` applies only when the
|
||||||
intake would otherwise start processing now (immediate class). For the
|
intake would otherwise start processing now (immediate class). For the
|
||||||
queued-behind-wedge 202 member and under maintenance queue mode it is
|
queued-behind-wedge 202 member and under maintenance queue mode it is
|
||||||
stripped 鈥?retention semantics win (the server logs the drop; the delay is
|
stripped — retention semantics win (the server logs the drop; the delay is
|
||||||
NOT honored). Pending wakes are **in-memory only 鈥?a server restart abandons
|
NOT honored). Pending wakes are **in-memory only — a server restart abandons
|
||||||
them**; a patrol loop must therefore tolerate a missed wake and re-arm.
|
them**; a patrol loop must therefore tolerate a missed wake and re-arm.
|
||||||
Never use `sleep <big>; curl ...` in a bash tool call to wait 鈥?that blocks
|
Never use `sleep <big>; curl ...` in a bash tool call to wait — that blocks
|
||||||
the turn with zero output (the [org-internal #3937] incident shape); the bash tool now
|
the turn with zero output (the [org-internal #3937] incident shape); the bash tool now
|
||||||
warns on bare sleeps > 120s (long-sleep guard, [org-internal #3937] C).
|
warns on bare sleeps > 120s (long-sleep guard, [org-internal #3937] C).
|
||||||
- `model` is `{ providerID, modelID }` or the shorthand string
|
- `model` is `{ providerID, modelID }` or the shorthand string
|
||||||
`"providerID/modelID"` (split on the FIRST `/`; accepted since [org-internal #4307] 鈥?
|
`"providerID/modelID"` (split on the FIRST `/`; accepted since [org-internal #4307] —
|
||||||
e.g. `"zai-coding-plan/glm-5.2"`). **Do not omit it.** Fill the values from
|
e.g. `"zai-coding-plan/glm-5.2"`). **Do not omit it.** Fill the values from
|
||||||
your own session's model (see "Reuse the current session's model" above) 鈥?
|
your own session's model (see "Reuse the current session's model" above) —
|
||||||
never hardcode a specific model. A string without a `/` is rejected with
|
never hardcode a specific model. A string without a `/` is rejected with
|
||||||
400 naming both accepted forms.
|
400 naming both accepted forms.
|
||||||
- When the seed prompt contains newlines/quotes, build the payload from a file
|
- When the seed prompt contains newlines/quotes, build the payload from a file
|
||||||
@@ -315,87 +315,87 @@ curl -s -X POST "$BASE/session/$SID/prompt_async?directory=$DIR" \
|
|||||||
jq -n --rawfile p seed.txt '{agent:"builder",model:{providerID:"<current providerID>",modelID:"<current modelID>"},parts:[{type:"text",text:$p}]}'
|
jq -n --rawfile p seed.txt '{agent:"builder",model:{providerID:"<current providerID>",modelID:"<current modelID>"},parts:[{type:"text",text:$p}]}'
|
||||||
```
|
```
|
||||||
|
|
||||||
### Step 3 鈥?Verify the session actually generated (do NOT assume success)
|
### Step 3 — Verify the session actually generated (do NOT assume success)
|
||||||
|
|
||||||
HTTP 204/202 鈮?generation success. Poll until an `assistant` message appears. The
|
HTTP 204/202 ≠generation success. Poll until an `assistant` message appears. The
|
||||||
endpoint is **`/session/:id/message` 鈥?SINGULAR**, and it returns a bare array
|
endpoint is **`/session/:id/message` — SINGULAR**, and it returns a bare array
|
||||||
whose elements are `{ info, parts }` with `role` at `.info.role`
|
whose elements are `{ info, parts }` with `role` at `.info.role`
|
||||||
(`groups/session.ts:88,183-194`). Poll the **tail with `limit=1`** 鈥?the
|
(`groups/session.ts:88,183-194`). Poll the **tail with `limit=1`** — the
|
||||||
no-`limit` form loads the ENTIRE transcript server-side (O(messages脳parts) per
|
no-`limit` form loads the ENTIRE transcript server-side (O(messages×parts) per
|
||||||
poll; pitfall #14's 478-message session paid it on every health check), while
|
poll; pitfall #14's 478-message session paid it on every health check), while
|
||||||
`limit=1` returns just the newest message (REQ-F-010 windowing, `message.ts`
|
`limit=1` returns just the newest message (REQ-F-010 windowing, `message.ts`
|
||||||
`page()`):
|
`page()`):
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# wait 5鈥?0s, then poll the TAIL 鈥?never the full transcript
|
# wait 5–10s, then poll the TAIL — never the full transcript
|
||||||
curl -s "$BASE/session/$SID/message?directory=$DIR&limit=1" \
|
curl -s "$BASE/session/$SID/message?directory=$DIR&limit=1" \
|
||||||
| jq '{latest_role: .[0].info.role, latest_id: .[0].info.id}'
|
| jq '{latest_role: .[0].info.role, latest_id: .[0].info.id}'
|
||||||
```
|
```
|
||||||
|
|
||||||
Verdict: `latest_role == "assistant"` 鈫?the first generation completed (the
|
Verdict: `latest_role == "assistant"` → the first generation completed (the
|
||||||
seed is `user`; the newest message flips to `assistant` only once the model
|
seed is `user`; the newest message flips to `assistant` only once the model
|
||||||
answered 鈥?an errored turn also persists an assistant message carrying the 鈿狅笍
|
answered — an errored turn also persists an assistant message carrying the ⚠️
|
||||||
usage-limit part, [org-internal #2912], so check the tail text before declaring success).
|
usage-limit part, [org-internal #2912], so check the tail text before declaring success).
|
||||||
Stays `user` across 2鈥? polls spaced 10鈥?0 s 鈫?**apparent silent stall**;
|
Stays `user` across 2–3 polls spaced 10–30 s → **apparent silent stall**;
|
||||||
almost certainly the model did not resolve to a usable one (fallback landed
|
almost certainly the model did not resolve to a usable one (fallback landed
|
||||||
badly, or you omitted `model`). Redeliver Step 2 with an explicit known-good
|
badly, or you omitted `model`). Redeliver Step 2 with an explicit known-good
|
||||||
`model`. Need everything newer than a known point? Page forward with
|
`model`. Need everything newer than a known point? Page forward with
|
||||||
`after=<cursor>&limit=N` and follow the `X-Next-Cursor` response header 鈥?
|
`after=<cursor>&limit=N` and follow the `X-Next-Cursor` response header —
|
||||||
just never omit `limit`.
|
just never omit `limit`.
|
||||||
|
|
||||||
> 鈿狅笍 Do NOT write `/messages` (plural) 鈥?that path does not exist for listing.
|
> ⚠️ Do NOT write `/messages` (plural) — that path does not exist for listing.
|
||||||
> (`POST /session/:id/message`, same singular path but POST, is the synchronous
|
> (`POST /session/:id/message`, same singular path but POST, is the synchronous
|
||||||
> prompt endpoint; don't confuse the two.)
|
> prompt endpoint; don't confuse the two.)
|
||||||
|
|
||||||
## Launch a session for a specific ticket (#N)
|
## Launch a session for a specific ticket (#N)
|
||||||
|
|
||||||
The common case: an agent (or cron/CI) spins up a fresh main session to work a
|
The common case: an agent (or cron/CI) spins up a fresh main session to work a
|
||||||
tracked issue 鈥?seed prompt composed **from the issue body**, launched on the
|
tracked issue — seed prompt composed **from the issue body**, launched on the
|
||||||
current session's model. **Step-0 owner check is MANDATORY ([org-internal #1803])**: four data
|
current session's model. **Step-0 owner check is MANDATORY ([org-internal #1803])**: four data
|
||||||
sources (session-title scan, assignee/claim, open PRs, remote branches); any
|
sources (session-title scan, assignee/claim, open PRs, remote branches); any
|
||||||
live claim 鈫?ABORT the launch (`session-scope-guard.md` points here for that
|
live claim → ABORT the launch (`session-scope-guard.md` points here for that
|
||||||
pre-step). Full recipe 鈥?owner-check scan script with `X-Total-Count` /
|
pre-step). Full recipe — owner-check scan script with `X-Total-Count` /
|
||||||
`X-Has-More` pagination handling ([org-internal #3190]), claim-first atomic 3-step ([org-internal #2297],
|
`X-Has-More` pagination handling ([org-internal #3190]), claim-first atomic 3-step ([org-internal #2297],
|
||||||
+ one-command provisioning via `script/claim-provision.sh`, [org-internal #3642]),
|
+ one-command provisioning via `script/claim-provision.sh`, [org-internal #3642]),
|
||||||
seed-prompt MUSTs, backend duplicate-ticket hard guard ([org-internal #1989], on by default
|
seed-prompt MUSTs, backend duplicate-ticket hard guard ([org-internal #1989], on by default
|
||||||
[org-internal #2350]), launch + poll commands: `reference/ticket-recipe.md` (read BEFORE
|
[org-internal #2350]), launch + poll commands: `reference/ticket-recipe.md` (read BEFORE
|
||||||
creating any ticket-driven session).
|
creating any ticket-driven session).
|
||||||
|
|
||||||
## Completion-report protocol (瀹屾垚鍥炴姤鍗忚, [org-internal #2374])
|
## Completion-report protocol (完成回报协议, [org-internal #2374])
|
||||||
|
|
||||||
`prompt_async` is fire-and-forget **for the launcher too**: without this
|
`prompt_async` is fire-and-forget **for the launcher too**: without this
|
||||||
protocol the orchestrator has NO push channel and must poll forever ([org-internal #2366]).
|
protocol the orchestrator has NO push channel and must poll forever ([org-internal #2366]).
|
||||||
The worker actively reports `status=done|blocked|handoff` back into the
|
The worker actively reports `status=done|blocked|handoff` back into the
|
||||||
orchestrator's session via `prompt_async`, with an issue-comment fallback.
|
orchestrator's session via `prompt_async`, with an issue-comment fallback.
|
||||||
Default report format is `branch=<ref>`-based 鈥?workers never open PRs
|
Default report format is `branch=<ref>`-based — workers never open PRs
|
||||||
(TD-678/[org-internal #4425]); `pr=#N` appears only on `uncoordinated` self-opens.
|
(TD-678/[org-internal #4425]); `pr=#N` appears only on `uncoordinated` self-opens.
|
||||||
Hard constraints: a headless worker MUST NOT call the `question` tool
|
Hard constraints: a headless worker MUST NOT call the `question` tool
|
||||||
([org-internal #2378] 鈥?directional/irreversible decisions are `status=blocked` reports,
|
([org-internal #2378] — directional/irreversible decisions are `status=blocked` reports,
|
||||||
then STOP), and MUST NOT self-merge its PR / self-close the issue / touch
|
then STOP), and MUST NOT self-merge its PR / self-close the issue / touch
|
||||||
`main` ([org-internal #2386] 鈥?keep the PR open; the orchestrator merges and closes). Full
|
`main` ([org-internal #2386] — keep the PR open; the orchestrator merges and closes). Full
|
||||||
protocol 鈥?orchestrator-side seed block (verbatim template), worker-side
|
protocol — orchestrator-side seed block (verbatim template), worker-side
|
||||||
steps, decision-authority layering: `reference/completion-report.md` (read
|
steps, decision-authority layering: `reference/completion-report.md` (read
|
||||||
BEFORE composing a seed prompt for any worker you need to hear back from).
|
BEFORE composing a seed prompt for any worker you need to hear back from).
|
||||||
|
|
||||||
## Post-launch lifecycle: health check, hung rescue, stale-worldview correction ([org-internal #2459])
|
## Post-launch lifecycle: health check, hung rescue, stale-worldview correction ([org-internal #2459])
|
||||||
|
|
||||||
Launching is half the job 鈥?a headless worker can hang silently or wake with
|
Launching is half the job — a headless worker can hang silently or wake with
|
||||||
a stale worldview. Health check = two polls 30鈥?0 s apart (`.state` + `progress`
|
a stale worldview. Health check = two polls 30–60 s apart (`.state` + `progress`
|
||||||
+ message count); `state=generating` with ALL counters frozen across BOTH
|
+ message count); `state=generating` with ALL counters frozen across BOTH
|
||||||
samples = hung 鈫?abort FIRST (`POST /session/:id/abort`), then re-wake with a
|
samples = hung → abort FIRST (`POST /session/:id/abort`), then re-wake with a
|
||||||
known-good model + the anti-re-hang clause. Stale worldview 鈫?fact-baseline
|
known-good model + the anti-re-hang clause. Stale worldview → fact-baseline
|
||||||
injection (only the CURRENT authority injects; if YOU might be the stale one,
|
injection (only the CURRENT authority injects; if YOU might be the stale one,
|
||||||
verify identity against the durable record first). Full runbooks 鈥?verdict
|
verify identity against the durable record first). Full runbooks — verdict
|
||||||
table + [org-internal #3215] dual-sample rule, abort + re-wake sequence, fact-baseline
|
table + [org-internal #3215] dual-sample rule, abort + re-wake sequence, fact-baseline
|
||||||
structure, authorization asymmetry: `reference/lifecycle-ops.md` (read when
|
structure, authorization asymmetry: `reference/lifecycle-ops.md` (read when
|
||||||
managing a launched session). Quota wall 鈥?the worker's turn killed by a
|
managing a launched session). Quota wall — the worker's turn killed by a
|
||||||
terminal usage-limit error (looks like silent completion: no report, ticket
|
terminal usage-limit error (looks like silent completion: no report, ticket
|
||||||
stalled) 鈥?has its own runbook: detection signals (finish=error 鈿狅笍 tail,
|
stalled) — has its own runbook: detection signals (finish=error ⚠️ tail,
|
||||||
provider quota markers), provider-switch re-wake, fact-baseline template,
|
provider quota markers), provider-switch re-wake, fact-baseline template,
|
||||||
pre-dispatch provider check: `reference/quota-wall-recovery.md` ([org-internal #3669], evidence
|
pre-dispatch provider check: `reference/quota-wall-recovery.md` ([org-internal #3669], evidence
|
||||||
[org-internal #3627]; read when a worker goes silent mid-task).
|
[org-internal #3627]; read when a worker goes silent mid-task).
|
||||||
|
|
||||||
## 宸℃妯″紡 / Patrol mode (delayed self-wake, [org-internal #3937])
|
## 巡检模式 / Patrol mode (delayed self-wake, [org-internal #3937])
|
||||||
|
|
||||||
A patrol loop periodically nudges a headless session without holding a
|
A patrol loop periodically nudges a headless session without holding a
|
||||||
connection or burning a turn on a blocking sleep. Since [org-internal #3937] A the server
|
connection or burning a turn on a blocking sleep. Since [org-internal #3937] A the server
|
||||||
@@ -414,82 +414,82 @@ curl -s -X POST "$BASE/session/$SID/prompt_async?directory=$DIR" \
|
|||||||
|
|
||||||
Rules of the road:
|
Rules of the road:
|
||||||
|
|
||||||
- **202 + `delay_sec` 鈮?queued-behind-wedge 202.** Both are 202, but the
|
- **202 + `delay_sec` ≠queued-behind-wedge 202.** Both are 202, but the
|
||||||
delayed wake holds the prompt in memory and delivers it exactly once when
|
delayed wake holds the prompt in memory and delivers it exactly once when
|
||||||
the delay elapses (session must have been idle at intake; `0` equals
|
the delay elapses (session must have been idle at intake; `0` equals
|
||||||
omission 鈫?immediate 204 path). If the session was NOT idle at intake, the
|
omission → immediate 204 path). If the session was NOT idle at intake, the
|
||||||
intake keeps the queued semantics and `delay_sec` is ignored (logged
|
intake keeps the queued semantics and `delay_sec` is ignored (logged
|
||||||
server-side) 鈥?the same precedence as maintenance queue mode, where the
|
server-side) — the same precedence as maintenance queue mode, where the
|
||||||
intake is retained and `delay_sec` never delays the flush.
|
intake is retained and `delay_sec` never delays the flush.
|
||||||
- **Paused at wake 鈫?dropped, not stored.** The wake's `prompt()` rejects and
|
- **Paused at wake → dropped, not stored.** The wake's `prompt()` rejects and
|
||||||
the server publishes an error event; the message is never stored. A patrol
|
the server publishes an error event; the message is never stored. A patrol
|
||||||
driver should check session state before assuming delivery.
|
driver should check session state before assuming delivery.
|
||||||
- **Restart abandons pending wakes.** They are in-memory server fibers 鈥?no
|
- **Restart abandons pending wakes.** They are in-memory server fibers — no
|
||||||
persistence, no drain. After any server restart the patrol driver owns
|
persistence, no drain. After any server restart the patrol driver owns
|
||||||
re-arming the next wake; treat a missed wake as expected, not as an error.
|
re-arming the next wake; treat a missed wake as expected, not as an error.
|
||||||
- **Latest wake wins, per session ([org-internal #4069]).** Arming `delay_sec` on a session
|
- **Latest wake wins, per session ([org-internal #4069]).** Arming `delay_sec` on a session
|
||||||
that already has a pending wake SUPERSEDES it: the previous countdown is
|
that already has a pending wake SUPERSEDES it: the previous countdown is
|
||||||
cancelled, only the latest wake fires. Re-arming each turn is the intended
|
cancelled, only the latest wake fires. Re-arming each turn is the intended
|
||||||
patrol pattern 鈥?a superseded wake never delivers its prompt and publishes
|
patrol pattern — a superseded wake never delivers its prompt and publishes
|
||||||
no error. (Pre-[org-internal #4069] stacks both FIFO 鈥?on old binaries, re-arm only after
|
no error. (Pre-[org-internal #4069] stacks both FIFO — on old binaries, re-arm only after
|
||||||
the previous wake fired.)
|
the previous wake fired.)
|
||||||
- **Never emulate a delay with the bash tool.** `sleep 900; curl ...` blocks
|
- **Never emulate a delay with the bash tool.** `sleep 900; curl ...` blocks
|
||||||
the turn with zero output and gets manually aborted as a hang (the [org-internal #3937]
|
the turn with zero output and gets manually aborted as a hang (the [org-internal #3937]
|
||||||
incident). The bash tool now prepends a `<shell_warning>` on bare sleeps
|
incident). The bash tool now prepends a `<shell_warning>` on bare sleeps
|
||||||
beyond 120s ([org-internal #3937] C) 鈥?treat that warning as a redirect to this section.
|
beyond 120s ([org-internal #3937] C) — treat that warning as a redirect to this section.
|
||||||
|
|
||||||
## Do NOT use this skill when
|
## Do NOT use this skill when
|
||||||
|
|
||||||
- A **persistent client** (Web UI / interactive TUI) is attached 鈥?those use the
|
- A **persistent client** (Web UI / interactive TUI) is attached — those use the
|
||||||
synchronous `prompt` endpoint (it blocks until generation completes) and send
|
synchronous `prompt` endpoint (it blocks until generation completes) and send
|
||||||
`model` automatically. Using `prompt_async` there gains nothing and loses the
|
`model` automatically. Using `prompt_async` there gains nothing and loses the
|
||||||
in-line response. This includes the rescue runbook: a hung session driven by
|
in-line response. This includes the rescue runbook: a hung session driven by
|
||||||
a persistent client is the client user's to handle (refresh / re-prompt from
|
a persistent client is the client user's to handle (refresh / re-prompt from
|
||||||
the UI) 鈥?abort + re-wake targets headless / managed sessions only.
|
the UI) — abort + re-wake targets headless / managed sessions only.
|
||||||
- You want the response **inline** in the same HTTP call 鈥?`prompt_async` is
|
- You want the response **inline** in the same HTTP call — `prompt_async` is
|
||||||
fire-and-forget; use the synchronous `prompt` endpoint instead.
|
fire-and-forget; use the synchronous `prompt` endpoint instead.
|
||||||
|
|
||||||
## Common pitfalls
|
## Common pitfalls
|
||||||
|
|
||||||
| # | Pitfall | Fix |
|
| # | Pitfall | Fix |
|
||||||
| --- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
| --- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||||
| 1 | Omitting `model` / relying on the fallback 鈫?apparent silent stall (most common) | Always send a known-good `model:{providerID,modelID}` 鈥?reuse the current session's |
|
| 1 | Omitting `model` / relying on the fallback → apparent silent stall (most common) | Always send a known-good `model:{providerID,modelID}` — reuse the current session's |
|
||||||
| 2 | Listing endpoint written `/messages` (plural) 鈫?404 | Use `/session/:id/message` (singular); returns a bare array, role at `.info.role` |
|
| 2 | Listing endpoint written `/messages` (plural) → 404 | Use `/session/:id/message` (singular); returns a bare array, role at `.info.role` |
|
||||||
| 3 | Copying the session record's model verbatim into the payload | Remap field names: session `.model.id` 鈫?payload `modelID` (session uses `id`, payload uses `modelID`) |
|
| 3 | Copying the session record's model verbatim into the payload | Remap field names: session `.model.id` → payload `modelID` (session uses `id`, payload uses `modelID`) |
|
||||||
| 4 | Stale `octopus` binary rejects newer config schema (`Unrecognized key`) | Use the source CLI `bun run ./src/index.ts ...`; the config itself is valid |
|
| 4 | Stale `octopus` binary rejects newer config schema (`Unrecognized key`) | Use the source CLI `bun run ./src/index.ts ...`; the config itself is valid |
|
||||||
| 5 | Missing `?directory=` query param | In-session launch: default to the current session's directory. Cron/CI (no current session): pass the repo root explicitly 鈥?server falls back to its own `process.cwd()` |
|
| 5 | Missing `?directory=` query param | In-session launch: default to the current session's directory. Cron/CI (no current session): pass the repo root explicitly — server falls back to its own `process.cwd()` |
|
||||||
| 6 | Using synchronous `prompt` for headless launch | `prompt` blocks until done 鈥?wrong for fire-and-forget; use `prompt_async` |
|
| 6 | Using synchronous `prompt` for headless launch | `prompt` blocks until done — wrong for fire-and-forget; use `prompt_async` |
|
||||||
| 7 | Shell-escaping errors in a multi-line seed prompt | Build the payload with `jq -n --rawfile` from a file |
|
| 7 | Shell-escaping errors in a multi-line seed prompt | Build the payload with `jq -n --rawfile` from a file |
|
||||||
| 8 | Verifying models via `GET /model` 鈥?no such endpoint (404) | Use `GET /provider` 鈫?`.connected` (provider live check); `list-models` tool also works. `GET /config/providers` lists _configured_ models only |
|
| 8 | Verifying models via `GET /model` — no such endpoint (404) | Use `GET /provider` → `.connected` (provider live check); `list-models` tool also works. `GET /config/providers` lists _configured_ models only |
|
||||||
| 9 | Launching a session for a ticket another live session already owns 鈫?conflicting diffs, duplicate work ([org-internal #1744]/[org-internal #1753]) | Run the Step-0 owner check (`GET /session` title scan + worktree/branch/PR scan); ABORT and report on conflict |
|
| 9 | Launching a session for a ticket another live session already owns → conflicting diffs, duplicate work ([org-internal #1744]/[org-internal #1753]) | Run the Step-0 owner check (`GET /session` title scan + worktree/branch/PR scan); ABORT and report on conflict |
|
||||||
| 10 | Creating a worktree / fresh checkout for the new session (unnecessary `bun install` + `.codegraph/` rebuild cost) | Don't 鈥?for _general_ launches: a session is a conversation pointed at a directory, so reuse the current workspace; worktree creation is a separate _workflow_ decision the launched session makes if/when it starts a workflow. **Ticket-recipe carve-out ([org-internal #2297]):** the ticket's work happens in a dedicated worktree anyway (worktree discipline), and claim-first pushes the workflow branch before launch 鈥?so the launcher pre-builds the worktree as the claim carrier + ready workspace, and the session reuses it (cost is front-loaded, not wasted). The worktree path goes in the claim comment + seed prompt; `?directory=` stays on the creator's workspace |
|
| 10 | Creating a worktree / fresh checkout for the new session (unnecessary `bun install` + `.codegraph/` rebuild cost) | Don't — for _general_ launches: a session is a conversation pointed at a directory, so reuse the current workspace; worktree creation is a separate _workflow_ decision the launched session makes if/when it starts a workflow. **Ticket-recipe carve-out ([org-internal #2297]):** the ticket's work happens in a dedicated worktree anyway (worktree discipline), and claim-first pushes the workflow branch before launch — so the launcher pre-builds the worktree as the claim carrier + ready workspace, and the session reuses it (cost is front-loaded, not wasted). The worktree path goes in the claim comment + seed prompt; `?directory=` stays on the creator's workspace |
|
||||||
| 11 | Launching a worker and never hearing back 鈥?orchestrator polls a few times, stops, loses visibility ([org-internal #2374]) | Append the completion-report block to the seed prompt (Completion-report protocol section): worker `prompt_async`s a one-line status back into the orchestrator session on done/blocked/handoff, with issue-comment fallback |
|
| 11 | Launching a worker and never hearing back — orchestrator polls a few times, stops, loses visibility ([org-internal #2374]) | Append the completion-report block to the seed prompt (Completion-report protocol section): worker `prompt_async`s a one-line status back into the orchestrator session on done/blocked/handoff, with issue-comment fallback |
|
||||||
| 12 | Headless worker calls the `question` tool 鈫?no attached client: the question deadlocks in a queue, or `auto_approve` silently auto-approves a direction-setting decision ([org-internal #2378]) | NEVER call `question` from a headless worker. Decision boundary (Completion-report protocol section): directional/irreversible decision 鈫?`status=blocked` report + one-line decision point, then STOP; the orchestrator is the sole human-decision entry point |
|
| 12 | Headless worker calls the `question` tool → no attached client: the question deadlocks in a queue, or `auto_approve` silently auto-approves a direction-setting decision ([org-internal #2378]) | NEVER call `question` from a headless worker. Decision boundary (Completion-report protocol section): directional/irreversible decision → `status=blocked` report + one-line decision point, then STOP; the orchestrator is the sole human-decision entry point |
|
||||||
| 13 | Headless worker self-merges its PR / self-closes the issue once the PR looks ready, despite a seed instruction to keep it open ([org-internal #2386]; N-02 [org-internal #2367] / N-03 [org-internal #2368], commits `d1565c99` / `040f21b7`) | Self-merge / self-close / touching `main` are irreversible directional actions 鈫?blocked by the Decision boundary. PR ready 鈫?keep it OPEN, report `status=done branch=<ref>`; the orchestrator opens/admits the PR, merges and closes uniformly |
|
| 13 | Headless worker self-merges its PR / self-closes the issue once the PR looks ready, despite a seed instruction to keep it open ([org-internal #2386]; N-02 [org-internal #2367] / N-03 [org-internal #2368], commits `d1565c99` / `040f21b7`) | Self-merge / self-close / touching `main` are irreversible directional actions → blocked by the Decision boundary. PR ready → keep it OPEN, report `status=done branch=<ref>`; the orchestrator opens/admits the PR, merges and closes uniformly |
|
||||||
| 14 | Trusting `state=generating` as "alive" 鈥?a hung stream goes unrescued (session A, 2026-08-16: `generating` 6+ min, count frozen at 478) | Poll state AND message count twice 30鈥?0 s apart; frozen count = hung 鈫?abort + re-wake (Post-launch lifecycle section) |
|
| 14 | Trusting `state=generating` as "alive" — a hung stream goes unrescued (session A, 2026-08-16: `generating` 6+ min, count frozen at 478) | Poll state AND message count twice 30–60 s apart; frozen count = hung → abort + re-wake (Post-launch lifecycle section) |
|
||||||
| 15 | Re-sending a wake prompt to a hung session and waiting 鈥?queued prompts are never consumed while the stream is dead | Abort FIRST (`POST /session/:id/abort` 鈫?200, verify `idle`), THEN re-send; a prompt queued before the abort may still never fire 鈥?always re-deliver |
|
| 15 | Re-sending a wake prompt to a hung session and waiting — queued prompts are never consumed while the stream is dead | Abort FIRST (`POST /session/:id/abort` → 200, verify `idle`), THEN re-send; a prompt queued before the abort may still never fire — always re-deliver |
|
||||||
| 16 | A worker woken from restart / long idle acts on its stale worldview 鈥?re-dispatches superseded work or claims authority it no longer holds (2026-08-16: revived worker re-ran an already-published FAIL verify; ops session announced an inherited "orchestrator" takeover) | Fact-baseline injection (Post-launch lifecycle section): authoritative facts + invalidated assumptions + one concrete re-assignment. Only the CURRENT authority injects; a session that suspects IT is stale verifies identity against the durable record first (`rules/compact.md` identity clause) |
|
| 16 | A worker woken from restart / long idle acts on its stale worldview — re-dispatches superseded work or claims authority it no longer holds (2026-08-16: revived worker re-ran an already-published FAIL verify; ops session announced an inherited "orchestrator" takeover) | Fact-baseline injection (Post-launch lifecycle section): authoritative facts + invalidated assumptions + one concrete re-assignment. Only the CURRENT authority injects; a session that suspects IT is stale verifies identity against the durable record first (`rules/compact.md` identity clause) |
|
||||||
| 17 | Worker goes silent mid-task: provider quota wall killed the turn (usage limit / 429) 鈥?no completion-report, no blocked report; from the issue side indistinguishable from silent completion (2026-08-29 [org-internal #3627]: opencode-go `weekly usage limit reached` after the 13:18 Round-1 FAIL; ticket stalled until manually re-woken) | Detect via tail probe (`finish=error` + 鈿狅笍 usage-limit part, `metadata.reason` in the quota family) + `GET /provider` quota markers; re-wake on a known-good provider with the fact-baseline template 鈥?`reference/quota-wall-recovery.md` ([org-internal #3669]). Prevent: pre-dispatch provider check + the seed's 閰嶉鑷姤 clause (`reference/completion-report.md`, [org-internal #3669]) 鈥?quota exhaustion is a `status=blocked reason=quota-exhausted` report, not a silent death |
|
| 17 | Worker goes silent mid-task: provider quota wall killed the turn (usage limit / 429) — no completion-report, no blocked report; from the issue side indistinguishable from silent completion (2026-08-29 [org-internal #3627]: opencode-go `weekly usage limit reached` after the 13:18 Round-1 FAIL; ticket stalled until manually re-woken) | Detect via tail probe (`finish=error` + ⚠️ usage-limit part, `metadata.reason` in the quota family) + `GET /provider` quota markers; re-wake on a known-good provider with the fact-baseline template — `reference/quota-wall-recovery.md` ([org-internal #3669]). Prevent: pre-dispatch provider check + the seed's 配额自报 clause (`reference/completion-report.md`, [org-internal #3669]) — quota exhaustion is a `status=blocked reason=quota-exhausted` report, not a silent death |
|
||||||
|
|
||||||
## Known limitation (separate from this workaround)
|
## Known limitation (separate from this workaround)
|
||||||
|
|
||||||
A missing `model` producing an **apparent silent stall** (no surfaced error, no
|
A missing `model` producing an **apparent silent stall** (no surfaced error, no
|
||||||
generation) is an observability / robustness gap. Current code does NOT
|
generation) is an observability / robustness gap. Current code does NOT
|
||||||
hard-require `model` 鈥?it fills it via a fallback chain (`prompt.ts:858`) and any
|
hard-require `model` — it fills it via a fallback chain (`prompt.ts:858`) and any
|
||||||
resolution failure surfaces as a published `Session.Event.Error` on the
|
resolution failure surfaces as a published `Session.Event.Error` on the
|
||||||
synchronous path (`prompt.ts:804-822`), but the **async** `prompt_async` handler
|
synchronous path (`prompt.ts:804-822`), but the **async** `prompt_async` handler
|
||||||
catches that cause (`forkPromptDelivery` in `handlers/session.ts`) so the HTTP
|
catches that cause (`forkPromptDelivery` in `handlers/session.ts`) so the HTTP
|
||||||
caller never sees it. The proper fix is to surface the error (or reject unusable
|
caller never sees it. The proper fix is to surface the error (or reject unusable
|
||||||
models at the boundary) so a stalled session is diagnosable. Until then, **always
|
models at the boundary) so a stalled session is diagnosable. Until then, **always
|
||||||
provide an explicit known-good `model`** 鈥?this skill is the documented
|
provide an explicit known-good `model`** — this skill is the documented
|
||||||
workaround (see [org-internal #1695] "閬楃暀 minor").
|
workaround (see [org-internal #1695] "遗留 minor").
|
||||||
|
|
||||||
**Update ([org-internal #2912], 2026-08-20)**: usage-limit turn failures (HTTP 429 /
|
**Update ([org-internal #2912], 2026-08-20)**: usage-limit turn failures (HTTP 429 /
|
||||||
FreeUsageLimitError / GoUsageLimitError) are no longer silent shells. When such
|
FreeUsageLimitError / GoUsageLimitError) are no longer silent shells. When such
|
||||||
a turn terminates (retry schedule ends or the spin is aborted), the errored
|
a turn terminates (retry schedule ends or the spin is aborted), the errored
|
||||||
assistant message persists `finish=error` plus a human-readable 鈿狅笍 text part
|
assistant message persists `finish=error` plus a human-readable ⚠️ text part
|
||||||
carrying provider, limit reason, and reset hint (`metadata: { error: true,
|
carrying provider, limit reason, and reset hint (`metadata: { error: true,
|
||||||
reason, provider }`) 鈥?visible via `GET /session/:id/message` and rendered in
|
reason, provider }`) — visible via `GET /session/:id/message` and rendered in
|
||||||
the web UI like any assistant text. Pitfall #14's double-poll is still the
|
the web UI like any assistant text. Pitfall #14's double-poll is still the
|
||||||
liveness check while a 429 retry spin is IN progress (the spin itself retries
|
liveness check while a 429 retry spin is IN progress (the spin itself retries
|
||||||
with backoff until interrupted); the explicit error part only appears once the
|
with backoff until interrupted); the explicit error part only appears once the
|
||||||
@@ -502,13 +502,13 @@ turn ends. The general async-path observability gap above remains tracked by
|
|||||||
- current-model reuse, and corrects the endpoint/claim against current code).
|
- current-model reuse, and corrects the endpoint/claim against current code).
|
||||||
- Completion-report protocol: **[org-internal #2374]** (worker-side active report back to the
|
- Completion-report protocol: **[org-internal #2374]** (worker-side active report back to the
|
||||||
launching orchestrator session; fallback to issue comment).
|
launching orchestrator session; fallback to issue comment).
|
||||||
- Quota-wall recovery recipe: **[org-internal #3669]** (evidence instance [org-internal #3627]) 鈥?detect a
|
- Quota-wall recovery recipe: **[org-internal #3669]** (evidence instance [org-internal #3627]) — detect a
|
||||||
worker killed by a provider usage limit, re-wake on a known-good provider,
|
worker killed by a provider usage limit, re-wake on a known-good provider,
|
||||||
pre-dispatch prevention.
|
pre-dispatch prevention.
|
||||||
- Decision boundary: **[org-internal #2378]** (headless worker MUST NOT call `question`;
|
- Decision boundary: **[org-internal #2378]** (headless worker MUST NOT call `question`;
|
||||||
directional decisions go through `status=blocked` reports to the
|
directional decisions go through `status=blocked` reports to the
|
||||||
orchestrator) and **[org-internal #2386]** (the worker MUST NOT self-merge its PR,
|
orchestrator) and **[org-internal #2386]** (the worker MUST NOT self-merge its PR,
|
||||||
self-close the issue, or touch `main` 鈥?the orchestrator merges and closes
|
self-close the issue, or touch `main` — the orchestrator merges and closes
|
||||||
uniformly).
|
uniformly).
|
||||||
- Correction of the prior misdiagnosis: **[org-internal #1691]** (closed Invalid), comment 9205.
|
- Correction of the prior misdiagnosis: **[org-internal #1691]** (closed Invalid), comment 9205.
|
||||||
- Synchronous client always sends `model`:
|
- Synchronous client always sends `model`:
|
||||||
@@ -519,29 +519,29 @@ turn ends. The general async-path observability gap above remains tracked by
|
|||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
**On-demand references** (NOT injected 鈥?read at the declared timing):
|
**On-demand references** (NOT injected — read at the declared timing):
|
||||||
|
|
||||||
- `reference/ticket-recipe.md` 鈥?ticket-driven launch (`Launch a session for a
|
- `reference/ticket-recipe.md` — ticket-driven launch (`Launch a session for a
|
||||||
specific ticket (#N)`): read BEFORE creating any session for issue #N 鈥?
|
specific ticket (#N)`): read BEFORE creating any session for issue #N —
|
||||||
Step-0 owner check, seed-prompt composition, launch + poll commands.
|
Step-0 owner check, seed-prompt composition, launch + poll commands.
|
||||||
- `reference/completion-report.md` 鈥?completion-report protocol (瀹屾垚鍥炴姤鍗忚,
|
- `reference/completion-report.md` — completion-report protocol (完成回报协议,
|
||||||
[org-internal #2374]): read BEFORE composing a seed prompt for a worker you need to hear
|
[org-internal #2374]): read BEFORE composing a seed prompt for a worker you need to hear
|
||||||
back from 鈥?orchestrator block template, worker-side report steps, decision
|
back from — orchestrator block template, worker-side report steps, decision
|
||||||
boundary ([org-internal #2378] / [org-internal #2386]), quota self-report clause ([org-internal #3669]).
|
boundary ([org-internal #2378] / [org-internal #2386]), quota self-report clause ([org-internal #3669]).
|
||||||
- `reference/lifecycle-ops.md` 鈥?post-launch lifecycle ([org-internal #2459]): read when
|
- `reference/lifecycle-ops.md` — post-launch lifecycle ([org-internal #2459]): read when
|
||||||
managing a launched session 鈥?health-check polling, hung-stream diagnosis
|
managing a launched session — health-check polling, hung-stream diagnosis
|
||||||
(state=generating + frozen counters), abort + re-wake rescue,
|
(state=generating + frozen counters), abort + re-wake rescue,
|
||||||
fact-baseline injection, authorization asymmetry.
|
fact-baseline injection, authorization asymmetry.
|
||||||
- `reference/quota-wall-recovery.md` 鈥?quota-wall recovery ([org-internal #3669], evidence
|
- `reference/quota-wall-recovery.md` — quota-wall recovery ([org-internal #3669], evidence
|
||||||
[org-internal #3627]): read when a worker goes silent mid-task (suspected provider usage
|
[org-internal #3627]): read when a worker goes silent mid-task (suspected provider usage
|
||||||
limit) or BEFORE dispatching a long-running worker 鈥?detection signals
|
limit) or BEFORE dispatching a long-running worker — detection signals
|
||||||
(finish=error 鈿狅笍 tail, provider quota markers, log grep patterns),
|
(finish=error ⚠️ tail, provider quota markers, log grep patterns),
|
||||||
provider-switch re-wake + fact-baseline template, pre-dispatch provider
|
provider-switch re-wake + fact-baseline template, pre-dispatch provider
|
||||||
check, quota self-report clause.
|
check, quota self-report clause.
|
||||||
|
|
||||||
- `rules/headless-session-ops` wiki page 鈥?authoritative reference (this
|
- `rules/headless-session-ops` wiki page — authoritative reference (this
|
||||||
skill is its runtime carrier; link in the Directory-convention note above).
|
skill is its runtime carrier; link in the Directory-convention note above).
|
||||||
- `core/rules/session-scope-guard.md` 鈥?owner-check + claim-first rule
|
- `core/rules/session-scope-guard.md` — owner-check + claim-first rule
|
||||||
(the ticket recipe's Step 0 is its launch-time application).
|
(the ticket recipe's Step 0 is its launch-time application).
|
||||||
- `core/rules/compact.md` 鈥?identity-verification clause backing the
|
- `core/rules/compact.md` — identity-verification clause backing the
|
||||||
authorization-asymmetry rule (`reference/lifecycle-ops.md`).
|
authorization-asymmetry rule (`reference/lifecycle-ops.md`).
|
||||||
|
|||||||
@@ -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(见 TERMINOLOGY)search(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(见 TERMINOLOGY)create` → 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".}
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -2,41 +2,41 @@
|
|||||||
name: project-kickoff
|
name: project-kickoff
|
||||||
description: >
|
description: >
|
||||||
Use ONLY when the user asks about project setup, kickoff, or project
|
Use ONLY when the user asks about project setup, kickoff, or project
|
||||||
initialization ("kickoff", "project setup", "椤圭洰鍒濆鍖?, "椤圭洰閰嶇疆").
|
initialization ("kickoff", "project setup", "项目初始化", "项目配置").
|
||||||
Provides guidance on the octopus project kickoff workflow: checking readiness,
|
Provides guidance on the octopus project kickoff workflow: checking readiness,
|
||||||
running /kickoff or octopus kickoff, configuring codegraph, and understanding
|
running /kickoff or octopus kickoff, configuring codegraph, and understanding
|
||||||
project scaffolding. Do not use for application-level configuration 鈥?
|
project scaffolding. Do not use for application-level configuration —
|
||||||
this skill covers the octopus project lifecycle, not user application code.
|
this skill covers the octopus project lifecycle, not user application code.
|
||||||
triggers:
|
triggers:
|
||||||
# High-discrimination phrases only ([org-internal #4525]): bare "setup"/"init"/"閰嶇疆" fire
|
# High-discrimination phrases only ([org-internal #4525]): bare "setup"/"init"/"配置" fire
|
||||||
# on any configuration mention, and the skill gate folds the previous
|
# on any configuration mention, and the skill gate folds the previous
|
||||||
# assistant turn on approval turns 鈥?one stray "閰嶇疆" in a reply re-fires
|
# assistant turn on approval turns — one stray "配置" in a reply re-fires
|
||||||
# the gate every turn. Keep triggers to phrases that name THIS workflow;
|
# the gate every turn. Keep triggers to phrases that name THIS workflow;
|
||||||
# generic setup/init questions still route via the description in the
|
# generic setup/init questions still route via the description in the
|
||||||
# skills listing.
|
# skills listing.
|
||||||
- kickoff
|
- kickoff
|
||||||
- project setup
|
- project setup
|
||||||
- 椤圭洰鍒濆鍖?
|
- 项目初始化
|
||||||
- 椤圭洰閰嶇疆
|
- 项目配置
|
||||||
role: Producer
|
role: Producer
|
||||||
---
|
---
|
||||||
|
|
||||||
> Core 涓珛鐗堬紙Increment 6a 鏀瑰啓锛屽師 deferHard verbatimDir锛夈€傛満鍒躲€佺粨鏋勪笌 frontmatter 淇濇寔锛涘疄渚嬫湳璇紙宸ュ叿鍚嶃€佽矾寰勩€佸伐鍗曞彿锛夋寜 `core/adapters/TERMINOLOGY.md` 缁戝畾鍒板叿浣撳疄渚嬨€?
|
> Core 中立版(Increment 6a 改写,原 deferHard verbatimDir)。机制、结构与 frontmatter 保持;实例术语(工具名、路径、工单号)按 `core/adapters/TERMINOLOGY.md` 绑定到具体实例。
|
||||||
# Project Kickoff
|
# Project Kickoff
|
||||||
|
|
||||||
## 1. Overview
|
## 1. Overview
|
||||||
|
|
||||||
Project kickoff is the octopus project initialization workflow that gets a project from zero to fully configured in one command. It covers readiness detection 鈫?setup 鈫?scaffold 鈫?config 鈫?codegraph init 鈫?skills overlay pull, producing a project that is ready for the full SDLC pipeline without manual configuration steps.
|
Project kickoff is the octopus project initialization workflow that gets a project from zero to fully configured in one command. It covers readiness detection → setup → scaffold → config → codegraph init → skills overlay pull, producing a project that is ready for the full SDLC pipeline without manual configuration steps.
|
||||||
|
|
||||||
## 2. When This Skill Activates
|
## 2. When This Skill Activates
|
||||||
|
|
||||||
This skill activates when the user's text matches any of the trigger keywords:
|
This skill activates when the user's text matches any of the trigger keywords:
|
||||||
|
|
||||||
- `kickoff` 鈥?asking about the `/kickoff` command or the kickoff workflow
|
- `kickoff` — asking about the `/kickoff` command or the kickoff workflow
|
||||||
- `project setup` 鈥?asking how to set up a project in octopus
|
- `project setup` — asking how to set up a project in octopus
|
||||||
- `椤圭洰鍒濆鍖朻 / `椤圭洰閰嶇疆` 鈥?asking how to initialize or configure an octopus project (Chinese)
|
- `项目初始化` / `项目配置` — asking how to initialize or configure an octopus project (Chinese)
|
||||||
|
|
||||||
Generic "setup"/"init"/"閰嶇疆" mentions are deliberately NOT triggers ([org-internal #4525]): they collide with everyday configuration talk, and the hard skill gate folds the previous assistant turn into its match text on approval turns 鈥?a passing "鎴栭厤缃?x: false" in a reply would re-fire the gate every turn. `/init` note: `/init` generates AGENTS.md via LLM; kickoff uses the managed template from `octopus setup` 鈥?the Builder distinguishes based on context.
|
Generic "setup"/"init"/"配置" mentions are deliberately NOT triggers ([org-internal #4525]): they collide with everyday configuration talk, and the hard skill gate folds the previous assistant turn into its match text on approval turns — a passing "或配置x: false" in a reply would re-fire the gate every turn. `/init` note: `/init` generates AGENTS.md via LLM; kickoff uses the managed template from `octopus setup` — the Builder distinguishes based on context.
|
||||||
|
|
||||||
Typical user intents include: encountering a readiness prompt at session start, wondering how to initialize a project, or wanting to understand the kickoff workflow steps.
|
Typical user intents include: encountering a readiness prompt at session start, wondering how to initialize a project, or wanting to understand the kickoff workflow steps.
|
||||||
|
|
||||||
@@ -57,43 +57,43 @@ ls .gitea/workflows/ .github/workflows/ 2>/dev/null || echo "[GAP: no CI workflo
|
|||||||
```
|
```
|
||||||
|
|
||||||
If no workflows exist, recommend creating `.gitea/workflows/ci.yml` (this repo
|
If no workflows exist, recommend creating `.gitea/workflows/ci.yml` (this repo
|
||||||
uses Gitea Actions) 鈥?or `.github/workflows/ci.yml` for a GitHub-hosted project
|
uses Gitea Actions) — or `.github/workflows/ci.yml` for a GitHub-hosted project
|
||||||
鈥?with at minimum: typecheck, lint, and test gates. Without CI, every merge is a
|
— with at minimum: typecheck, lint, and test gates. Without CI, every merge is a
|
||||||
manual gate 鈥?type errors, lint regressions, and test failures can land on
|
manual gate — type errors, lint regressions, and test failures can land on
|
||||||
main and go undetected until the next local checkout.
|
main and go undetected until the next local checkout.
|
||||||
|
|
||||||
## 4. Workflow
|
## 4. Workflow
|
||||||
|
|
||||||
The kickoff workflow runs in four automated steps:
|
The kickoff workflow runs in four automated steps:
|
||||||
|
|
||||||
1. **Check readiness** 鈥?Determine if the project needs setup. Run `checkReadinessWithGitea()` or `octopus kickoff --check-only` to inspect the current state.
|
1. **Check readiness** — Determine if the project needs setup. Run `checkReadinessWithGitea()` or `octopus kickoff --check-only` to inspect the current state.
|
||||||
2. **Run kickoff** 鈥?Execute `/kickoff` from the TUI or `octopus kickoff` from the terminal.
|
2. **Run kickoff** — Execute `/kickoff` from the TUI or `octopus kickoff` from the terminal.
|
||||||
3. **What happens** 鈥?A Phase 0 gate, then five sequential phases:
|
3. **What happens** — A Phase 0 gate, then five sequential phases:
|
||||||
- Phase 0 鈥?**Gitea auth check**: derives the `gitea` block from the git remote origin (persisting missing fields), then verifies the token (`GET /user`) and runs the auth wizard on failure; never aborts kickoff
|
- Phase 0 — **Gitea auth check**: derives the `gitea` block from the git remote origin (persisting missing fields), then verifies the token (`GET /user`) and runs the auth wizard on failure; never aborts kickoff
|
||||||
- [1/5] **Setup** 鈥?Creates project directories and writes `AGENTS.md` from the managed template
|
- [1/5] **Setup** — Creates project directories and writes `AGENTS.md` from the managed template
|
||||||
- [2/5] **Scaffold** 鈥?Creates the `<instance-root>/` skeleton (`core/skills/` directory + default `octopus.jsonc` template if absent); domain skills content arrives via the [5/5] overlay pull
|
- [2/5] **Scaffold** — Creates the `<instance-root>/` skeleton (`core/skills/` directory + default `octopus.jsonc` template if absent); domain skills content arrives via the [5/5] overlay pull
|
||||||
- [3/5] **Config stub + MCP wiring** 鈥?Writes the `mcp.codegraph` block into `octopus.jsonc`
|
- [3/5] **Config stub + MCP wiring** — Writes the `mcp.codegraph` block into `octopus.jsonc`
|
||||||
- [4/5] **Codegraph init** 鈥?Initializes and builds the per-project `.codegraph/` index
|
- [4/5] **Codegraph init** — Initializes and builds the per-project `.codegraph/` index
|
||||||
- [5/5] **Skills remote + overlay pull** 鈥?Opt-in semantics ([org-internal #4370]); branches by flags when no `overlay.remotes` entry is configured:
|
- [5/5] **Skills remote + overlay pull** — Opt-in semantics ([org-internal #4370]); branches by flags when no `overlay.remotes` entry is configured:
|
||||||
- no flags (interactive run) 鈥?**safe default**: nothing is written; a remediation hint is printed (pass `--skills-remote`, or edit `overlay.remotes`), and the readiness skills item stays not-ready
|
- no flags (interactive run) — **safe default**: nothing is written; a remediation hint is printed (pass `--skills-remote`, or edit `overlay.remotes`), and the readiness skills item stays not-ready
|
||||||
- `--yes` 鈥?legacy behavior kept: writes the default skills remote, then pulls domain skills content (`octopus skills pull` equivalent)
|
- `--yes` — legacy behavior kept: writes the default skills remote, then pulls domain skills content (`octopus skills pull` equivalent)
|
||||||
- `--skills-remote <owner/repo>` (+ optional `--skills-remote-base-url <url>`) 鈥?writes the specified remote instead (ref `main`; base URL defaults to `<instance-base-url>`), then pulls from it
|
- `--skills-remote <owner/repo>` (+ optional `--skills-remote-base-url <url>`) — writes the specified remote instead (ref `main`; base URL defaults to `<instance-base-url>`), then pulls from it
|
||||||
- `overlay.remotes` already configured 鈥?the write is skipped (idempotent; an explicit `--skills-remote` is ignored with a warning)
|
- `overlay.remotes` already configured — the write is skipped (idempotent; an explicit `--skills-remote` is ignored with a warning)
|
||||||
4. **Post-kickoff verification** 鈥?Run `octopus kickoff --check-only` again. After a `--yes` or `--skills-remote` run it reports all 5 items as ready; after a no-flag interactive run it reports 4 of 5 鈥?the skills item stays not-ready until a remote is configured (`--skills-remote`, `--yes`, or a manual `overlay.remotes` edit + `octopus skills pull`).
|
4. **Post-kickoff verification** — Run `octopus kickoff --check-only` again. After a `--yes` or `--skills-remote` run it reports all 5 items as ready; after a no-flag interactive run it reports 4 of 5 — the skills item stays not-ready until a remote is configured (`--skills-remote`, `--yes`, or a manual `overlay.remotes` edit + `octopus skills pull`).
|
||||||
|
|
||||||
### First-ticket hint ([org-internal #3061])
|
### First-ticket hint ([org-internal #3061])
|
||||||
|
|
||||||
For a brand-new project still validating its concept, open the FIRST ticket as
|
For a brand-new project still validating its concept, open the FIRST ticket as
|
||||||
`Kind/MVP` 鈥?the interactive no-pipeline mode (decision log + debt register in
|
`Kind/MVP` — the interactive no-pipeline mode (decision log + debt register in
|
||||||
the ticket body). It graduates by relabeling to `Kind/Feature` (analyze-dag
|
the ticket body). It graduates by relabeling to `Kind/Feature` (analyze-dag
|
||||||
backfill mode picks up the decision log) when a second session, an external
|
backfill mode picks up the decision log) when a second session, an external
|
||||||
contract, or a second consumer of a shared type appears. Do not route early
|
contract, or a second consumer of a shared type appears. Do not route early
|
||||||
concept work into the full pipeline "to be safe" 鈥?the pipeline's coordination
|
concept work into the full pipeline "to be safe" — the pipeline's coordination
|
||||||
machinery is cost, not quality, at zero coordination.
|
machinery is cost, not quality, at zero coordination.
|
||||||
|
|
||||||
## 5. Re-kickoff / Idempotency
|
## 5. Re-kickoff / Idempotency
|
||||||
|
|
||||||
Re-running kickoff on an already-configured project is safe 鈥?every step is idempotent:
|
Re-running kickoff on an already-configured project is safe — every step is idempotent:
|
||||||
|
|
||||||
- **Setup** skips existing directories
|
- **Setup** skips existing directories
|
||||||
- **Scaffold** skips existing files
|
- **Scaffold** skips existing files
|
||||||
@@ -107,26 +107,26 @@ Re-running kickoff on an already-configured project is safe 鈥?every step is id
|
|||||||
|
|
||||||
Common issues and their resolutions:
|
Common issues and their resolutions:
|
||||||
|
|
||||||
- **`codegraph: command not found`** 鈥?Run the `codegraph-setup` skill, or install via `bun add -g @colbymchenry/codegraph`
|
- **`codegraph: command not found`** — Run the `codegraph-setup` skill, or install via `bun add -g @colbymchenry/codegraph`
|
||||||
- **Permission denied** 鈥?Check project directory ownership and write permissions
|
- **Permission denied** — Check project directory ownership and write permissions
|
||||||
- **Partially complete state** 鈥?Re-run `/kickoff`; idempotent steps will complete the missing parts without affecting already-completed steps
|
- **Partially complete state** — Re-run `/kickoff`; idempotent steps will complete the missing parts without affecting already-completed steps
|
||||||
- **MCP tools not available** 鈥?Verify `mcp.codegraph` exists in `octopus.jsonc`; re-run `octopus kickoff` to write it
|
- **MCP tools not available** — Verify `mcp.codegraph` exists in `octopus.jsonc`; re-run `octopus kickoff` to write it
|
||||||
|
|
||||||
## 7. Related Commands
|
## 7. Related Commands
|
||||||
|
|
||||||
- `/init` 鈥?Generates `AGENTS.md` using an LLM (custom, context-aware); kickoff uses the managed template from `octopus setup`
|
- `/init` — Generates `AGENTS.md` using an LLM (custom, context-aware); kickoff uses the managed template from `octopus setup`
|
||||||
- `octopus setup` 鈥?Creates directories + `AGENTS.md` from the managed template; kickoff calls this internally
|
- `octopus setup` — Creates directories + `AGENTS.md` from the managed template; kickoff calls this internally
|
||||||
- `octopus scaffold` 鈥?Creates the `<instance-root>/` skeleton (`core/skills/` directory + default `octopus.jsonc` template if absent); run `octopus skills pull` to fetch domain skills content; kickoff calls this internally
|
- `octopus scaffold` — Creates the `<instance-root>/` skeleton (`core/skills/` directory + default `octopus.jsonc` template if absent); run `octopus skills pull` to fetch domain skills content; kickoff calls this internally
|
||||||
- `octopus kickoff --check-only` 鈥?Readiness check only, no pipeline execution
|
- `octopus kickoff --check-only` — Readiness check only, no pipeline execution
|
||||||
- `octopus kickoff --skills-remote <owner/repo>` 鈥?Explicit opt-in: writes the given skills remote to `overlay.remotes` (ref `main`) and pulls it instead of the default
|
- `octopus kickoff --skills-remote <owner/repo>` — Explicit opt-in: writes the given skills remote to `overlay.remotes` (ref `main`) and pulls it instead of the default
|
||||||
- `octopus kickoff --skills-remote-base-url <url>` 鈥?Base URL for `--skills-remote` (requires it); defaults to `<instance-base-url>` when omitted
|
- `octopus kickoff --skills-remote-base-url <url>` — Base URL for `--skills-remote` (requires it); defaults to `<instance-base-url>` when omitted
|
||||||
- `octopus kickoff --yes` 鈥?Skips the opt-in confirmation and keeps the legacy behavior of writing the default skills remote (`Octopus/octopus-skills`)
|
- `octopus kickoff --yes` — Skips the opt-in confirmation and keeps the legacy behavior of writing the default skills remote (`Octopus/octopus-skills`)
|
||||||
- `codegraph-setup` skill 鈥?For installing the codegraph CLI and initializing the per-project index
|
- `codegraph-setup` skill — For installing the codegraph CLI and initializing the per-project index
|
||||||
|
|
||||||
## 8. Reference
|
## 8. Reference
|
||||||
|
|
||||||
- `<harness-package>/src/project-kickoff/readiness.ts` 鈥?`checkReadiness()` implementation (sync 4-item base)
|
- `<harness-package>/src/project-kickoff/readiness.ts` — `checkReadiness()` implementation (sync 4-item base)
|
||||||
- `<harness-package>/src/project-kickoff/readiness-gitea.ts` 鈥?`checkReadinessWithGitea()` composed 5-item check (adds gitea auth reachable)
|
- `<harness-package>/src/project-kickoff/readiness-gitea.ts` — `checkReadinessWithGitea()` composed 5-item check (adds gitea auth reachable)
|
||||||
- `<harness-package>/src/cli/cmd/kickoff.ts` 鈥?`/kickoff` command handler
|
- `<harness-package>/src/cli/cmd/kickoff.ts` — `/kickoff` command handler
|
||||||
- `core/skills/codegraph-setup/SKILL.md` 鈥?CodeGraph setup skill
|
- `core/skills/codegraph-setup/SKILL.md` — CodeGraph setup skill
|
||||||
- Project roadmap at `.artifacts/project-kickoff/` (deprecated 鈥?now stored as wiki page `project-kickoff/roadmap/` via `wiki 璇诲啓 API锛堣 TERMINOLOGY锛塦)
|
- Project roadmap at `.artifacts/project-kickoff/` (deprecated — now stored as wiki page `project-kickoff/roadmap/` via `wiki 读写 API(见 TERMINOLOGY)`)
|
||||||
|
|||||||
@@ -4,22 +4,22 @@ description: >
|
|||||||
Use ONLY when ready to cut a release. The Release Manager (Worker) inspects
|
Use ONLY when ready to cut a release. The Release Manager (Worker) inspects
|
||||||
project state (git log, version files, build) to determine the semver bump,
|
project state (git log, version files, build) to determine the semver bump,
|
||||||
generate the changelog, tag, and produce a release artifact. Works on any
|
generate the changelog, tag, and produce a release artifact. Works on any
|
||||||
project state 鈥?no pipeline artifacts required.
|
project state — no pipeline artifacts required.
|
||||||
triggers:
|
triggers:
|
||||||
- release
|
- release
|
||||||
- cut release
|
- cut release
|
||||||
- 鍙戠増
|
- 发版
|
||||||
- publish release
|
- publish release
|
||||||
- ship release
|
- ship release
|
||||||
role: Producer
|
role: Producer
|
||||||
---
|
---
|
||||||
|
|
||||||
> Core 涓珛鐗堬紙Increment 6a 鏀瑰啓锛屽師 deferHard verbatimDir锛夈€傛満鍒躲€佺粨鏋勪笌 frontmatter 淇濇寔锛涘疄渚嬫湳璇紙宸ュ叿鍚嶃€佽矾寰勩€佸伐鍗曞彿锛夋寜 `core/adapters/TERMINOLOGY.md` 缁戝畾鍒板叿浣撳疄渚嬨€?
|
> Core 中立版(Increment 6a 改写,原 deferHard verbatimDir)。机制、结构与 frontmatter 保持;实例术语(工具名、路径、工单号)按 `core/adapters/TERMINOLOGY.md` 绑定到具体实例。
|
||||||
# Release
|
# Release
|
||||||
|
|
||||||
Prepare and tag a release based on project facts 鈥?git history since the last
|
Prepare and tag a release based on project facts — git history since the last
|
||||||
tag, current version, build status, and dependency health. No dependency on `.artifacts/{slug}/`
|
tag, current version, build status, and dependency health. No dependency on `.artifacts/{slug}/`
|
||||||
(**deprecated** 鈥?SDLC artifacts now live in the Gitea wiki at `{slug}/...`;
|
(**deprecated** — SDLC artifacts now live in the Gitea wiki at `{slug}/...`;
|
||||||
see `_shared/gitea-read-patterns.md`) or any pipeline skill outputs.
|
see `_shared/gitea-read-patterns.md`) or any pipeline skill outputs.
|
||||||
|
|
||||||
**This SKILL does not deploy.** Deployment is project-specific (K8s, Docker,
|
**This SKILL does not deploy.** Deployment is project-specific (K8s, Docker,
|
||||||
@@ -32,12 +32,12 @@ The release is owned and executed by the **Release Manager** (Worker).
|
|||||||
|
|
||||||
**Context compaction**: release is a pipeline stage boundary. The main session
|
**Context compaction**: release is a pipeline stage boundary. The main session
|
||||||
compacts at this clean boundary ONLY when a capacity/projection trigger holds,
|
compacts at this clean boundary ONLY when a capacity/projection trigger holds,
|
||||||
per `core/rules/compact.md` 搂"Stage-boundary compaction" (long multi-stage
|
per `core/rules/compact.md` §"Stage-boundary compaction" (long multi-stage
|
||||||
runs 鈥?DAG Epic orchestration 鈥?keep the legacy every-boundary compaction;
|
runs — DAG Epic orchestration — keep the legacy every-boundary compaction;
|
||||||
short runs 鈥?bugfix / DAG task 鈥?and standalone runs default to NOT
|
short runs — bugfix / DAG task — and standalone runs default to NOT
|
||||||
compacting). The Release Manager itself is single-phase and
|
compacting). The Release Manager itself is single-phase and
|
||||||
artifact-driven (version files, changelog, tag); a mid-run compaction loses
|
artifact-driven (version files, changelog, tag); a mid-run compaction loses
|
||||||
nothing 鈥?re-read the release checklist state and git log to resume.
|
nothing — re-read the release checklist state and git log to resume.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ Before starting the release:
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Phase 1 鈥?Pre-release Gate
|
## Phase 1 — Pre-release Gate
|
||||||
|
|
||||||
Run each check against the live project. Stop and report failures.
|
Run each check against the live project. Stop and report failures.
|
||||||
|
|
||||||
@@ -65,82 +65,82 @@ Run each check against the live project. Stop and report failures.
|
|||||||
- No new HIGH or CRITICAL CVEs block the release.
|
- No new HIGH or CRITICAL CVEs block the release.
|
||||||
- Pre-existing HIGH/CRITICAL CVEs do NOT block but **MUST be filed as
|
- Pre-existing HIGH/CRITICAL CVEs do NOT block but **MUST be filed as
|
||||||
tech-debt** before the release lands. File them in TWO TIERS (batching
|
tech-debt** before the release lands. File them in TWO TIERS (batching
|
||||||
rule, [org-internal #3846] 鈥?a single release audit once fanned out to 14 TDs 鈫?14 PRs 鈫?
|
rule, [org-internal #3846] — a single release audit once fanned out to 14 TDs → 14 PRs →
|
||||||
56-84 CI runs on a saturated runner pool): Tier A below is the general
|
56-84 CI runs on a saturated runner pool): Tier A below is the general
|
||||||
mechanical-change batching rule; Tier B and the escape hatch follow it.
|
mechanical-change batching rule; Tier B and the escape hatch follow it.
|
||||||
|
|
||||||
<a id="phase-1-tier-a"></a>
|
<a id="phase-1-tier-a"></a>
|
||||||
### Tier A 鈥?閫氱敤鏈烘鍙樻洿骞跺崟瑙勫垯 / Universal mechanical-change batching rule
|
### Tier A — 通用机械变更并单规则 / Universal mechanical-change batching rule
|
||||||
|
|
||||||
Origin [org-internal #3846] (audit-batch pilot, generalized in place): the rule covers ANY
|
Origin [org-internal #3846] (audit-batch pilot, generalized in place): the rule covers ANY
|
||||||
single-source fan-out of mechanical changes, not just dependency audits.
|
single-source fan-out of mechanical changes, not just dependency audits.
|
||||||
Two source types today: (a) dependency bump audits; (b) docs 鎵归噺淇
|
Two source types today: (a) dependency bump audits; (b) docs 批量修订
|
||||||
(batch docs revisions).
|
(batch docs revisions).
|
||||||
|
|
||||||
#### 瑙勫垯姝f枃 / Rule text
|
#### 规则正文 / Rule text
|
||||||
|
|
||||||
- **Core**: mechanical changes of ONE class fanning out from a single
|
- **Core**: mechanical changes of ONE class fanning out from a single
|
||||||
source are filed as ONE batch issue + ONE batch PR 鈥?never N脳issue +
|
source are filed as ONE batch issue + ONE batch PR — never N×issue +
|
||||||
N脳PR. The batch body keeps one row per item (per-CVE / per-doc mapping),
|
N×PR. The batch body keeps one row per item (per-CVE / per-doc mapping),
|
||||||
so per-item traceability is unchanged; work the batch as a single PR.
|
so per-item traceability is unchanged; work the batch as a single PR.
|
||||||
- **(a) Dependency bump audits** (the [org-internal #3846] origin case): advisories whose
|
- **(a) Dependency bump audits** (the [org-internal #3846] origin case): advisories whose
|
||||||
remediation is a plain version bump (lockfile-only diff, no semver-major
|
remediation is a plain version bump (lockfile-only diff, no semver-major
|
||||||
jump, no overrides/catalog surgery, no API or adapter fallout) are filed
|
jump, no overrides/catalog surgery, no API or adapter fallout) are filed
|
||||||
together via `宸ュ崟 API锛堣 TERMINOLOGY锛塩reate` with
|
together via `工单 API(见 TERMINOLOGY)create` with
|
||||||
`title="[{origin}] audit-batch@{version}: mechanical bumps for {N} advisories (TD-{NNN})"`
|
`title="[{origin}] audit-batch@{version}: mechanical bumps for {N} advisories (TD-{NNN})"`
|
||||||
and labels `[tech_debt_label_id, severity_label_id, kind_bug_label_id]`
|
and labels `[tech_debt_label_id, severity_label_id, kind_bug_label_id]`
|
||||||
(severity = highest in the batch). One row per advisory 鈥?CVE/GHSA ID,
|
(severity = highest in the batch). One row per advisory — CVE/GHSA ID,
|
||||||
affected package + from鈫抰o version, severity (CVSS), advisory URL,
|
affected package + from→to version, severity (CVSS), advisory URL,
|
||||||
recommended remediation, and a per-advisory Reactivation Trigger
|
recommended remediation, and a per-advisory Reactivation Trigger
|
||||||
("resolved when `npm audit` / `bun audit` reports no HIGH/CRITICAL for
|
("resolved when `npm audit` / `bun audit` reports no HIGH/CRITICAL for
|
||||||
this advisory") 鈥?so the release-notes `TD-NNN 鈫?#NNNN` mapping stays
|
this advisory") — so the release-notes `TD-NNN → #NNNN` mapping stays
|
||||||
per-advisory.
|
per-advisory.
|
||||||
- **(b) Docs 鎵归噺淇 (batch docs revisions)**: many small same-class docs
|
- **(b) Docs 批量修订 (batch docs revisions)**: many small same-class docs
|
||||||
corrections discovered in one pass (e.g. a terminology sweep) file as ONE
|
corrections discovered in one pass (e.g. a terminology sweep) file as ONE
|
||||||
issue via the same flow,
|
issue via the same flow,
|
||||||
`title="[{origin}] docs-batch: {class} revisions for {N} files"`, with one
|
`title="[{origin}] docs-batch: {class} revisions for {N} files"`, with one
|
||||||
row per file (path, correction, reason); same ONE-batch-PR landing.
|
row per file (path, correction, reason); same ONE-batch-PR landing.
|
||||||
- **Landing**: when members live on separate branches, compose the single
|
- **Landing**: when members live on separate branches, compose the single
|
||||||
batch PR via the `land-batch` skill (`core/skills/land-batch/SKILL.md`
|
batch PR via the `land-batch` skill (`core/skills/land-batch/SKILL.md`
|
||||||
鈥?cross-branch batch composition, topology B). For dependency-bump
|
— cross-branch batch composition, topology B). For dependency-bump
|
||||||
batches pass `batch-compose --convergent bun.lock` (script
|
batches pass `batch-compose --convergent bun.lock` (script
|
||||||
`.gitea/scripts/batch-compose.ts`): the regenerable lockfile is exempt
|
`.gitea/scripts/batch-compose.ts`): the regenerable lockfile is exempt
|
||||||
from path-overlap admission and lockfile-only conflicts are surgically
|
from path-overlap admission and lockfile-only conflicts are surgically
|
||||||
resolved to the running head's version.
|
resolved to the running head's version.
|
||||||
|
|
||||||
#### 閫傜敤杈圭晫 / Applicability boundary
|
#### 适用边界 / Applicability boundary
|
||||||
|
|
||||||
- **闈?mechanical锛堝惈鍒ゆ柇鎴愬垎鐨勫彉鏇达紝涓嶅苟鍗曪級** 鈥?items requiring judgment
|
- **非mechanical(含判断成分的变更,不并单)** — items requiring judgment
|
||||||
(wording decisions, behavior/API changes, review-dependent edits) are
|
(wording decisions, behavior/API changes, review-dependent edits) are
|
||||||
never batched; file each as its own issue.
|
never batched; file each as its own issue.
|
||||||
- **璺緞閲嶅彔锛坆atch 鎴愬憳瑙︾鐩稿悓鏂囦欢 鈫?涓嶅苟鍗曪紱鍞竴璞佸厤 =
|
- **路径重叠(batch 成员触碰相同文件 → 不并单;唯一豁免 =
|
||||||
鍙啀鐢熷叡浜枃浠?`bun.lock` 缁?batch-compose `--convergent` 鏀舵暃锛?* 鈥?the sole
|
可再生共享文件`bun.lock` 经batch-compose `--convergent` 收敛)** — the sole
|
||||||
path-overlap exemption is the regenerable shared lockfile under
|
path-overlap exemption is the regenerable shared lockfile under
|
||||||
`--convergent`; every other same-file collision stays un-batched.
|
`--convergent`; every other same-file collision stays un-batched.
|
||||||
- **semver-major 璺冲彉 / overrides路catalog 鎵嬫湳 / adapter路peer 鑱斿姩锛堚啋
|
- **semver-major 跳变 / overrides·catalog 手术 / adapter·peer 联动(→
|
||||||
Tier B 鐙珛鍗曪紝缁存寔 per-package锛?* 鈥?these stay per-package Tier B
|
Tier B 独立单,维持 per-package)** — these stay per-package Tier B
|
||||||
issues. Tier B and the escape hatch (below) survive this generalization
|
issues. Tier B and the escape hatch (below) survive this generalization
|
||||||
unchanged.
|
unchanged.
|
||||||
|
|
||||||
#### 闄嶇骇璺緞 / Degradation path
|
#### 降级路径 / Degradation path
|
||||||
|
|
||||||
- **骞跺崟 PR CI 澶辫触 鈫?鎸夐攣鏂囦欢 hunk 鎷嗗寘鍥為€€锛坆isect锛?*: for dependency
|
- **并单 PR CI 失败 → 按锁文件 hunk 拆包回退(bisect)**: for dependency
|
||||||
batches, bisect by splitting lockfile hunks per package back into
|
batches, bisect by splitting lockfile hunks per package back into
|
||||||
per-package PRs 鈥?the original Tier A bisect semantics carried over
|
per-package PRs — the original Tier A bisect semantics carried over
|
||||||
verbatim (鍚岃涔夛紝娉涘寲鎺緸: every mechanical batch degrades the same way).
|
verbatim (同语义,泛化措辞: every mechanical batch degrades the same way).
|
||||||
- **Docs batches**: split per file back into per-file PRs.
|
- **Docs batches**: split per file back into per-file PRs.
|
||||||
- **land-batch composition fallback** (exit codes per
|
- **land-batch composition fallback** (exit codes per
|
||||||
`.gitea/scripts/batch-compose.ts`): `3` path-overlap 鈫?split the batch
|
`.gitea/scripts/batch-compose.ts`): `3` path-overlap → split the batch
|
||||||
along the reported pairs (or land the overlapping member 1:1) and re-run
|
along the reported pairs (or land the overlapping member 1:1) and re-run
|
||||||
per group; `4` merge conflict / `5` transport/git error 鈫?fall back to
|
per group; `4` merge conflict / `5` transport/git error → fall back to
|
||||||
1:1 PRs for the whole batch.
|
1:1 PRs for the whole batch.
|
||||||
- **Preflight hedge ([org-internal #3846])**: run `bun install --dry-run` before opening
|
- **Preflight hedge ([org-internal #3846])**: run `bun install --dry-run` before opening
|
||||||
a dependency-batch PR 鈥?the resolver accepting the composed version set
|
a dependency-batch PR — the resolver accepting the composed version set
|
||||||
is a cheap pre-CI rejection of impossible bump combinations.
|
is a cheap pre-CI rejection of impossible bump combinations.
|
||||||
|
|
||||||
Tier B and the escape hatch (unchanged by the generalization):
|
Tier B and the escape hatch (unchanged by the generalization):
|
||||||
|
|
||||||
- **Tier B 鈥?surgery, one issue per package**: semver-major jumps,
|
- **Tier B — surgery, one issue per package**: semver-major jumps,
|
||||||
adapter/peer fallout, or overrides/catalog surgery keep the
|
adapter/peer fallout, or overrides/catalog surgery keep the
|
||||||
per-package issue: `title="[{origin}] {CVE-ID/GHSA-ID} in {package} (TD-{NNN})"`
|
per-package issue: `title="[{origin}] {CVE-ID/GHSA-ID} in {package} (TD-{NNN})"`
|
||||||
with the same labels; body requirements match the Tier A rows.
|
with the same labels; body requirements match the Tier A rows.
|
||||||
@@ -148,7 +148,7 @@ Tier B and the escape hatch (unchanged by the generalization):
|
|||||||
issue when same-day remediation is required (urgent HIGH/CRITICAL).
|
issue when same-day remediation is required (urgent HIGH/CRITICAL).
|
||||||
|
|
||||||
De-duplicate against open `tech-debt` issues (match by
|
De-duplicate against open `tech-debt` issues (match by
|
||||||
CVE/GHSA ID) before creating. Record the `TD-NNN 鈫?#NNNN` mapping in the
|
CVE/GHSA ID) before creating. Record the `TD-NNN → #NNNN` mapping in the
|
||||||
release notes. See `verify` Phase 5.5 for the tech-debt promotion body
|
release notes. See `verify` Phase 5.5 for the tech-debt promotion body
|
||||||
template and `_shared/gitea-write-patterns.md` Pattern 3.
|
template and `_shared/gitea-write-patterns.md` Pattern 3.
|
||||||
|
|
||||||
@@ -157,19 +157,19 @@ template and `_shared/gitea-write-patterns.md` Pattern 3.
|
|||||||
|
|
||||||
| Check | Status |
|
| Check | Status |
|
||||||
| ----------------- | ----------------------------------------------------- |
|
| ----------------- | ----------------------------------------------------- |
|
||||||
| Clean workspace | 鉁?/ 鉂? |
|
| Clean workspace | ✅/ ❌ |
|
||||||
| Branch | {branch name} |
|
| Branch | {branch name} |
|
||||||
| Build | 鉁?/ 鉂? |
|
| Build | ✅/ ❌ |
|
||||||
| Typecheck + Lint | 鉁?/ 鉂? |
|
| Typecheck + Lint | ✅/ ❌ |
|
||||||
| Tests | 鉁?/ 鉂?(N passed, M failed) |
|
| Tests | ✅/ ❌(N passed, M failed) |
|
||||||
| Dependency audit | 鉁?/ 鈿狅笍 N known CVEs (pre-existing) / 鉂?N new CVEs |
|
| Dependency audit | ✅/ ⚠️ N known CVEs (pre-existing) / ❌N new CVEs |
|
||||||
```
|
```
|
||||||
|
|
||||||
If any gate fails except known CVEs, stop and report what failed.
|
If any gate fails except known CVEs, stop and report what failed.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Phase 2 鈥?Version Bump
|
## Phase 2 — Version Bump
|
||||||
|
|
||||||
Determine the new version by inspecting git history since the last tag.
|
Determine the new version by inspecting git history since the last tag.
|
||||||
|
|
||||||
@@ -193,14 +193,14 @@ Determine the new version by inspecting git history since the last tag.
|
|||||||
| `docs:` / `chore:` | (skip) | Not user-visible |
|
| `docs:` / `chore:` | (skip) | Not user-visible |
|
||||||
|
|
||||||
If no conventional prefix found, infer from the subject line:
|
If no conventional prefix found, infer from the subject line:
|
||||||
- "add", "implement", "introduce" 鈫?MINOR
|
- "add", "implement", "introduce" → MINOR
|
||||||
- "fix", "resolve", "correct" 鈫?PATCH
|
- "fix", "resolve", "correct" → PATCH
|
||||||
- "remove", "drop", "rename" (public API) 鈫?MAJOR
|
- "remove", "drop", "rename" (public API) → MAJOR
|
||||||
|
|
||||||
5. **Compute bump**:
|
5. **Compute bump**:
|
||||||
- If any MAJOR commit 鈫?bump MAJOR.
|
- If any MAJOR commit → bump MAJOR.
|
||||||
- Else if any MINOR commit 鈫?bump MINOR.
|
- Else if any MINOR commit → bump MINOR.
|
||||||
- Else 鈫?bump PATCH.
|
- Else → bump PATCH.
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
## Version Bump
|
## Version Bump
|
||||||
@@ -210,12 +210,12 @@ Determine the new version by inspecting git history since the last tag.
|
|||||||
**New version**: {new version}
|
**New version**: {new version}
|
||||||
**Type**: MAJOR / MINOR / PATCH
|
**Type**: MAJOR / MINOR / PATCH
|
||||||
**Commits since last tag**: {N}
|
**Commits since last tag**: {N}
|
||||||
**Reason**: {justification 鈥?e.g. "2 feat + 3 fix 鈫?MINOR"}
|
**Reason**: {justification — e.g. "2 feat + 3 fix → MINOR"}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Phase 3 鈥?Changelog
|
## Phase 3 — Changelog
|
||||||
|
|
||||||
Generate the changelog from `git log <last-tag>..HEAD`:
|
Generate the changelog from `git log <last-tag>..HEAD`:
|
||||||
|
|
||||||
@@ -224,10 +224,10 @@ Generate the changelog from `git log <last-tag>..HEAD`:
|
|||||||
- Scope (if present, e.g. `feat(auth):`).
|
- Scope (if present, e.g. `feat(auth):`).
|
||||||
- Description (the subject line, past tense, human-readable).
|
- Description (the subject line, past tense, human-readable).
|
||||||
2. **Group by type**:
|
2. **Group by type**:
|
||||||
- **Added** 鈥?`feat:` commits.
|
- **Added** — `feat:` commits.
|
||||||
- **Changed** 鈥?modifications to existing behavior (non-breaking).
|
- **Changed** — modifications to existing behavior (non-breaking).
|
||||||
- **Fixed** 鈥?`fix:` commits.
|
- **Fixed** — `fix:` commits.
|
||||||
- **Breaking** 鈥?`BREAKING CHANGE:` or `!:` commits.
|
- **Breaking** — `BREAKING CHANGE:` or `!:` commits.
|
||||||
3. **Deduplicate**: squash multiple commits for the same change into one entry
|
3. **Deduplicate**: squash multiple commits for the same change into one entry
|
||||||
where it makes narrative sense.
|
where it makes narrative sense.
|
||||||
4. Read the existing root `CHANGELOG.md` (if any) and prepend this release.
|
4. Read the existing root `CHANGELOG.md` (if any) and prepend this release.
|
||||||
@@ -235,7 +235,7 @@ Generate the changelog from `git log <last-tag>..HEAD`:
|
|||||||
Write to root `CHANGELOG.md` (prepend section).
|
Write to root `CHANGELOG.md` (prepend section).
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
## [{version}] 鈥?{YYYY-MM-DD}
|
## [{version}] — {YYYY-MM-DD}
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
@@ -251,24 +251,24 @@ Write to root `CHANGELOG.md` (prepend section).
|
|||||||
|
|
||||||
### Breaking
|
### Breaking
|
||||||
|
|
||||||
- {breaking change} ({hash}) 鈥?see migration notes above
|
- {breaking change} ({hash}) — see migration notes above
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Phase 4 鈥?Tag & Finalize
|
## Phase 4 — Tag & Finalize
|
||||||
|
|
||||||
1. **Update version file** 鈥?write the new version to the project's version
|
1. **Update version file** — write the new version to the project's version
|
||||||
manifest.
|
manifest.
|
||||||
2. **Commit**: `git add` version file + changelog file, commit with message:
|
2. **Commit**: `git add` version file + changelog file, commit with message:
|
||||||
```
|
```
|
||||||
release: {version}
|
release: {version}
|
||||||
```
|
```
|
||||||
3. **Tag**: `git tag v{version}` (adjust prefix per project convention 鈥?
|
3. **Tag**: `git tag v{version}` (adjust prefix per project convention —
|
||||||
check existing tags with `git tag -l`).
|
check existing tags with `git tag -l`).
|
||||||
4. **Verify tag**: `git tag -l v{version}` confirms the tag exists.
|
4. **Verify tag**: `git tag -l v{version}` confirms the tag exists.
|
||||||
|
|
||||||
**Do not push** 鈥?the user must explicitly request pushing to remote.
|
**Do not push** — the user must explicitly request pushing to remote.
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
## Release Artifact
|
## Release Artifact
|
||||||
@@ -282,15 +282,15 @@ Write to root `CHANGELOG.md` (prepend section).
|
|||||||
|
|
||||||
| File | Change |
|
| File | Change |
|
||||||
| -------------- | ------------------------- |
|
| -------------- | ------------------------- |
|
||||||
| {version file} | {old} 鈫?{new} |
|
| {version file} | {old} → {new} |
|
||||||
| CHANGELOG.md | Prepended {version} |
|
| CHANGELOG.md | Prepended {version} |
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Phase 4b 鈥?Publish Release Artifacts (octopus project, manual)
|
## Phase 4b — Publish Release Artifacts (octopus project, manual)
|
||||||
|
|
||||||
> Octopus-specific. Other projects: substitute your own artifact pipeline 鈥?
|
> Octopus-specific. Other projects: substitute your own artifact pipeline —
|
||||||
> the goal is identical (turn the tag into downloadable assets).
|
> the goal is identical (turn the tag into downloadable assets).
|
||||||
|
|
||||||
The CI publish pipeline (`.gitea/workflows/publish.yml`) was **retired by
|
The CI publish pipeline (`.gitea/workflows/publish.yml`) was **retired by
|
||||||
@@ -303,17 +303,17 @@ credentials in the environment.
|
|||||||
|
|
||||||
- Clean checkout of the release commit (tag pushed or about to be pushed).
|
- Clean checkout of the release commit (tag pushed or about to be pushed).
|
||||||
- Credentials in env:
|
- Credentials in env:
|
||||||
- `GITEA_TOKEN` 鈥?PAT with `write:repository` (release create/undraft,
|
- `GITEA_TOKEN` — PAT with `write:repository` (release create/undraft,
|
||||||
tag push) and `write:package` (container registry). The `CI_PAT` secret
|
tag push) and `write:package` (container registry). The `CI_PAT` secret
|
||||||
value is the canonical token.
|
value is the canonical token.
|
||||||
- `NODE_AUTH_TOKEN` 鈥?only if publishing to npm.
|
- `NODE_AUTH_TOKEN` — only if publishing to npm.
|
||||||
- `AUR_KEY` 鈥?only if pushing the AUR package.
|
- `AUR_KEY` — only if pushing the AUR package.
|
||||||
- Docker logged in to the Gitea container registry:
|
- Docker logged in to the Gitea container registry:
|
||||||
`echo "$GITEA_TOKEN" | docker login <instance-registry-host> -u <user> --password-stdin`.
|
`echo "$GITEA_TOKEN" | docker login <instance-registry-host> -u <user> --password-stdin`.
|
||||||
|
|
||||||
### Steps
|
### Steps
|
||||||
|
|
||||||
1. **Version + draft release** (idempotent 鈥?skips if already published):
|
1. **Version + draft release** (idempotent — skips if already published):
|
||||||
```bash
|
```bash
|
||||||
GITEA_TOKEN=<pat> GH_REPO=Octopus/octopus bun script/version.ts
|
GITEA_TOKEN=<pat> GH_REPO=Octopus/octopus bun script/version.ts
|
||||||
```
|
```
|
||||||
@@ -344,7 +344,7 @@ credentials in the environment.
|
|||||||
-H "Content-Type: application/json" -d '{"draft": false}'
|
-H "Content-Type: application/json" -d '{"draft": false}'
|
||||||
```
|
```
|
||||||
|
|
||||||
5. **Verify assets** 鈥?the release must carry 12 assets:
|
5. **Verify assets** — the release must carry 12 assets:
|
||||||
```bash
|
```bash
|
||||||
curl -sS "<instance-base-url>/api/v1/repos/Octopus/octopus/releases/tags/v{version}" \
|
curl -sS "<instance-base-url>/api/v1/repos/Octopus/octopus/releases/tags/v{version}" \
|
||||||
-H "Authorization: token $GITEA_TOKEN" | jq '.assets | length'
|
-H "Authorization: token $GITEA_TOKEN" | jq '.assets | length'
|
||||||
@@ -368,7 +368,7 @@ credentials in the environment.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Phase 5 鈥?Rollback Plan
|
## Phase 5 — Rollback Plan
|
||||||
|
|
||||||
Document how to undo this release:
|
Document how to undo this release:
|
||||||
|
|
||||||
@@ -379,7 +379,7 @@ Document how to undo this release:
|
|||||||
2. **Data rollback** (if applicable):
|
2. **Data rollback** (if applicable):
|
||||||
- For each migration, confirm the `down` migration exists and has been tested.
|
- For each migration, confirm the `down` migration exists and has been tested.
|
||||||
- If the release changes data format without a reversible migration, mark
|
- If the release changes data format without a reversible migration, mark
|
||||||
`鈿狅笍 IRREVERSIBLE DATA CHANGE`.
|
`⚠️ IRREVERSIBLE DATA CHANGE`.
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
## Rollback Plan
|
## Rollback Plan
|
||||||
@@ -403,7 +403,7 @@ git tag -d v{version}
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Phase 6 鈥?Post-release Smoke Test
|
## Phase 6 — Post-release Smoke Test
|
||||||
|
|
||||||
1. Run the project's build command on the tagged commit. Must pass.
|
1. Run the project's build command on the tagged commit. Must pass.
|
||||||
2. Run the test suite. Must pass.
|
2. Run the test suite. Must pass.
|
||||||
@@ -411,15 +411,15 @@ git tag -d v{version}
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Phase 7 鈥?Report
|
## Phase 7 — Report
|
||||||
|
|
||||||
```markdown
|
```markdown
|
||||||
## Release Report
|
## Release Report
|
||||||
|
|
||||||
**Version**: {old} 鈫?{new} (MAJOR / MINOR / PATCH)
|
**Version**: {old} → {new} (MAJOR / MINOR / PATCH)
|
||||||
**Tag**: v{version}
|
**Tag**: v{version}
|
||||||
**Commits**: {N} since last tag ({feat} features, {fix} fixes)
|
**Commits**: {N} since last tag ({feat} features, {fix} fixes)
|
||||||
**Typecheck**: 鉁?| Lint: 鉁?| Tests: 鉁?
|
**Typecheck**: ✅| Lint: ✅| Tests: ✅
|
||||||
|
|
||||||
### Changelog
|
### Changelog
|
||||||
|
|
||||||
@@ -427,21 +427,21 @@ git tag -d v{version}
|
|||||||
|
|
||||||
### Release Artifacts
|
### Release Artifacts
|
||||||
|
|
||||||
- Commit: {hash} 鈥?`release: {version}`
|
- Commit: {hash} — `release: {version}`
|
||||||
- Tag: v{version}
|
- Tag: v{version}
|
||||||
- Changelog: CHANGELOG.md updated
|
- Changelog: CHANGELOG.md updated
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Deploy**: {manual step 鈥?"merge to main triggers CI", etc.}
|
**Deploy**: {manual step — "merge to main triggers CI", etc.}
|
||||||
**Rollback**: see Phase 5 above
|
**Rollback**: see Phase 5 above
|
||||||
**Next**: `core/skills/retrospective/SKILL.md` (optional 鈥?run on any project state)
|
**Next**: `core/skills/retrospective/SKILL.md` (optional — run on any project state)
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
- `core/checklists/release.md` 鈥?Release checklist
|
- `core/checklists/release.md` — Release checklist
|
||||||
- Semver spec: https://semver.org
|
- Semver spec: https://semver.org
|
||||||
- Conventional Commits: https://www.conventionalcommits.org
|
- Conventional Commits: https://www.conventionalcommits.org
|
||||||
|
|||||||
@@ -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` (D1–D4) 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` (D1–D4) 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.
|
||||||
|
|||||||
@@ -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 comments(only when step 2.56 ran;omit for no-PR flows): `inline: X posted / Y file-level / Z fallback / W failed`
|
Inline comments(only when step 2.56 ran;omit for no-PR flows): `inline: X posted / Y file-level / Z fallback / W failed`
|
||||||
|
|
||||||
|
|||||||
+14
-16
@@ -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
|
||||||
|
|||||||
@@ -0,0 +1,94 @@
|
|||||||
|
#!/usr/bin/env bun
|
||||||
|
|
||||||
|
import fs from "node:fs"
|
||||||
|
import path from "node:path"
|
||||||
|
|
||||||
|
const ROOT = path.resolve(import.meta.dir, "..")
|
||||||
|
const CORE_DIR = path.join(ROOT, "core")
|
||||||
|
|
||||||
|
const SCAN_EXTENSIONS = new Set([".md", ".json", ".ts", ".tsx", ".js", ".mjs", ".cjs", ".yaml", ".yml", ".txt"])
|
||||||
|
|
||||||
|
export const MOJIBAKE_SIGNATURES: { name: string; pattern: RegExp }[] = [
|
||||||
|
{ name: "em-dash-mojibake", pattern: /\u9225/ },
|
||||||
|
{ name: "right-arrow-mojibake", pattern: /\u922B/ },
|
||||||
|
{ name: "check-mark-mojibake", pattern: /\u9257|\u9253/ },
|
||||||
|
{ name: "cross-mark-mojibake", pattern: /\u9242/ },
|
||||||
|
{ name: "geq-mojibake", pattern: /\u922E/ },
|
||||||
|
{ name: "multiplication-mojibake", pattern: /\u8137/ },
|
||||||
|
{ name: "replacement-char", pattern: /\uFFFD/ },
|
||||||
|
{ name: "truncated-mojibake", pattern: /[\u9000-\u9FFF]\?/ },
|
||||||
|
{ name: "gbk-cjk-bigram", pattern: /椤圭|鍒濆|閰嶇|鐨勶|鎷夎|浼氳瘽|鏃犲|淇濇|缁戝|瀹屾|涓珛|鏀瑰啓|瀹炰緥|鎵归|寤惰|璋冨|绀轰|鍘熸|閰嶉|鎹薄|閫昏|椤甸|绔犵|宸插|鍒嗛|鍏抽|鍚岀|鍚庣|鍓嶇|杩斿|杩涘|閫変|鎸夌|鏉冮|鏂囦|鏂囨/ },
|
||||||
|
]
|
||||||
|
|
||||||
|
export interface EncodingViolation {
|
||||||
|
file: string
|
||||||
|
line: number
|
||||||
|
signature: string
|
||||||
|
match: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export function scanFile(filePath: string): EncodingViolation[] {
|
||||||
|
const text = fs.readFileSync(filePath, "utf8")
|
||||||
|
const violations: EncodingViolation[] = []
|
||||||
|
const lines = text.split(/\r?\n/)
|
||||||
|
for (let i = 0; i < lines.length; i++) {
|
||||||
|
for (const sig of MOJIBAKE_SIGNATURES) {
|
||||||
|
const m = lines[i]!.match(sig.pattern)
|
||||||
|
if (m) {
|
||||||
|
violations.push({
|
||||||
|
file: filePath,
|
||||||
|
line: i + 1,
|
||||||
|
signature: sig.name,
|
||||||
|
match: m[0]!,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return violations
|
||||||
|
}
|
||||||
|
|
||||||
|
export function collectFiles(dir: string): string[] {
|
||||||
|
const out: string[] = []
|
||||||
|
if (!fs.existsSync(dir)) return out
|
||||||
|
const walk = (d: string) => {
|
||||||
|
for (const entry of fs.readdirSync(d, { withFileTypes: true })) {
|
||||||
|
const full = path.join(d, entry.name)
|
||||||
|
if (entry.isDirectory()) walk(full)
|
||||||
|
else if (SCAN_EXTENSIONS.has(path.extname(entry.name))) out.push(full)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
walk(dir)
|
||||||
|
return out.sort()
|
||||||
|
}
|
||||||
|
|
||||||
|
function main(): void {
|
||||||
|
if (!fs.existsSync(CORE_DIR)) {
|
||||||
|
console.error(`check-encoding-health: core/ directory not found at ${CORE_DIR} — cannot verify (fail-closed)`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
const files = collectFiles(CORE_DIR)
|
||||||
|
if (files.length === 0) {
|
||||||
|
console.error(`check-encoding-health: 0 scannable files under ${CORE_DIR} — cannot verify (fail-closed)`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
const violations: EncodingViolation[] = []
|
||||||
|
for (const f of files) {
|
||||||
|
try {
|
||||||
|
violations.push(...scanFile(f))
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`check-encoding-health: failed to read ${f}: ${(err as Error).message}`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (violations.length > 0) {
|
||||||
|
console.error(`check-encoding-health: ${violations.length} mojibake violation(s) in core/:`)
|
||||||
|
for (const v of violations) {
|
||||||
|
const rel = path.relative(ROOT, v.file)
|
||||||
|
console.error(` ${rel}:${v.line} [${v.signature}] ${JSON.stringify(v.match)}`)
|
||||||
|
}
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
console.log(`check-encoding-health: OK (${files.length} files scanned, 0 violations)`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (import.meta.main) main()
|
||||||
+3
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "octopus-workflow",
|
"name": "octopus-workflow",
|
||||||
"version": "0.1.0",
|
"version": "0.1.3",
|
||||||
"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,
|
||||||
@@ -9,7 +9,8 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"check:core-cohesion": "bun guards/check-core-cohesion.ts",
|
"check:core-cohesion": "bun guards/check-core-cohesion.ts",
|
||||||
"check:core-p1": "bun guards/check-core-p1.ts",
|
"check:core-p1": "bun guards/check-core-p1.ts",
|
||||||
"delink:core": "bun guards/delink-core.ts"
|
"delink:core": "bun guards/delink-core.ts",
|
||||||
|
"check:encoding-health": "bun guards/check-encoding-health.ts"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|||||||
Reference in New Issue
Block a user