5 Commits
34 changed files with 620 additions and 465 deletions
+3 -2
View File
@@ -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`,
-41
View File
@@ -138,31 +138,6 @@ curl -fsS -X PATCH "<gitea-base-url>/api/v1/repos/Octopus/octopus/issues/comment
单评论聚合不变量(工件索引、当前状态表等)的执行手段。 单评论聚合不变量(工件索引、当前状态表等)的执行手段。
## Pattern 7: move-issue-to-column
看板列迁移(Todo → In Progress → Review → Done)。projects/columns
发现用 REST;「把工单移到列」的操作端点语义复杂,经 swagger 查询后
调用,不臆造路径:
```bash
# 列出 repo 级 projects(projects 仅 repo 级,无 org/user 级端点)
curl -fsS -H "Authorization: token <token>" \
"<gitea-base-url>/api/v1/repos/Octopus/octopus/projects"
# 列某 project 的 columns
curl -fsS -H "Authorization: token <token>" \
"<gitea-base-url>/api/v1/repos/Octopus/octopus/projects/{project_id}/columns"
# 移动工单到列:先经 swagger.v1.json 查询 projects 相关端点后调用
curl -s <gitea-base-url>/swagger.v1.json -o /tmp/gitea-sw.json
jq -r '.paths | keys[]' /tmp/gitea-sw.json | grep projects
```
## Pattern 7.5: move-issue-to-pipeline-stage
管线阶段板列(Pipeline Stages board column)承载阶段迁移——阶段转移
落到板列,**不落** `## 当前状态` 行(该表只承载 PR / 评审 / CI 行与
非阶段阻塞项)。列序列按管线阶段定义;移动与 Pattern 7 同款
(projects/columns 经 REST + swagger 发现,同上)。
## Pattern 8: post-commit-status ## Pattern 8: post-commit-status
commit status 直发 REST(评审综合的 Tier-2 落点): commit status 直发 REST(评审综合的 Tier-2 落点):
@@ -183,22 +158,6 @@ context 公式:`pipeline/review-{stage}`(`code` / `review-dag` /
`audit-process`)。token 从实例配置读取(此处 `<token>` 占位)。 `audit-process`)。token 从实例配置读取(此处 `<token>` 占位)。
merge 前读回验证:`GET /commits/{PR_SHA}/status`。 merge 前读回验证:`GET /commits/{PR_SHA}/status`。
## Pattern 9: create-iteration-board
```bash
# 建 project(board):先经 swagger 查询 create 端点契约
curl -s <gitea-base-url>/swagger.v1.json -o /tmp/gitea-sw.json
jq '.paths["/repos/{owner}/{repo}/projects"].post' /tmp/gitea-sw.json
# 随后 POST 建板(title="{slug} — Iteration {N}", description="…")
# 再对每列(Todo / In Progress / Review / Done):
jq '.paths["/repos/{owner}/{repo}/projects/{project_id}/columns"].post' /tmp/gitea-sw.json
# 随后 POST 建列
```
projects 仅 repo 级。DAG 聚合 agent 在单门 PASS 后建板;工单正文模板带
`## Node Reference`(指向 `{epic-slug}/dag`)、`## Acceptance Criteria`、
`## Parent`。
## Pattern 10: artifact-index(工单 ↔ 工件索引) ## Pattern 10: artifact-index(工单 ↔ 工件索引)
技能发布工件后,在源工单维护 **`## 工件索引` 评论**——单一原位编辑的 技能发布工件后,在源工单维护 **`## 工件索引` 评论**——单一原位编辑的
+2 -4
View File
@@ -80,7 +80,7 @@ curl -fsS -H "Authorization: token <token>" \
## Pattern 4: read-project-column-issues ## Pattern 4: read-project-column-issues
看板列工单队列。project/column 发现: 看板列工单队列(只读)。project/column 发现:
``` ```
curl -fsS -H "Authorization: token <token>" \ curl -fsS -H "Authorization: token <token>" \
@@ -89,9 +89,7 @@ curl -fsS -H "Authorization: token <token>" \
"<gitea-base-url>/api/v1/repos/Octopus/octopus/projects/{project_id}/columns" "<gitea-base-url>/api/v1/repos/Octopus/octopus/projects/{project_id}/columns"
``` ```
「把工单移到列」等复杂操作:经 `swagger.v1.json` projects 仅 repo 级(无 org/user 级端点)。
(`jq '.paths | keys[]' | grep projects`)查询 projects 相关端点后调用,
不要臆造具体端点路径。projects 仅 repo 级(无 org/user 级端点)。
## Pattern 5: read-review-status(CLI) ## Pattern 5: read-review-status(CLI)
+1
View File
@@ -179,6 +179,7 @@ creating thin wrapper checklist files would add no value.
| 8.5 | 防范范围收窄:明令禁止「只看重点/从简」类弱化措辞(IEEE 1028-2008 完整性) | ☐ | ☐ | | | 8.5 | 防范范围收窄:明令禁止「只看重点/从简」类弱化措辞(IEEE 1028-2008 完整性) | ☐ | ☐ | |
| 8.6 | 留痕:审计产物落盘到结构化文件,而非仅存于对话 | ☐ | ☐ | | | 8.6 | 留痕:审计产物落盘到结构化文件,而非仅存于对话 | ☐ | ☐ | |
| 8.7 | 闭环:存在重审循环与收敛/审批终止条件(ISO 19011:2018 跟踪与关闭) | ☐ | ☐ | | | 8.7 | 闭环:存在重审循环与收敛/审批终止条件(ISO 19011:2018 跟踪与关闭) | ☐ | ☐ | |
| 8.8 | 流程自身测试为绿:Orchestrator(bash 可用)在派发审计员**之前**机械运行流程自身的测试套件与守卫门(`bun run test:parallel` 全量分片 + `check:*` 守卫族,如 core-parity / encoding-health / derived-artifacts),并把命令、基线 SHA 与结果摘要落盘到 `<runs-root>/audit/reviews/audit-process/process-tests-green.md`(Tier 1);审计员(只读,无 bash)核对该记录存在于当前基线且为绿——任何红测试/红守卫本身即流程缺陷,按 findings 登记(缺陷逃逸案例:红测试存活 3 轮审计、无任何维度运行过测试套件,[org-internal #4787]) | ☐ | ☐ | 运行归 Orchestrator、核对归审计员(角色边界:`core/skills/_shared/review-orchestrator-rules.md` §No bash for Explorer sub-agents) |
--- ---
+22 -1
View File
@@ -1,6 +1,6 @@
# Release 检查清单 # Release 检查清单
> 发版前自检。Release Manager 在 Phase 1 和 Phase 6 完整过一遍。 > 发版前自检。Producer (Worker) 在 Phase 1 和 Phase 6 完整过一遍。
> **维度命名空间(dimension namespace)**: 本清单中的维度代码(如 TST/BEH/GATE/REG)是**清单局部(checklist-local)**标识符,仅在本文档内唯一;其他清单可能对相同字母串绑定不同概念(如 code-review.md 的 TST=Test Quality、verification.md 的 TST=Test Suite Completeness、port.md 的 TST=Test Porting)。跨清单引用时必须带清单限定(如 code-review.md TST 5.8),不得使用裸代码。 > **维度命名空间(dimension namespace)**: 本清单中的维度代码(如 TST/BEH/GATE/REG)是**清单局部(checklist-local)**标识符,仅在本文档内唯一;其他清单可能对相同字母串绑定不同概念(如 code-review.md 的 TST=Test Quality、verification.md 的 TST=Test Suite Completeness、port.md 的 TST=Test Porting)。跨清单引用时必须带清单限定(如 code-review.md TST 5.8),不得使用裸代码。
@@ -74,3 +74,24 @@
| 6.1 | Tagged commit 可构建 | ☐ | ☐ | | | 6.1 | Tagged commit 可构建 | ☐ | ☐ | |
| 6.2 | 测试全部通过 | ☐ | ☐ | | | 6.2 | 测试全部通过 | ☐ | ☐ | |
| 6.3 | 回到了原始分支 | ☐ | ☐ | | | 6.3 | 回到了原始分支 | ☐ | ☐ | |
---
## 7. 产物发布(DIST — Artifact Distribution,octopus 项目)
> 对应 `.octopus/skills/release/SKILL.md` Phase 4b(#2003 退役 CI publish
> 管线后的手工发布流程)。**主仓库 tag + release 对象 ≠ 发布完成**——CLI
> 二进制、公开镜像、桌面 feed 三层外圈全部就位才算。本清单从磁盘读取,
> 作为技能注入被截断时的兜底事实源(v0.25.0 发布时技能注入截断恰好切掉
> Phase 4b,导致外圈整层遗漏——本节由此设立)。
| # | 检查项 | 通过 | 不通过 | 备注 |
| --- | ---------------------------------------- | ---- | ------ | ---- |
| 7.1 | 12 平台 CLI 二进制已构建(tag 的干净 checkout,`bun packages/octopus/script/build.ts`),全部 smoke test 输出版本号 | ☐ | ☐ | Windows 无 `python3` 命令,zip 归档需垫 shim 或手动补齐 |
| 7.2 | 12 个归档已挂到主仓库 release(zip×6 + tar.gz×6) | ☐ | ☐ | |
| 7.3 | (可选,Gitea-only 口径可跳过)npm/docker/AUR 外部 registry 已发或明确跳过 | ☐ | ☐ | `OCTOPUS_GITEA_ONLY=true` |
| 7.4 | `sync-public-assets.sh` 已跑:公共镜像 `Octopus/octopus-release` 的 `v{version}` release 有 12 资产 | ☐ | ☐ | 大 POST 出站不通时经服务器 localhost 中继 |
| 7.5 | `sync-public-install.sh` 已跑:install/install.ps1/README 同步到镜像 main(注意 cp 目标是**根 README.md**,不是 scripts/ 副本) | ☐ | ☐ | |
| 7.6 | `verify-public-sync.sh` PASSED | ☐ | ☐ | |
| 7.7 | Desktop feed 三腿已派发(mac 走 `desktop-mac.yml` dispatch;win/linux 各一次 `desktop-feed.yml` dispatch),`bun packages/desktop/script/verify-feed.ts --version <ver> --channel <beta|prod>` PASS(三平台 yml 全量校验:`latest.yml`/`latest-linux.yml`/`latest-mac.yml` 的 `version:` == CLI 版本 + size 对资产逐项核对 + 匿名可达) | ☐ | ☐ | #6378 起以 verify-feed.ts 为准(v0.31.0 漏腿事故 #6375 的 fail-loud 补丁);concurrency 组已按 platform 分离;stable 通道 feed 缺失见 #6376 |
| 7.8 | README 下载链接抽查(install 脚本 raw、资产直链、docs 相对链接)匿名可达 | ☐ | ☐ | v0.18 遗留 wiki 坏链由此发现 |
+3 -3
View File
@@ -46,9 +46,9 @@ Rules:
surfaced by checklist annotations alone — do NOT add live-status rows for surfaced by checklist annotations alone — do NOT add live-status rows for
them. PR / review / CI transitions carry BOTH a checklist annotation AND them. PR / review / CI transitions carry BOTH a checklist annotation AND
a live-status row. a live-status row.
- **Stage rows live on the board, not here**: pipeline stage transitions are - **No stage rows here**: pipeline stage transitions are NOT represented by
represented by the Pipeline Stages board column (shared write-patterns `## 当前状态` rows — they surface through checklist annotations at skill
Pattern 7.5), NOT by `## 当前状态` rows. Keep only the exits. Keep only the
status-sync-automated rows (PR / 代码评审 / CI) and non-stage status-sync-automated rows (PR / 代码评审 / CI) and non-stage
blockers/deferrals. blockers/deferrals.
- Update in place (same section heading `## 当前状态`), never append a second copy. - Update in place (same section heading `## 当前状态`), never append a second copy.
+1 -1
View File
@@ -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": {
+1 -1
View File
@@ -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"
+5 -3
View File
@@ -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",
@@ -31,6 +31,7 @@
}, },
"quantitative_summary": { "quantitative_summary": {
"type": "object", "type": "object",
"description": "Every check the reviewer executed this round (PASS and FAIL counted in total_checks; NA reported via na_count). The verdict field must follow these counts per the thresholds documented on verdict — a synthesis consumer recalculates pass_rate from passed/total_checks and treats a mismatch as a malformed artifact.",
"required": ["total_checks", "passed", "failed", "pass_rate"], "required": ["total_checks", "passed", "failed", "pass_rate"],
"properties": { "properties": {
"total_checks": { "type": "integer", "minimum": 1 }, "total_checks": { "type": "integer", "minimum": 1 },
@@ -74,12 +75,13 @@
}, },
"severity": { "severity": {
"type": "string", "type": "string",
"enum": ["BLOCKER", "MAJOR", "MINOR", "INFO"] "enum": ["BLOCKER", "MAJOR", "MINOR", "INFO"],
"description": "BLOCKER: breaks an acceptance criterion, hangs/leaks, or opens an abuse channel — the artifact must not land as-is. MAJOR: a real defect on a non-blocking path, or a test/lock blind to a plausible regression (a mutation would escape). MINOR: quality/drift issue with a cheap local fix (naming, duplication, small docs gap); no behavioral risk. INFO: observation, accepted-risk note, or follow-up ticket material — no action required this round. Calibration rule: severity grades the FINDING's impact, not the reviewer's confidence; low-confidence suspicions go to INFO with the evidence, never inflated to MAJOR."
}, },
"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",
+4 -4
View File
@@ -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"] },
+3 -3
View File
@@ -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"],
+3 -3
View File
@@ -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"],
+11 -11
View File
@@ -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" }
} }
+21 -7
View File
@@ -41,11 +41,6 @@ in `_shared/gitea-write-patterns.md`.
**Orchestrator actions**: **Orchestrator actions**:
0. **Move the source issue to the `review` column** on the Pipeline Stages
board per `_shared/gitea-write-patterns.md` Pattern 7.5. Skip if no
source issue exists. The review stage is a single board column covering
all review targets (DAG single gate / code review / audit-process).
0.5. **Initialize the review-status file** (round 1 only). The Orchestrator 0.5. **Initialize the review-status file** (round 1 only). The Orchestrator
creates `<runs-root>/{slug}/reviews/{stage}/status.json` before dispatching creates `<runs-root>/{slug}/reviews/{stage}/status.json` before dispatching
round 1, with the full schema-required field set round 1, with the full schema-required field set
@@ -111,6 +106,13 @@ in `_shared/gitea-write-patterns.md`.
`core/skills/_shared/review-orchestrator-rules.md` — Default `core/skills/_shared/review-orchestrator-rules.md` — Default
Explorer Permissions). Explorer Permissions).
- Temperature: 0.1 (deterministic output). - Temperature: 0.1 (deterministic output).
- StructuredOutput mount ([org-internal #5658]): pass `format: { type:
"json_schema", schema: <the reviewer-output.schema.json contents you
read at prompt build time> }` on the task dispatch — the reviewer's
final emission is schema-enforced (malformed verdict/severity/summary
args are rejected with field-path repair feedback inside the reviewer's
own loop). The findings FILE remains the canonical Tier-1 artifact; the
structured output mirrors it as a self-check.
4. Wait for all {REVIEWER_OR_AUDITOR}s to complete. Each reviewer writes its 4. Wait for all {REVIEWER_OR_AUDITOR}s to complete. Each reviewer writes its
dimension result to the skill's configured findings sink: dimension result to the skill's configured findings sink:
@@ -171,6 +173,11 @@ in `_shared/gitea-write-patterns.md`.
`tier1-local` skills; `/tmp/octopus/...` for `tier2-comment` skills) `tier1-local` skills; `/tmp/octopus/...` for `tier2-comment` skills)
- `permissions`: read + write + edit + bash + glob + grep (all allow) - `permissions`: read + write + edit + bash + glob + grep (all allow)
- `temperature`: 0.1 - `temperature`: 0.1
- `format` ([org-internal #5658]): `{ type: "json_schema", schema: <the
synthesis.schema.json contents you read> }` — the Synthesizer's final
emission is schema-enforced (malformed verdict/dimension args rejected
with field-path repair feedback in-loop). The posted fenced JSON block
remains the canonical artifact; the structured output mirrors it.
- Foreground (the Orchestrator waits for completion). - Foreground (the Orchestrator waits for completion).
3. The Synthesizer: 3. The Synthesizer:
@@ -324,8 +331,15 @@ After the {REVISION_ROLE} completes revision:
round, or the diff touches a frozen cross-session contract) — round, or the diff touches a frozen cross-session contract) —
`<instance-root>/workflow-routing.yaml` `review.max_rounds` ([org-internal #3567], process-diet `<instance-root>/workflow-routing.yaml` `review.max_rounds` ([org-internal #3567], process-diet
P0b). At budget without convergence: STOP, register the remaining P0b). At budget without convergence: STOP, register the remaining
BLOCKER/MAJOR findings as TD registry rows, verdict FAIL if any BLOCKER BLOCKER/MAJOR findings as TD registry rows, then compute the verdict
remains else PASS — the debt is carried by those TD registry rows with the shared three-tier ladder (`core/schemas/synthesis.schema.json`
`overall_verdict` + `core/skills/_shared/review-orchestrator-rules.md`
rule 3): FAIL if any BLOCKER or FAIL dimension remains, else WARN,
PASS only if every remaining dimension is PASS ([org-internal #4786]:
the prior binary "else PASS" wording silently upgraded WARN-eligible
outcomes to PASS; a PASS round has already stopped at the convergence
table above, so the residual tail lands WARN or FAIL). The debt is
carried by those TD registry rows
(see `review-code/SKILL.md` Phase D). This (see `review-code/SKILL.md` Phase D). This
supersedes the 2026-08-25 [org-internal #3380] calibration (p50=2, p95=5, n=66): the runs supersedes the 2026-08-25 [org-internal #3380] calibration (p50=2, p95=5, n=66): the runs
past round 3 are the marathon tail this budget exists to cut; their residue past round 3 are the marathon tail this budget exists to cut; their residue
@@ -32,6 +32,18 @@ Label: {DIMENSION_NAME}
## Instructions ## Instructions
### Gate records are deterministic inputs ([org-internal #5656] N-04)
When the work item carries gate records (`gate` tool results — metadata
`exit_code`, `duration_ms`, `ci_run_id`), those records ARE the command
outcomes for mechanical questions (tests/typecheck/lint): cite the exit code
verbatim (`gate <id> (<tier>): exit_code=<n> -> green|red` — canonical block
in `packages/octopus/src/gate/consume.ts`) and never substitute a model's
narration of command output. A claim that disagrees with a record's
exit_code is a finding (record wins); a mechanical claim with no record is
unverifiable — request the citation. A green verdict-cache hit
(`metadata.verdict_cache.key`) counts the same as a fresh run.
{READ_FIRST_STEPS} {READ_FIRST_STEPS}
4. For each checklist item, determine whether the {ARTIFACT_NOUN} satisfies it. 4. For each checklist item, determine whether the {ARTIFACT_NOUN} satisfies it.
Every checklist item must receive a PASS or FAIL verdict. Every checklist item must receive a PASS or FAIL verdict.
@@ -46,6 +58,21 @@ Label: {DIMENSION_NAME}
(reviewer_count > 1), use the slot-prefixed form {DIM}R{k}-F{NNN} with your (reviewer_count > 1), use the slot-prefixed form {DIM}R{k}-F{NNN} with your
1-based slot k (e.g. TOPOR1-F001) so parallel slot files never collide on id. 1-based slot k (e.g. TOPOR1-F001) so parallel slot files never collide on id.
{CONTRADICTION_RULE} {CONTRADICTION_RULE}
6b. Code graph availability (worktree sessions): if `codegraph_status` is
unavailable or reports no index for the tree under review (typical when
reviewing from a session worktree whose per-worktree `.codegraph/` index
has not finished building — see `<instance-root>/rules/code-graph.md`), do NOT
silently substitute grep+read for structural questions ("who calls /
what depends on / where defined"). Instead:
- For symbols on the MAIN branch side (baselines, design refs, cross-session
frozen contracts), you MAY query the main checkout's index (path given in
your dispatch preamble) and cite `main:<path>:<line>`.
- If no index is reachable at all, mark the affected checklist items NA
with reason "codegraph unavailable — structural check degraded to grep"
and emit one INFO finding (id `{DIM}-F001`-style, in your dimension's own
code) recording the degradation, so the Synthesizer can surface it.
Findings derived purely from grep+read when a graph query was required are
treated as low-confidence and may be waived in synthesis.
7. Assign severity strictly: 7. Assign severity strictly:
{SEVERITY_DEFINITIONS} {SEVERITY_DEFINITIONS}
8. Count every PASS and FAIL. Compute pass_rate = passed / total_checks. 8. Count every PASS and FAIL. Compute pass_rate = passed / total_checks.
+1 -1
View File
@@ -1,4 +1,4 @@
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。 # > Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
name: Coordinator name: Coordinator
purpose: > purpose: >
Pool dispatch coordinator (billing-v2 [org-internal #4041] §7, REQ-F-012): a wake-driven, Pool dispatch coordinator (billing-v2 [org-internal #4041] §7, REQ-F-012): a wake-driven,
+1 -1
View File
@@ -1,4 +1,4 @@
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。 # > Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
name: Producer name: Producer
purpose: > purpose: >
Write production artifacts — requirements, design, plans, code, fixes, Write production artifacts — requirements, design, plans, code, fixes,
+1 -1
View File
@@ -1,4 +1,4 @@
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。 # > Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
name: Reviewer name: Reviewer
purpose: > purpose: >
Review one dimension of an artifact against its checklist, read-only. Review one dimension of an artifact against its checklist, read-only.
+1 -1
View File
@@ -1,4 +1,4 @@
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。 # > Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
name: Tool name: Tool
purpose: > purpose: >
Specialized tool agent for image interpretation and other vision/utility Specialized tool agent for image interpretation and other vision/utility
+1 -1
View File
@@ -1,4 +1,4 @@
> Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。 # > Core 中立版(Increment 6a 改写,原 deferHard verbatim)。编号与条目结构严格不变(C-2 不变量);实例术语按 `core/adapters/TERMINOLOGY.md` 绑定。
name: Verifier name: Verifier
purpose: > purpose: >
Run the iteration's DoD matrix, integration tests, NFR validation, and Run the iteration's DoD matrix, integration tests, NFR validation, and
+13
View File
@@ -198,6 +198,14 @@
- **Rule 4 — Aggregation, not reinterpretation**: You MUST NOT soften, rephrase, - **Rule 4 — Aggregation, not reinterpretation**: You MUST NOT soften, rephrase,
or downgrade reviewer findings. If a reviewer writes "BLOCKER", it stays or downgrade reviewer findings. If a reviewer writes "BLOCKER", it stays
"BLOCKER" in synthesis. "BLOCKER" in synthesis.
1c. **Pass-rate recalc post-check (MANDATORY, [org-internal #5658])**: for
every dimension, recompute `pass_rate = passed / total_checks` from its
`quantitative_summary` and compare with the reported value. On a mismatch:
keep the raw file unchanged, use the RECOMPUTED value in the synthesis
digest, and note the mismatch — a reviewer-output whose `pass_rate` does
not match its own counts is a malformed artifact (the schema description
says exactly this; this step is the enforcement).
- **Rule 5 — Anonymous findings preservation**: If two reviewers independently - **Rule 5 — Anonymous findings preservation**: If two reviewers independently
flag the same issue from different dimensions, preserve both entries. Do not flag the same issue from different dimensions, preserve both entries. Do not
deduplicate by discarding one — cross-dimension agreement is itself a signal. deduplicate by discarding one — cross-dimension agreement is itself a signal.
@@ -209,6 +217,11 @@
Return ONLY this summary as your final message. Do NOT include the full Return ONLY this summary as your final message. Do NOT include the full
synthesis JSON — it is already posted to the pipeline PR / source issue synthesis JSON — it is already posted to the pipeline PR / source issue
comment (for audit-process runs, the wiki page `audit/{date}/round{N}/synthesis`). comment (for audit-process runs, the wiki page `audit/{date}/round{N}/synthesis`).
When your dispatch carries `format` (StructuredOutput mount, [org-internal
[org-internal #5658]]): the structured output is the MIRROR self-check of this summary (same
schema, schema-enforced), not a second artifact — the posted fenced JSON block
and this summary remain canonical.
Persist this summary verbatim to Persist this summary verbatim to
`<runs-root>/{slug}/reviews/{stage}/round{N}/synthesis-return.md` before `<runs-root>/{slug}/reviews/{stage}/round{N}/synthesis-return.md` before
returning (step 9 above — worker-report persistence [org-internal #2847]). returning (step 9 above — worker-report persistence [org-internal #2847]).
+2 -21
View File
@@ -1,33 +1,14 @@
--- ---
name: codegraph-setup name: codegraph-setup
description: > description: >
Use ONLY when installing, configuring, initializing, or troubleshooting CodeGraph Use ONLY when installing, configuring, initializing, or troubleshooting
(@colbymchenry/codegraph — https://github.com/colbymchenry/codegraph) in the CodeGraph (@colbymchenry/codegraph) in the octopus environment.
octopus environment. Covers global CLI install, per-project `.codegraph/`
indexing, and maintenance commands (status, sync, upgrade, uninstall). MCP
wiring into octopus.jsonc is handled by the kickoff pipeline
(`octopus kickoff` or `/kickoff`). Also use when a project session reports a
missing/stale `.codegraph/` index.
triggers: triggers:
- codegraph - codegraph
- code graph - code graph
- codegraph setup
- codegraph install
- codegraph init
- .codegraph
- codegraph_explore
- codegraph_node
- codegraph_callers
- codegraph_status
- install codegraph
# Chinese — bare nouns for code-graph concepts. The English "codegraph" brand
# name above already covers mixed-language phrasings; these cover pure Chinese.
- 代码图 - 代码图
- 代码图谱
- 代码索引 - 代码索引
- 索引代码 - 索引代码
- 装代码图
- 初始化代码图
role: Producer role: Producer
--- ---
+34 -34
View File
@@ -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.
+150 -150
View File
@@ -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`).
+4 -4
View File
@@ -3,7 +3,7 @@ name: image-interpret
description: > description: >
Use ONLY when the agent needs to read or analyze an image (screenshot, Use ONLY when the agent needs to read or analyze an image (screenshot,
design mockup, UI capture, .png/.jpg/.webp file) but the active model lacks design mockup, UI capture, .png/.jpg/.webp file) but the active model lacks
vision capability. Delegates to the Image Interpreter role (vision-capable vision capability. Delegates to the Tool role (image interpretation) (vision-capable
model) which returns a structured text spec — layout regions, colors (hex), model) which returns a structured text spec — layout regions, colors (hex),
spacing (px), typography, component hierarchy, and responsive breakpoints. spacing (px), typography, component hierarchy, and responsive breakpoints.
Do not use for non-image tasks or when the active model already supports Do not use for non-image tasks or when the active model already supports
@@ -31,7 +31,7 @@ role: Tool
You (the calling agent) cannot read images — your model has no vision. Any You (the calling agent) cannot read images — your model has no vision. Any
task that requires *seeing* a visual artifact must be delegated to the task that requires *seeing* a visual artifact must be delegated to the
**Image Interpreter** role, which runs on a vision-capable model and returns a **Tool** role (image interpretation), which runs on a vision-capable model and returns a
structured text description you can act on. structured text description you can act on.
Trigger situations: Trigger situations:
@@ -64,7 +64,7 @@ Trigger situations:
) )
``` ```
4. **Consume the returned text spec.** The Image Interpreter returns a 4. **Consume the returned text spec.** The Tool sub-agent (image interpretation) returns a
structured description only — it cannot write code or artifacts. Use the structured description only — it cannot write code or artifacts. Use the
returned spec to drive your own work (code generation, design review, returned spec to drive your own work (code generation, design review,
planning). planning).
@@ -74,7 +74,7 @@ Trigger situations:
## Notes ## Notes
- The Image Interpreter is **read-only** — it describes images, it does not edit - The Tool sub-agent (image interpretation) is **read-only** — it describes images, it does not edit
files or write code. files or write code.
- One delegation per distinct visual question. Batch multiple images into a - One delegation per distinct visual question. Batch multiple images into a
single delegation only when they must be compared. single delegation only when they must be compared.
+14
View File
@@ -127,6 +127,20 @@ Rules:
Each execution mode below defines its own Tester focus and any mode-specific orchestration overrides. Each execution mode below defines its own Tester focus and any mode-specific orchestration overrides.
**Producer test preflight** (before writing any test):
1. **No absolute-path targets outside the test's own fixture/instance dir** — target fixtures or instance-relative paths instead. Where behavior is
platform-dependent (path resolution differs Windows/Linux), assert the
platform-specific expectation explicitly; a platform mismatch must fail,
never pass by accident.
2. **Polling/wait-for loops must be bounded AND non-vacuous** — bound the
loop AND assert the body executed at least once (e.g. an iteration
counter asserted after the loop), so a timeout-cut never yields a false
green.
Anchor: CI job 112302 (PR [org-internal #6409] first head red — nonexistent absolute path
drill + 15s-bound vacuous loop).
## Greenfield vs. Brownfield ## Greenfield vs. Brownfield
**Greenfield** (new project): Create new files following the design. Project conventions are defined by the design document. **Greenfield** (new project): Create new files following the design. Project conventions are defined by the design document.
@@ -179,10 +179,6 @@ Available work items:
#### Phase 1 — Parse Context #### Phase 1 — Parse Context
> **Pipeline stage**: if the source issue exists, move it to the `implement`
> column on the Pipeline Stages board per `_shared/gitea-write-patterns.md`
> Pattern 7.5. Skip if no source issue exists.
Read the upstream artifacts to build a complete implementation context. Read the upstream artifacts to build a complete implementation context.
Resolve inputs per the DAG-route read map (Preconditions above); standalone Resolve inputs per the DAG-route read map (Preconditions above); standalone
modes read the request/bug report instead: modes read the request/bug report instead:
@@ -499,11 +495,6 @@ 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 (`POST <gitea-base-url>/api/v1/repos/{owner}/{repo}/issues` → Backlog,
> `POST .../pulls` → Review; no manual moves). Single shared reference:
> `_shared/gitea-write-patterns.md` Pattern 7.5; column semantics: wiki
> `kanban-lifecycle`.
--- ---
#### Phase 5 — Report #### Phase 5 — Report
+49 -49
View File
@@ -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)`)
+95 -92
View File
@@ -1,25 +1,23 @@
--- ---
name: release name: release
description: > description: >
Use ONLY when ready to cut a release. The Release Manager (Worker) inspects Use ONLY when ready to cut a release. The Producer (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
- 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` 缁戝畾鍒板叿浣撳疄渚嬨€?
# 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,
@@ -28,16 +26,16 @@ produced here is the input to project-specific deployment workflows.
## Agent Role ## Agent Role
The release is owned and executed by the **Release Manager** (Worker). The release is owned and executed by the **Producer** (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 `.octopus/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 Producer 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.
--- ---
@@ -46,11 +44,11 @@ nothing 鈥?re-read the release checklist state and git log to resume.
Before starting the release: Before starting the release:
- [ ] Working tree is clean (`git status` has no uncommitted changes). - [ ] Working tree is clean (`git status` has no uncommitted changes).
- [ ] `core/checklists/release.md` is accessible. - [ ] `.octopus/checklists/release.md` is accessible.
--- ---
## 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 +63,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 REST `POST .../issues` 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 (`.octopus/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 +146,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 +155,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 +191,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 +208,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 +222,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 +233,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 +249,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 +280,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 +301,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 eightarms.net -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
``` ```
@@ -323,7 +321,7 @@ credentials in the environment.
```bash ```bash
OCTOPUS_VERSION={version} OCTOPUS_RELEASE={release-id} \ OCTOPUS_VERSION={version} OCTOPUS_RELEASE={release-id} \
GH_REPO=Octopus/octopus GITEA_TOKEN=<pat> \ GH_REPO=Octopus/octopus GITEA_TOKEN=<pat> \
bun <harness-package>/script/build.ts bun packages/octopus/script/build.ts
``` ```
`build.ts` attaches the 12 release assets to the draft release. `build.ts` attaches the 12 release assets to the draft release.
@@ -339,14 +337,14 @@ credentials in the environment.
4. **Undraft the release** (makes it public): 4. **Undraft the release** (makes it public):
```bash ```bash
curl -fsS -X PATCH \ curl -fsS -X PATCH \
"<instance-base-url>/api/v1/repos/Octopus/octopus/releases/{release-id}" \ "https://eightarms.net/api/v1/repos/Octopus/octopus/releases/{release-id}" \
-H "Authorization: token $GITEA_TOKEN" \ -H "Authorization: token $GITEA_TOKEN" \
-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 "https://eightarms.net/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,18 +366,23 @@ credentials in the environment.
--- ---
## Phase 5 鈥?Rollback Plan ## Phase 5 — Rollback Plan
Document how to undo this release: Document how to undo this release:
1. **Git rollback**: 1. **Git rollback**:
- `git tag -d v{version}` (if not yet pushed). - `git tag -d v{version}` (if not yet pushed).
- `git revert {commit_hash}` (if already merged). - `git revert {commit_hash}` (if already merged).
- If the release includes DB migrations, confirm the `down` migration exists. - If the release includes DB migrations, confirm the `down` migration exists
**when the project's migration tooling provides one**. This repo's Drizzle
convention is forward-only (`migration.sql` + `snapshot.json`, no `down`
files) — there, record the reversal plan (manual SQL / restore) or mark the
change irreversible instead of waiting on a `down` file that never comes.
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
where the tooling offers one; otherwise document the manual reversal steps.
- 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
@@ -390,7 +393,7 @@ git tag -d v{version}
# or: git revert {hash} # or: git revert {hash}
### Data Rollback ### Data Rollback
- Migration `{name}`: down {exists / NOT FOUND} - Migration `{name}`: down {exists / NOT FOUND / N/A — forward-only tooling}
- {additional risks} - {additional risks}
### Rollback Triggers ### Rollback Triggers
@@ -403,7 +406,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 +414,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 +430,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**: `.octopus/skills/retrospective/SKILL.md` (optional — run on any project state)
``` ```
--- ---
## References ## References
- `core/checklists/release.md` 鈥?Release checklist - `.octopus/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
+11 -5
View File
@@ -97,10 +97,7 @@ The stage IDs remain as-is — they are used by the stage registry and auto-appr
This skill follows the standardized parallel-review pipeline. The full mechanics are in: This skill follows the standardized parallel-review pipeline. The full mechanics are in:
- `<!-- inject: ../_shared/review-pipeline-phases.md -->` — Phase A through Phase E - `<!-- inject: ../_shared/review-pipeline-phases.md -->` — Phase A through Phase E
- **Phase A step 0**: move the source issue to the `review` column on the (canonical orchestrator actions for the shared flow)
Pipeline Stages board per `_shared/gitea-write-patterns.md` Pattern 7.5
(see `_shared/review-pipeline-phases.md` for the canonical orchestrator
actions — step 0 is now part of the shared flow).
- `<!-- inject: ../_shared/review-orchestrator-rules.md -->` — mandatory orchestrator rules - `<!-- inject: ../_shared/review-orchestrator-rules.md -->` — mandatory orchestrator rules
- `<!-- inject: ../_shared/review-final-report-template.md -->` — final report format - `<!-- inject: ../_shared/review-final-report-template.md -->` — final report format
- `<!-- inject: ../_shared/review-revision-prompt.md -->` — Phase C revision prompt template - `<!-- inject: ../_shared/review-revision-prompt.md -->` — Phase C revision prompt template
@@ -187,6 +184,15 @@ The audit target is the **SDLC factory itself** — the set of skills, checklist
- [ ] `core/skills/` exists and contains at least one `SKILL.md`. - [ ] `core/skills/` exists and contains at least one `SKILL.md`.
- [ ] `core/checklists/audit-process.md` is accessible. - [ ] `core/checklists/audit-process.md` is accessible.
- [ ] `core/schemas/` contains the shared review schemas (`reviewer-output`, `synthesis`, `review-status`). - [ ] `core/schemas/` contains the shared review schemas (`reviewer-output`, `synthesis`, `review-status`).
- [ ] **Process-tests-green gate ([org-internal #4787] STD 8.8)**: before dispatching
auditors, the Orchestrator (bash-capable; Explorer auditors have none by
construction) runs the process's own suite and guards — from
`<harness-package>`: `bun run test:parallel`, plus the repo-root `check:*`
guard family — and writes the commands, baseline SHA, and per-command result
summary to `<runs-root>/audit/reviews/audit-process/process-tests-green.md`
(Tier 1). The read-only STD 8.8 auditor verifies that record against the
current baseline; any red test/guard is itself a process defect and MUST be
filed as a finding.
No `.artifacts/{slug}/` inputs are required — the audit reads `<instance-root>/` itself. Audit outputs are published as Gitea wiki pages under `audit/{date}/`. No `.artifacts/{slug}/` inputs are required — the audit reads `<instance-root>/` itself. Audit outputs are published as Gitea wiki pages under `audit/{date}/`.
@@ -201,7 +207,7 @@ No `.artifacts/{slug}/` inputs are required — the audit reads `<instance-root>
| 5 | FLOW | Pipeline Cohesion | FLOW 5.1–5.6 | Preconditions + output sections of every `SKILL.md` | | 5 | FLOW | Pipeline Cohesion | FLOW 5.1–5.6 | Preconditions + output sections of every `SKILL.md` |
| 6 | SCH | Schema Contract | SCH 6.1–6.5 | `core/schemas/*.json` + skills that reference them | | 6 | SCH | Schema Contract | SCH 6.1–6.5 | `core/schemas/*.json` + skills that reference them |
| 7 | DUP | Duplication & Drift | DUP 7.1–7.4 | ALL `SKILL.md` + ALL `checklists/*.md` | | 7 | DUP | Duplication & Drift | DUP 7.1–7.4 | ALL `SKILL.md` + ALL `checklists/*.md` |
| 8 | STD | Audit Standards (ISO/IEEE) | STD 8.1–8.7 | `review-*` skills, `review-artifact` (target: `audit-process`) itself | | 8 | STD | Audit Standards (ISO/IEEE) | STD 8.1–8.8 | `review-*` skills, `review-artifact` (target: `audit-process`) itself |
| 9 | AGT | AGENTS.md Conformance | AGT 9.1–9.5 | ALL `SKILL.md` + `AGENTS.md` | | 9 | AGT | AGENTS.md Conformance | AGT 9.1–9.5 | ALL `SKILL.md` + `AGENTS.md` |
| 10 | TRC | Traceability | TRC 10.1–10.5 | `review-*` skills, schemas, status artifacts | | 10 | TRC | Traceability | TRC 10.1–10.5 | `review-*` skills, schemas, status artifacts |
+24 -2
View File
@@ -458,7 +458,7 @@ table).
<!-- section:phase-a --> <!-- section:phase-a -->
## Phase A — Parallel Review ## Phase A — Parallel Review
> **Canonical mechanics**: `core/skills/_shared/review-pipeline-phases.md` — keep this section in sync with the canonical Phase A–E flow. This section adds review-code-specific constants (10 dimensions, severity definitions, risk tiers, output mode, standalone-bugfix mode). Step 0 (move source issue to `review` column on Pipeline Stages board per Pattern 7.5) is inherited from the shared Phase A orchestrator actions; see `_shared/review-pipeline-phases.md`. > **Canonical mechanics**: `core/skills/_shared/review-pipeline-phases.md` — keep this section in sync with the canonical Phase A–E flow. This section adds review-code-specific constants (10 dimensions, severity definitions, risk tiers, output mode, standalone-bugfix mode); orchestrator actions are inherited from the shared Phase A flow.
### Standalone Bugfix Mode — Orchestrator Dispatch ### Standalone Bugfix Mode — Orchestrator Dispatch
@@ -530,6 +530,13 @@ fallback is retired with the design pipeline, [org-internal #3072] phase 3):
emits the reviewer's output; it does NOT touch the code under review, so emits the reviewer's output; it does NOT touch the code under review, so
rule #7 auditor independence holds.) rule #7 auditor independence holds.)
- Temperature: 0.1 (deterministic output). - Temperature: 0.1 (deterministic output).
- StructuredOutput mount ([org-internal #5658]): pass `format: { type: "json_schema",
schema: <the reviewer-output.schema.json contents you read at prompt
build time> }` on the Task dispatch — the reviewer's final emission is
schema-enforced (malformed verdict/severity/summary args are rejected
with field-path repair feedback inside the reviewer's own loop). The
findings FILE remains the canonical Tier-1 artifact; the structured
output mirrors it as a self-check.
**Worktree-path rule (mandatory)**: Every file path in the reviewer prompt **Worktree-path rule (mandatory)**: Every file path in the reviewer prompt
(`{FILE_SECTIONS}`, design reference paths, artifact paths) MUST be the (`{FILE_SECTIONS}`, design reference paths, artifact paths) MUST be the
@@ -545,6 +552,21 @@ fallback is retired with the design pipeline, [org-internal #3072] phase 3):
observed in the 2026-07 auto-approve retrospective (5-round convergence, observed in the 2026-07 auto-approve retrospective (5-round convergence,
67.7% rework fraction). 67.7% rework fraction).
**Code-graph availability preamble (worktree sessions, [org-internal #4187])**: the main
checkout's `.codegraph/` index does NOT cover a session worktree
([org-internal #2561]/TD-006). Before dispatch, the Orchestrator checks the worktree's
own index: run `codegraph_status` against the worktree root, or check for
`<worktree-root>/.codegraph/codegraph.db` (a bare `init.log` without the
db means the background `session-worktree.sh create` init is still running).
Include in each reviewer's dispatch preamble: (a) the worktree index state
(ready / building / absent), and (b) when absent or still building, the
ABSOLUTE path of the main checkout (which has its own `.codegraph/`) so
DGN/COR reviewers can query MAIN-side symbols (design baselines, frozen
cross-session contracts) from the main checkout's index and cite them as
`main:<path>:<line>`. Reviewers apply rule 6b of
`_shared/review-reviewer-prompt.md` (NA + INFO finding) when no index is
reachable at all — grep-only structural claims must never pass silently.
4. Wait for all reviewers to complete. Each reviewer writes its finding JSON to 4. Wait for all reviewers to complete. Each reviewer writes its finding JSON to
`<runs-root>/{slug}/reviews/code/round{N}/findings-{DIMENSION_CODE}.json` `<runs-root>/{slug}/reviews/code/round{N}/findings-{DIMENSION_CODE}.json`
(Tier 1 local, validated by `core/schemas/reviewer-output.schema.json`). After all (Tier 1 local, validated by `core/schemas/reviewer-output.schema.json`). After all
@@ -860,7 +882,7 @@ After the Developer completes revision:
**Convergence criteria**: see `core/skills/_shared/review-pipeline-phases.md` Phase D for the canonical table (stop conditions). `{MAX_ROUNDS}` is per-skill — code review binds the routing budget: **2** rounds default, **3** for high risk (a BLOCKER survived a round, or the diff touches a frozen cross-session contract) — `<instance-root>/workflow-routing.yaml` `review.max_rounds` ([org-internal #3567]). The code-review-specific addition is inheritance of the shared INFO-disposition rule. The shared Round-3 Escalation Guardrail is scoped to the `review-artifact` skill targets only and does not apply here. **Convergence criteria**: see `core/skills/_shared/review-pipeline-phases.md` Phase D for the canonical table (stop conditions). `{MAX_ROUNDS}` is per-skill — code review binds the routing budget: **2** rounds default, **3** for high risk (a BLOCKER survived a round, or the diff touches a frozen cross-session contract) — `<instance-root>/workflow-routing.yaml` `review.max_rounds` ([org-internal #3567]). The code-review-specific addition is inheritance of the shared INFO-disposition rule. The shared Round-3 Escalation Guardrail is scoped to the `review-artifact` skill targets only and does not apply here.
**Round budget (mandatory, [org-internal #3567])**: when `current_round` reaches the budget (2; 3 for high risk) without convergence, do NOT enter another round. STOP: register every remaining BLOCKER/MAJOR finding as TD registry rows (`rules/ticket-lifecycle.md`), record the verdict (FAIL if any BLOCKER remains, else PASS — the debt is carried by the TD registry rows just registered), and close the loop. A review that cannot converge within budget is a design-level stall wearing review clothes (retro 2026-08-21 / [org-internal #3050]): in DAG task mode, map it to the DAG oversize signals (`<instance-root>/workflow-routing.yaml` `dag.size_derivation.oversize_signals`) and let the aggregator agent drive re-derivation + a `review-dag` re-run; otherwise record the design-level finding on the Epic, or open `{slug}/03-design-amendments` via `wiki 读写 API(见 TERMINOLOGY)` (`_shared/gitea-write-patterns.md` Pattern 1). **Round budget (mandatory, [org-internal #3567])**: when `current_round` reaches the budget (2; 3 for high risk) without convergence, do NOT enter another round. STOP: register every remaining BLOCKER/MAJOR finding as TD registry rows (`rules/ticket-lifecycle.md`), record the verdict with the shared three-tier ladder (`core/schemas/synthesis.schema.json` `overall_verdict` + `core/skills/_shared/review-orchestrator-rules.md` rule 3): FAIL if any BLOCKER or FAIL dimension remains, else WARN, PASS only if every remaining dimension is PASS ([org-internal #4786]: the prior binary "else PASS" wording silently upgraded WARN-eligible outcomes to PASS), and close the loop. The debt is carried by the TD registry rows just registered, not by the recorded verdict. A review that cannot converge within budget is a design-level stall wearing review clothes (retro 2026-08-21 / [org-internal #3050]): in DAG task mode, map it to the DAG oversize signals (`<instance-root>/workflow-routing.yaml` `dag.size_derivation.oversize_signals`) and let the aggregator agent drive re-derivation + a `review-dag` re-run; otherwise record the design-level finding on the Epic, or open `{slug}/03-design-amendments` via `wiki 读写 API(见 TERMINOLOGY)` (`_shared/gitea-write-patterns.md` Pattern 1).
**Efficiency optimization**: see `core/skills/_shared/review-pipeline-phases.md` Phase D (same rule — from round 2 onward ([org-internal #2598]), only spawn reviewers for FAIL/WARN/UNRESOLVED dimensions; full sweep every 2 rounds). Round ≥2 reviewer input is delta-scoped to the revised files + their neighbors. **Efficiency optimization**: see `core/skills/_shared/review-pipeline-phases.md` Phase D (same rule — from round 2 onward ([org-internal #2598]), only spawn reviewers for FAIL/WARN/UNRESOLVED dimensions; full sweep every 2 rounds). Round ≥2 reviewer input is delta-scoped to the revised files + their neighbors.
+15 -8
View File
@@ -196,10 +196,6 @@ each missing item.
<!-- section:phase-1 --> <!-- section:phase-1 -->
## Phase 1 — Load Verification Context ## Phase 1 — Load Verification Context
> **Pipeline stage**: if the source issue exists, move it to the `verify`
> column on the Pipeline Stages board per `_shared/gitea-write-patterns.md`
> Pattern 7.5. Skip if no source issue exists.
Read the upstream artifacts. DAG task/milestone mode (the live path — see Read the upstream artifacts. DAG task/milestone mode (the live path — see
the DAG branch below for its read map): the DAG branch below for its read map):
@@ -310,6 +306,20 @@ gate fingerprint is keyed on the working tree (SHA + uncommitted state +
untracked), so it covers pre-push/headless states the same-SHA CI clauses untracked), so it covers pre-push/headless states the same-SHA CI clauses
cannot. cannot.
### 2.0b — Gate Records as Deterministic Inputs ([org-internal #5656] N-04)
Mechanical checks MUST be grounded in gate records (`gate` tool result
metadata), never in a model's transcription of command output:
- Cite `exit_code` verbatim — canonical block from
`packages/octopus/src/gate/consume.ts`: `gate <id> (<tier>):
exit_code=<n> -> green|red`, plus `duration_ms` / `ci_run_id` when present.
- `exit_code === null` (timeout/abort) is NO verdict — rerun required; a
pass claim cannot stand on it.
- A green verdict-cache hit (`metadata.verdict_cache.key`) satisfies the
check the same as a fresh run (content-addressed, same fingerprint).
- Narration drift (claim != record) is itself a verification finding.
### 2.1 Full Test Suite ### 2.1 Full Test Suite
```bash ```bash
@@ -1203,10 +1213,7 @@ If the user confirms, invoke the commit workflow per
An iteration is NOT Done until all code is committed in git history. An iteration is NOT Done until all code is committed in git history.
After the commit gate passes, move the source issue to the `done` column on Issue closing follows the lifecycle per `issue-checklist-sync.md` 归档动作.
the Pipeline Stages board per `_shared/gitea-write-patterns.md` Pattern 7.5.
Skip if no source issue exists. `done` is a stage column marker; issue
closing follows the lifecycle per `issue-checklist-sync.md` 归档动作.
To notify workflow completion, call the `signal_stage_done` tool. To notify workflow completion, call the `signal_stage_done` tool.
+94
View File
@@ -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
View File
@@ -1,6 +1,6 @@
{ {
"name": "octopus-workflow", "name": "octopus-workflow",
"version": "0.1.2", "version": "0.1.7",
"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",