diff --git a/.trae-html-share-packages/scripts/create-cloudbird-agent-app.html.zip b/.trae-html-share-packages/scripts/create-cloudbird-agent-app.html.zip index 2096cec..1955bd5 100644 Binary files a/.trae-html-share-packages/scripts/create-cloudbird-agent-app.html.zip and b/.trae-html-share-packages/scripts/create-cloudbird-agent-app.html.zip differ diff --git a/.trae-html-share-packages/scripts/create-verifier-app.html.zip b/.trae-html-share-packages/scripts/create-verifier-app.html.zip new file mode 100644 index 0000000..df37f0c Binary files /dev/null and b/.trae-html-share-packages/scripts/create-verifier-app.html.zip differ diff --git a/AGENTS.md b/AGENTS.md index e92a2f2..2200c3d 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -14,6 +14,9 @@ PM(项目经理)工作契约(ADR-0085,索引型;治理仓豁免行数 6. front-desk 命令(卡 issue 评论,conductor 转介 arbiter 处理):/claim 认领 · /release 释放租约 · /retry 隔离回流 + +迷路了(从别的仓/入口进来)?全入口路由表:docs/NAVIGATION.md(入口矩阵+高频困惑 FAQ,ADR-0055/0085,#363 收口)。 + ## PM 优先(ADR-0085) - **入职三步**:① `governance/REPOS.yaml` 看版图 → ② `docs/pm/PLAYBOOK.md`(阶段手册:资源/用法/代价/报告格式)→ ③ 最近 4 周运行报告(`Cloudbird-Software/archive` 仓 `runs/`) @@ -32,7 +35,7 @@ PM(项目经理)工作契约(ADR-0085,索引型;治理仓豁免行数 ## 硬规则 -- 治理文件(governance/ standards/ scripts/ .github/ CODEOWNERS profile/ Makefile docs/)= C1 路径:PR 必须引用 ADR-NNNN(家园=archive/adr/,ADR-0085),owner-only review +- 治理文件(governance/ standards/ scripts/ .github/ CODEOWNERS profile/ Makefile docs/)= C1 路径:PR 必须引用 ADR-NNNN(家园=archive/adr/,ADR-0085),owner-only review;**治理变更不需要卡**(卡只承载 spec 派生的实现工作)。spec 位置:治理 specs=`specs/IR-XXXX/`(本仓),产品 feature specs=产品仓 `specs//` - 写仓身份 = GitHub App `cloudbrid-agent`(AG-1);令牌经 scripts/gh-app-token.sh,单仓作用域、1h 过期(本仓驻留 agent 直接用 `scripts/ghcb`)。例外:org 级 Project 写与成员判定用 GOVERNANCE_TOKEN(仅 workflow secrets 面,不落 agent 手) - ADR 落盘:PR 至 `archive/adr/ADR-NNNN-*.md` + 更新同目录 INDEX.yaml(家园单仓化,ADR-0085) - 红队守门(ADR-0082):spec/测试设计路径 PR 必须经红队审计;g060 锁定 specs/*/suite/**(ADR-0061/0081) @@ -42,14 +45,14 @@ PM(项目经理)工作契约(ADR-0085,索引型;治理仓豁免行数 ## 常用命令(本仓驻留) - 校验本仓声明:`.github/workflows/gate.yml`(本地等价:`make gates-pr`) -- 漂移检测/修复(owner/CI 专属,需 org admin PAT):`GH_TOKEN= bash governance/drift-check.sh`(每日 CI 自动跑)· `GH_TOKEN= bash governance/apply.sh`(幂等) +- 漂移检测/修复(owner/CI 专属,需 org admin PAT):`GH_TOKEN= make drift-check`(等价 `bash governance/drift-check.sh`,每日 CI 自动跑;agent 侧预检=`make gates-pr`)· `GH_TOKEN= bash governance/apply.sh`(幂等) - 修复循环上限执法:`GH_TOKEN= bash governance/auto-fix-limit.sh` · 成本熔断:`GH_TOKEN= bash governance/cost-check.sh` - 取 App 令牌:`GH_TOKEN=$(scripts/ghcb )`(缓存命中零网络,ADR-0044) - CNB 池运维:`Cloudbird-Software/cnb-bridge` 仓(accounts.yaml/cnb_pool.py/REMOVAL.md);周审计=本仓 cnb-audit 工作流(EX-1 三接缝之一) ## 索引(用到再读) -- 治理总声明 governance/GOVERNANCE.yaml(agent_runtime/external_compute 域=ADR-0085 新范式)· 组织地图 governance/REPOS.yaml · 期望状态 governance/expected-state.json +- 全入口路由 docs/NAVIGATION.md(从哪进来→去哪→怎么走;断链=test-navigation.sh 红)· 治理总声明 governance/GOVERNANCE.yaml(agent_runtime/external_compute 域=ADR-0085 新范式)· 组织地图 governance/REPOS.yaml · 期望状态 governance/expected-state.json - 政策集 governance/policy/(languages/testing/automation-limits 含 cnb: 节) - 状态机 governance/transitions.yaml(T7 卡就绪/T8 卡完成/T9 IR 验收——conductor 唯一定义源) - PM 手册 docs/pm/PLAYBOOK.md · 工具目录 governance/providers.yaml(无密钥) · 运行报告 archive 仓 runs/ diff --git a/Makefile b/Makefile index e0df20a..f173b5a 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ CARD ?= # 注释须独立成行:行尾注释会把 # 前的尾随空格并入 REPO 值,gh -R 解析失败且被吞。 REPO ?= Cloudbird-Software/.github -.PHONY: card-test gates-pr +.PHONY: card-test gates-pr drift-check card-test: ## 读卡 AC 列表并提示测试先行:make card-test CARD= @test -n "$(CARD)" || { echo "用法: make card-test CARD=(缺 CARD)" >&2; exit 2; } @echo "== 卡 $(REPO)#$(CARD) 的 AC(测试先行:先按 AC 写红测试再实现)==" @@ -28,3 +28,7 @@ gates-pr: ## 本地等价关卡清单(gate.yml 语义):make gates-pr && echo "OK 治理自测(governance/tests;需 jq)" @python3 -c "import glob,yaml;[yaml.safe_load(open(f,encoding='utf-8')) for f in glob.glob('governance/**/*.yaml',recursive=True)+glob.glob('standards/**/*.yaml',recursive=True)+glob.glob('.github/workflows/*.yml')];print('OK yaml 解析(governance/standards/workflows)')" @echo "== 开 PR 前检查单(机器不可判部分):PR body 引用 ADR-NNNN(C1)/ body 带 Card: 元数据行 / diff<400 行 ==" + + +drift-check: ## C1 本地预检(owner/CI 面,需 org admin PAT):GH_TOKEN= make drift-check + @bash governance/drift-check.sh diff --git a/docs/NAVIGATION.md b/docs/NAVIGATION.md new file mode 100644 index 0000000..9037dc3 --- /dev/null +++ b/docs/NAVIGATION.md @@ -0,0 +1,67 @@ +# NAVIGATION —— 全入口路由(从哪进来 → 该做什么 → 怎么做) + +> 目的:任何 agent / 人从任意入口(org 首页、任意仓、issue 表单、Actions)落地后, +> 30 秒内定位「该做什么、怎么做」。这是 #362 治理可达性审计(32 次 PM 模拟运行, +> 置信度 4.8/10)的收口件之一。 +> 依据:ADR-0055(统一入口协议)· ADR-0085(PM 优先范式)· ADR-0064(Bug 流)。 +> 维护契约:新增入口面(仓 / 模板 / 表单 / 工作流)须同步本表;本表引用的本仓文件 +> 必须真实存在——断链由 `governance/tests/test-navigation.sh` 机械检测(gate 每 PR 跑)。 + +## §0 三句话版本 + +1. **在产品/支撑仓干活** → 唯一工作凭证是卡:`bash ghcb next ` 找 + `state:ready` 卡 → `bash ghcb claim ` 认领 → 实现 → PR body 带 `Card: /#` 行。 +2. **要改治理面**(governance/ standards/ scripts/ .github/ specs/ profile/ CODEOWNERS, + 以及按 AGENTS.md 硬规则视同 C1 的 docs/ 与 Makefile)→ **不需要卡**:直接开 PR + + 引用 ADR-NNNN + owner review(C1 流程,见 §2)。 +3. **带全局职责进来(PM)** → [AGENTS.md](../AGENTS.md)「PM 优先」节 + + [docs/pm/PLAYBOOK.md](pm/PLAYBOOK.md) 全文 + archive 仓 `runs/` 最近 4 周运行报告。 + +## §1 入口矩阵 + +| 你在哪 / 你是谁 | 先读 | 然后 | +|---|---|---| +| org 首页(profile/README.md) | 本表 §0 | 按角色下钻;仓库全量真源 [governance/REPOS.yaml](../governance/REPOS.yaml) | +| 产品仓(AI_Web_School / mutual / QW_Arena1 …) | 该仓根部 AGENTS.md 的入口协议块 | 取 ghcb(AGENTS.md 内钉 SHA 命令)→ `ghcb next ` 找卡;无卡不开工,新想法走 intent 表单 | +| 治理仓 .github | [AGENTS.md](../AGENTS.md)(本仓契约) | 治理变更走 C1(§2,无需卡);卡工作照入口协议块 | +| 治理仓 CI-Workflows | 该仓 AGENTS.md | workflow/pipeline 变更=C1 面(.github/ 路径):PR 引 ADR + owner review | +| 治理仓 archive | `runs/README.md` | 运行报告只追加(append-only);ADR 落 `adr/` + 更新 INDEX.yaml | +| 支撑仓 cnb-bridge | 仓内 `REMOVAL.md` + `accounts.yaml` | 池运维 owner 面;派单一律经 .github 仓 `cnb-dispatch` 工作流(key 不入上下文) | +| 支撑仓 arbiter / holdout | —(owner 直管) | 你不直接调用(见 §2「conductor/arbiter」);holdout 对 agent 只读 | +| 发现 bug | [.github/ISSUE_TEMPLATE/bug.yml](../.github/ISSUE_TEMPLATE/bug.yml) | 提交即机器复现(B1–B5,ADR-0064):reproduced → 修复合入 → fixed → done | +| 有新意图(feature/治理意图) | [.github/ISSUE_TEMPLATE/intent.yml](../.github/ISSUE_TEMPLATE/intent.yml) | IR 流:owner 签署 → spec(自著合法)→ 红队 → 开卡(PLAYBOOK §2–§3) | +| 想看全局进度 | `bash ghcb board` | 全状态流水线(ir-draft…done 的 IR 与卡,非只 ready 卡) | + +## §2 高频困惑(#362 实测断裂点,逐条落点) + +- **spec 放哪**:治理 specs 在 `.github/specs/IR-XXXX/`;产品 feature specs 落产品仓 + 本仓 `specs//`(IR 一律在 .github 仓开,编号全局唯一;spec 与 suite 随实现仓走, + suite 门 T-14/T5 按所在仓生效)。 +- **治理变更要不要开卡**:不要。卡流程(T7→T8)只承载 spec 派生的实现工作; + 治理变更走 C1:PR + 引用 ADR-NNNN(新建或引既有)+ owner-only review + merge。 + 同一 PR 不混两种性质。 +- **C1 的「drift-check 本地预检」跑不了**:drift-check 需 org admin PAT(owner/CI 专属面)。 + agent 侧等价预检 = `make gates-pr`(.github 仓);owner 可 `make drift-check`。 + 治理漂移的最终判定是每日 CI drift(GOVERNANCE_TOKEN),不靠你本地。 +- **g060 拦截了你的 suite 变更**:不是故障,是设计。`specs/*/suite/**` 被锁定 + (ADR-0061/0081),授权身份仅 owner 与 verifier-app;你的 spec PR 含 suite 变更会被 + g060 拦下(exit 2)并自动开裁决 issue,owner 以 `/g060-adopt <证据>` 采纳或 + `/g060-reject` 驳回(TTL 72h)。首次创建 suite 同样走此路径——无豁免通道是刻意的。 +- **conductor / arbiter 怎么触发**:不用也无法手动调用。conductor 监听 issue 事件 + (`state:*` 标签、评论 `/start` `/claim` `/retry`),arbiter 由 conductor 转介执行 + CAS 租约。你只管评论与打标签,状态换签是机器的事。 +- **测试先行 vs gate 要绿**:红测试不进 main。spec PR 的 suite 断言制度/结构不变量, + 合入时必须绿;修 bug 的失败复现测试走 bug 流(B2 reproduced 锚定 base 红, + fix PR 合入时转绿)。 +- **PM 能不能改 .github/workflows/**:能提 PR——但这是 C1 面:引用 ADR、owner-only + review;PM 的自主性在生成侧,判定与合并归 owner 与 gate(PLAYBOOK §0 红线)。 +- **根目录文件归属(C1 还是 C3)**:GOVERNANCE.yaml `flows.governance_change` 的 + classes 是分类真源;AGENTS.md 硬规则把 docs/ 与 Makefile 一并视同 C1(宁严勿松)。 + 拿不准就按 C1 走:多引一个 ADR 的成本远低于走错流程的返工。 + +## §3 机器护栏 + +本文件的断链由 [governance/tests/test-navigation.sh](../governance/tests/test-navigation.sh) +机械检测(随 gate 与 `make gates-pr` 每次运行):入口引用的本仓文件必须存在、 +AGENTS.md 行数 ≤ 60(治理仓豁免上限)、入口协议块标记完整、本表必须保留 g060/C1/ +drift-check 等高频困惑锚点。 diff --git a/docs/pm/PLAYBOOK.md b/docs/pm/PLAYBOOK.md index dfff386..a9b130d 100644 --- a/docs/pm/PLAYBOOK.md +++ b/docs/pm/PLAYBOOK.md @@ -41,6 +41,13 @@ worker 视角的认领/开工协议(AGENTS.md entry-protocol v1 块)对 PM - 你自己写——完全合法,且是深度理解 IR 的最好方式; - spec-author 快速通道:CI-Workflows `spec-author.yml` 流水线生成骨架你再修。 模板:CI-Workflows `pipeline/spec-template.md`(正文条款结构 / AC given-when-then / INV-BEH-IFACE 分节)。 +- **spec 放哪**(#363 落点):治理 specs 在 `.github/specs/IR-XXXX/`; + 产品 feature specs 落产品仓本仓 `specs//`——IR 一律在 .github 仓开 + (编号全局唯一),spec 与 suite 随实现仓走(suite 门 T-14/T5 按所在仓生效)。 +- **g060 会拦你的 suite**(不是故障,是设计):`specs/*/suite/**` 被锁定 + (ADR-0061/0081),授权身份仅 owner 与 verifier-app;你的 spec PR 含 suite 变更 + 会被 g060 拦下(exit 2)并自动开裁决 issue,owner 以 `/g060-adopt <证据>` 采纳 + (TTL 72h,dead-man 兜底)。首次创建 suite 同样走此路径——无豁免通道。 - **spec PR 必带**: - `suite/` 目录(ADR-0083 suite 门;至少一个非空可解析测试文件——T5 `suite_ready_required` 谓词会现场重查,T-14 亦要求);注意 `specs/*/suite/**` 在 g060 锁定集内 @@ -132,3 +139,26 @@ IR 级收口,证据可机械回查。门禁=T9 谓词(ADR-0085 决策 5) | 验证者 | 独立验收/封存场景考试 | holdout 仓(owner 直管)+ CI-Workflows `pipeline/verifier-exam/`、`verifier-exam.yml` | verifier 档 token(计量入账,automation-limits.yaml) | | 治理政策 | 无人值守阈值/测试政策/语言准入 | `governance/policy/`(automation-limits.yaml ADR-0040 · testing.yaml T-01..T-15 · languages.yaml) | 读它比违约便宜 | | 运行报告 | 经验沉淀与改进燃料 | `archive/runs/YYYY-WNN.md`(追加);digest=archive `runs-digest.yml` | 10 分钟/次;不写=下周 PM 重复踩坑 | +| conductor / arbiter | 生命周期转移与 CAS 租约(T1–T9) | **无需也无法手动调用**:conductor 监听 issue 事件(`state:*` 标签、`/start` `/claim` `/retry` 评论),arbiter 由 conductor 转介 | 零(App 身份自动执行) | +| 全入口路由 | 任何入口落地后 30 秒定位该做什么 | `.github` 仓 `docs/NAVIGATION.md`(入口矩阵+高频困惑 FAQ) | 一次阅读 | + +## §9 发起治理变更(C1 runbook,#363 落点) + +你主动改治理面(governance/ standards/ scripts/ .github/ specs/ profile/ CODEOWNERS, +及按 AGENTS.md 硬规则视同 C1 的 docs/ 与 Makefile)时:**不需要卡**——卡流程 +(T7→T8)只承载 spec 派生的实现工作,治理变更的授权凭证是 ADR + PR 记录 +(GOVERNANCE.yaml `flows.governance_change`)。 + +1. **分类**:查 `flows.governance_change` classes(C1/C2/C3 真源);拿不准按 C1 + 走——多引一个 ADR 的成本远低于走错流程的返工。 +2. **ADR**:新决策=新写 ADR(PR 至 archive `adr/ADR-NNNN-*.md` + 更新 INDEX.yaml); + 执行性变更(既有决策的落地)=引用既有 ADR-NNNN 即可。 +3. **本地预检**:`make gates-pr`(gate.yml 本地等价面)。drift-check 是 owner/CI 面 + (org admin PAT)——你跑不了不是你的问题,agent 侧等价预检就是 gates-pr。 +4. **PR**:title/body 引用 ADR-NNNN(gate adr-required 机器拦,幽灵 ADR 不放行); + bug 修复关联 bug 单(B3 状态回写靠它)。 +5. **合并**:owner-only review + merge。你的自主性在生成侧(写什么、怎么写), + 判定与合并归 owner 与 gate(§0 红线)——包括 `.github/workflows/`:能提 PR, + 不能自批。 + +迷路时先读 `docs/NAVIGATION.md`(全入口路由),再回来翻本手册对应阶段。 diff --git a/ghcb b/ghcb new file mode 100755 index 0000000..40a92cb --- /dev/null +++ b/ghcb @@ -0,0 +1,90 @@ +#!/usr/bin/env bash +# ghcb —— cloudbrid-agent 便捷入口(ADR-0044 令牌 + ADR-0051 找活协议 + ADR-0055 front-desk 扩展) +# +# 用法: +# GH_TOKEN=$(ghcb [--refresh]) # 铸 App 单仓安装令牌(ADR-0044,原用法不变) +# ghcb next [repo] # 找活:列出 (缺省=origin 所在仓)state:ready 的卡 +# ghcb claim [repo] # 认领:在卡 #n 评论 /claim——conductor 校验先到先得并置 +# # state:in-progress(ADR-0049 T3);ghcb 不持有状态写权 +# ghcb release [repo] # 释放:评论 /release——conductor 转介 arbiter 删租约 +# # (仅 holder 本人或 owner 可成,ADR-0054 §4) +# ghcb status [repo] # 只读:卡标签态(真相源)+ arbiter 租约持有者/到期 +# ghcb card-meta [repo] # 输出 PR body 应贴的卡元数据行(Card: /#) +# +# next/claim/release 用调用方自己的 gh 凭据(gh auth login);认领合法性单一真源=conductor guard +# + arbiter CAS(ADR-0055 转介)。status 读 arbiter 仓 refs/leases/*(只读,无写权要求)。 +set -euo pipefail + +origin_repo() { # 从 origin 远端推断 owner/name;非 git 仓则空 + git remote get-url origin 2>/dev/null | sed -E 's#.*github\.com[:/]##; s#\.git$##' || true +} + +ORG="Cloudbird-Software" # 本 CLI 是组织内部入口(org 段固定,仓段可省略) +LEAS_REPO="$ORG/arbiter" # 租约宿主仓(arbiter,ADR-0054) + +norm_repo() { # 'repo' | 'owner/repo' → 'owner/repo'(org 段缺省补全) + local r="${1:?}" + [[ "$r" == */* ]] || r="$ORG/$r" + printf '%s' "$r" +} + +CMD="${1:-}" +case "$CMD" in + next) + REPO_ARG="${2:-$(origin_repo)}" + [[ -n "$REPO_ARG" ]] || { echo "错误:不在 git 仓内且未指定仓(用法: ghcb next [repo])" >&2; exit 2; } + gh issue list --repo "$REPO_ARG" --label state:ready --state open --json number,title --jq '.[] | "#\(.number) \(.title)"' || { echo "查询失败:检查 gh 凭据(gh auth login)与仓权限" >&2; exit 2; } + ;; + claim) + # 参数序 [repo](首版误把 $1 当 repo,PR#157 修正) + N="${2:?用法: ghcb claim [repo]}" + REPO_ARG="${3:-$(origin_repo)}" + [[ -n "$REPO_ARG" ]] || { echo "错误:不在 git 仓内且未指定仓" >&2; exit 2; } + # MSYS 路径转换豁免:Git Bash 会把以 / 开头的参数改写成 Windows 路径 + # (实测 "/claim"→"D:/development/Git/claim",conductor 白名单不认); + # 非 MSYS 环境该 env 为无害空设 + MSYS2_ARG_CONV_EXCL='/claim' MSYS_NO_PATHCONV=1 gh issue comment "$N" --repo "$REPO_ARG" --body "/claim" >/dev/null + echo "已评论 /claim(#$N @$REPO_ARG)——conductor 转介 arbiter CAS 裁决并置 state:in-progress;确认:ghcb status $N" + ;; + release) + # 释放租约(ADR-0055):/release 评论由 conductor 转介 arbiter——非 holder/无租约=deny + N="${2:?用法: ghcb release [repo]}" + REPO_ARG="${3:-$(origin_repo)}" + [[ -n "$REPO_ARG" ]] || { echo "错误:不在 git 仓内且未指定仓" >&2; exit 2; } + MSYS2_ARG_CONV_EXCL='/release' MSYS_NO_PATHCONV=1 gh issue comment "$N" --repo "$REPO_ARG" --body "/release" >/dev/null + echo "已评论 /release(#$N @$REPO_ARG)——conductor 转介 arbiter 释放租约(仅 holder/owner);确认:ghcb status $N" + ;; + status) + # 只读:标签态(宪法 §12 真相源)+ 租约视图(arbiter refs/leases/____) + N="${2:?用法: ghcb status [repo]}" + REPO_ARG="${3:-$(origin_repo)}" + [[ -n "$REPO_ARG" ]] || { echo "错误:不在 git 仓内且未指定仓" >&2; exit 2; } + FULL="$(norm_repo "$REPO_ARG")"; NAME="${FULL##*/}" + gh issue view "$N" --repo "$FULL" --json number,title,state,labels,assignees \ + --jq '"#\(.number) \(.title)\n 状态: \(.state) | 标签: \([.labels[].name] | join(", ")) | assignee: \([.assignees[].login] | join(", "))"' \ + || { echo "查询失败:检查 gh 凭据与仓权限" >&2; exit 2; } + REF="refs/leases/${ORG}__${NAME}__${N}" + if SHA=$(gh api "repos/$LEAS_REPO/git/ref/$REF" --jq '.object.sha' 2>/dev/null); then + MSG=$(gh api "repos/$LEAS_REPO/git/commits/$SHA" --jq '.message' 2>/dev/null || true) + HOLDER=$(grep -o '"holder": *"[^"]*"' <<<"$MSG" | head -1 | sed 's/.*"holder": *"//; s/"$//' || true) + EXP=$(grep -o '"expires_at": *"[^"]*"' <<<"$MSG" | head -1 | sed 's/.*"expires_at": *"//; s/"$//' || true) + echo " 租约: holder=${HOLDER:-?} expires_at=${EXP:-?}(ref $REF)" + else + echo " 租约: 无活跃租约(ref $REF 不存在)" + fi + ;; + card-meta) + # 输出 PR body 必贴的卡元数据行(ADR-0055 决策 1:缺失=后续关卡 exit 3) + N="${2:?用法: ghcb card-meta [repo]}" + REPO_ARG="${3:-$(origin_repo)}" + [[ -n "$REPO_ARG" ]] || { echo "错误:不在 git 仓内且未指定仓" >&2; exit 2; } + echo "Card: $(norm_repo "$REPO_ARG")#$N" + echo "提示:把上面一行原样贴进 PR body(缺失=后续关卡 exit 3)" >&2 + ;; + *) + export REPO="${1:?用法: GH_TOKEN=$(ghcb [--refresh]) | ghcb next [repo] | ghcb claim [repo] | ghcb release [repo] | ghcb status [repo] | ghcb card-meta [repo]}" + shift || true + DIR="$(cd "$(dirname "$0")" && pwd)" + exec bash "$DIR/gh-app-token.sh" "$@" + ;; +esac diff --git a/governance/tests/test-navigation.sh b/governance/tests/test-navigation.sh new file mode 100644 index 0000000..661ad3b --- /dev/null +++ b/governance/tests/test-navigation.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +# test-navigation.sh —— 导航可达性自测(#363 / #362 审计收口) +# +# 治理可达性断裂曾是 #362 审计的核心发现(32 次 PM 模拟运行,置信度 4.8/10): +# 被引用的导航文件不存在、入口面之间互相指空。本测试把「导航不变量」机械化为 +# gate 关卡(断链即红,fail-closed 防回归): +# §A 全入口路由面存在且保留高频困惑锚点(g060 / C1 / drift-check / spec 位置) +# §B AGENTS.md 契约:行数 ≤ 60(治理仓豁免上限)、入口协议块标记完整、索引可达 +# §C PLAYBOOK.md 契约:spec 位置规则、g060 路径、C1 runbook、路由回指 +# §D profile/README.md(org 首页):指向路由表与 REPOS.yaml,不含已退役仓的活跃行 +# §E NAVIGATION.md 内的本仓 markdown 链接全部可解析(断链=红) +# §F Makefile 暴露 drift-check 入口(C1 预检命令可发现性) +# 用法:bash governance/tests/test-navigation.sh(零网络) +set -uo pipefail +HERE="$(cd "$(dirname "$0")" && pwd)" +ROOT="$(cd "$HERE/../.." && pwd)" + +PASS=0; FAIL=0 +pass() { PASS=$((PASS+1)); echo "PASS $1"; } +fail() { FAIL=$((FAIL+1)); echo "FAIL $1"; } + +cd "$ROOT" + +# ---------- §A 全入口路由面 ---------- +NAV="docs/NAVIGATION.md" +if [[ -s "$NAV" ]]; then pass "$NAV 存在且非空"; else fail "$NAV 缺失或为空(#363 复现面)"; fi +for anchor in "g060" "C1" "drift-check" "specs/IR-" "bug.yml" "intent.yml" "ghcb"; do + if grep -q -- "$anchor" "$NAV" 2>/dev/null; then pass "NAVIGATION 锚点[$anchor] 在"; else fail "NAVIGATION 锚点[$anchor] 缺(高频困惑回归)"; fi +done + +# ---------- §B AGENTS.md 契约 ---------- +AG="AGENTS.md" +if [[ -s "$AG" ]]; then pass "$AG 存在"; else fail "$AG 缺失"; fi +LINES=$(wc -l < "$AG" 2>/dev/null || echo 999) +if (( LINES <= 60 )); then pass "AGENTS.md 行数 $LINES ≤ 60(治理仓豁免上限)"; else fail "AGENTS.md 行数 $LINES 超 60 上限"; fi +if grep -q '' "$AG" && grep -q '' "$AG"; then + pass "入口协议块标记完整(drift §17 对账面)" +else + fail "入口协议块标记缺失/不完整" +fi +grep -q "docs/NAVIGATION.md" "$AG" && pass "AGENTS.md 指向全入口路由表" || fail "AGENTS.md 未指向 docs/NAVIGATION.md(陌生 agent 断链)" +# 索引引用的本仓文件必须存在(引用链断裂= #362 P0 根因 1) +for f in governance/GOVERNANCE.yaml governance/REPOS.yaml governance/expected-state.json \ + governance/transitions.yaml governance/providers.yaml governance/policy \ + docs/pm/PLAYBOOK.md docs/NAVIGATION.md .github/workflows/gate.yml scripts/ghcb; do + if [[ -e "$f" ]]; then pass "索引引用存在: $f"; else fail "索引引用断链: $f(#362 根因:文档引用链断裂)"; fi +done + +# ---------- §C PLAYBOOK 契约 ---------- +PB="docs/pm/PLAYBOOK.md" +if [[ -s "$PB" ]]; then pass "$PB 存在且非空"; else fail "$PB 缺失或为空(#362 P0-1 复现面)"; fi +for anchor in "spec 放哪" "g060 会拦你的 suite" "发起治理变更(C1 runbook" "conductor / arbiter" "NAVIGATION.md"; do + if grep -q -- "$anchor" "$PB" 2>/dev/null; then pass "PLAYBOOK 锚点[$anchor] 在"; else fail "PLAYBOOK 锚点[$anchor] 缺(#363 落点回归)"; fi +done + +# ---------- §D org 首页契约 ---------- +PR="profile/README.md" +if [[ -s "$PR" ]]; then pass "$PR 存在"; else fail "$PR 缺失"; fi +grep -q "NAVIGATION.md" "$PR" && pass "profile README 指向全入口路由表" || fail "profile README 未指向 NAVIGATION.md" +grep -q "REPOS.yaml" "$PR" && pass "profile README 指向组织地图真源" || fail "profile README 未指向 REPOS.yaml" +if grep -q "agent-registry" "$PR"; then fail "profile README 仍列已退役仓 agent-registry 为活跃(ADR-0085)"; else pass "profile README 无已退役仓活跃行"; fi + +# ---------- §E NAVIGATION 本仓链接可解析 ---------- +BROKEN=0 +while IFS= read -r link; do + [[ -n "$link" ]] || continue + target="${link%%#*}" + [[ -n "$target" ]] || continue + if [[ ! -e "docs/$target" ]]; then echo " 断链: docs/$target"; BROKEN=1; fi +done < <(grep -oE '\]\(([^)#]+)(#[^)]*)?\)' "$NAV" 2>/dev/null | sed -E 's/^\]\(//; s/\)$//' | grep -vE '^(https?:|#)' || true) +(( BROKEN == 0 )) && pass "NAVIGATION 本仓链接全部可解析" || fail "NAVIGATION 存在断链(见上行)" + +# ---------- §F Makefile 入口可发现性 ---------- +if grep -qE '^drift-check:' Makefile 2>/dev/null; then pass "Makefile 暴露 drift-check 目标(C1 预检可发现)"; else fail "Makefile 缺 drift-check 目标"; fi +if grep -qE '^gates-pr:' Makefile 2>/dev/null; then pass "Makefile 暴露 gates-pr 目标"; else fail "Makefile 缺 gates-pr 目标"; fi + +echo "== test-navigation: pass=$PASS fail=$FAIL ==" +[[ $FAIL -eq 0 ]] diff --git a/profile/README.md b/profile/README.md index 805fef5..85d27ed 100644 --- a/profile/README.md +++ b/profile/README.md @@ -4,6 +4,8 @@ **运行范式(ADR-0085)**:强模型 = 项目经理(PM),公司只规定四道阶段门禁——IR→spec → spec 过红队→开卡 → 卡完成+全 gate 绿 → IR 全面验收;门禁之间怎么干由 PM 自主。常规实现默认派 CNB 免费算力池,PM 处理上升问题。PM 入口:[.github 仓 AGENTS.md](https://github.com/Cloudbird-Software/.github/blob/main/AGENTS.md) + [PM Playbook](https://github.com/Cloudbird-Software/.github/blob/main/docs/pm/PLAYBOOK.md)。每次 run 的经验沉淀在 [archive/runs](https://github.com/Cloudbird-Software/archive/tree/main/runs)(周度 digest 消费)。 +**任何入口进来先读**:[全入口路由表 NAVIGATION.md](https://github.com/Cloudbird-Software/.github/blob/main/docs/NAVIGATION.md)——入口矩阵 + 高频困惑 FAQ(spec 放哪 / 治理变更要不要开卡 / g060 拦截怎么办等,#362 审计收口)。 + ## 我们的工程制度 - 每个仓库唯一的必需 check 是 **`gate`**:聚合 hygiene(密钥/大文件扫描)+ `make check`(lint + test)+ 依赖审查。 @@ -14,16 +16,20 @@ ## 仓库 -组织地图(结构层导航的唯一真源):[governance/REPOS.yaml](../governance/REPOS.yaml) +组织地图(结构层导航的唯一真源):[governance/REPOS.yaml](https://github.com/Cloudbird-Software/.github/blob/main/governance/REPOS.yaml) | 仓库 | 层 | 用途 | |---|---|---| -| [.github](https://github.com/Cloudbird-Software/.github) | L0 | 治理总仓:GOVERNANCE / 标准 schema / 漂移检测 | -| [CI-Workflows](https://github.com/Cloudbird-Software/CI-Workflows) | L0 | 可复用工作流(唯一真相源) | -| [agent-registry](https://github.com/Cloudbird-Software/agent-registry) | L1 | agent/skill/tool/team 声明 + 模型注册表 + ADR(私有) | -| [template-service](https://github.com/Cloudbird-Software/template-service) | L2 | 新项目模板 | -| [AI_Web_School](https://github.com/Cloudbird-Software/AI_Web_School) | L2 | 产品仓库 | - -## 自治流水线(W0) - -组织已具备意图→规格的无人值守链路:用意图表单提交 IR,owner 打 `state:ir-signed` 后,conductor(状态机)自动调用 spec-author 起草条款级规格并开出 spec PR,全程计量落盘。规格与波次计划见 [`specs/IR-0001/`](../specs/IR-0001/)。 +| [.github](https://github.com/Cloudbird-Software/.github) | L0 | 治理总仓:GOVERNANCE / 标准 schema / 漂移检测 / 全入口路由 | +| [CI-Workflows](https://github.com/Cloudbird-Software/CI-Workflows) | L0 | 可复用工作流(唯一真相源)+ bug 复现/红队执行层 | +| [archive](https://github.com/Cloudbird-Software/archive) | L1 | 记忆层:ADR 家园(正本+INDEX)+ PM 运行报告 runs/ + 退役快照 | +| [holdout](https://github.com/Cloudbird-Software/holdout) | L1 | 试卷层:封存验收场景(owner 直管,agent 只读) | +| [template-service](https://github.com/Cloudbird-Software/template-service) | L2 | 新项目模板(入口协议块下发真源) | +| [arbiter](https://github.com/Cloudbird-Software/arbiter) | L2 | 仲裁内核:确定性裁决 / CAS 租约(无 LLM) | +| [cnb-bridge](https://github.com/Cloudbird-Software/cnb-bridge) | L2 | CNB 免费算力桥接(可删除层,ADR-0085) | +| 产品仓(AI_Web_School / mutual / QW_Arena1 / Shorts_Director / Script_Writer / Use-up-Plan) | L2 | 业务实现——全量与状态见 REPOS.yaml | + +## 意图→交付链路 + +- **Feature 流(签署前置)**:[intent 表单](https://github.com/Cloudbird-Software/.github/issues/new?template=intent.yml)提交 IR → owner 签署 → spec(PM 自著或 spec-author 快速通道)→ 红队审计 → 开卡 → 实现(CNB 默认)→ 验收。规格与波次计划见 [`specs/`](https://github.com/Cloudbird-Software/.github/tree/main/specs)。 +- **Bug 流(复现前置,签署点后移——ADR-0064)**:[bug 表单](https://github.com/Cloudbird-Software/.github/issues/new?template=bug.yml)提交即机器复现,三值判定(reproduced / cannot-reproduce / inconclusive),reproduced 后修复合入自动回写状态。