diff --git a/.github/workflows/cnb-audit.yml b/.github/workflows/cnb-audit.yml new file mode 100644 index 0000000..48522f7 --- /dev/null +++ b/.github/workflows/cnb-audit.yml @@ -0,0 +1,204 @@ +name: cnb-audit +# ============================================================================= +# CNB 周审计 + 三接缝隔离审计(IR-0004 AC-14/AC-19,BEH-17 周审计项之一) +# +# 硬注释(改动前必读): +# - 本工作流是治理仓 CNB 三接缝之一(GOVERNANCE EX-1 声明条目 / IFACE-01, +# ADR-0085 决策 6 / IR-0004):与 cnb-dispatch.yml、GOVERNANCE 声明条目 +# 共同构成治理仓对 CNB 的全部合法引用面。 +# - 判定永不外置(INV-02):本审计只核资源面(配额/窗口/隔离),不消费 +# NPC 输出作为任何 gate 输入;沙箱自报数字不采信(INV-01)。 +# - 凭据纪律(INV-06):token 只经 org secret 注入,不落日志/摘要/issue; +# 配额数字与窗口状态不含敏感值,可进审计面。 +# - 无人值守护栏适用(ADR-0040):本审计产出即护栏输入——配额告警开 +# cost 类 issue(cost-infra),供派单前置检查消费。 +# - ADR-0085 决策 6 审计口径:第③步 grep 匹配**操作性引用** +# (endpoint=cnb.cool / token 名=CNB_TOKEN / 派单协议=@CodeBuddy), +# 目录导航性提及(如仓库名 cnb-bridge、工作流名)不计入——故 pattern +# 刻意只列三样;排除清单=接缝本体+既有声明/登记面(逐项依据见第③步注释)。 +# - fail-closed(INV-04):配额查询失败、账号清单不可读、隔离 grep 非 0 +# 一律红;窗口停摆仅 WARN(观测面非判定面)。 +# ============================================================================= +on: + schedule: + - cron: "41 3 * * 1" # 周一 03:41 UTC 错峰(避 archive gate 周扫 04:23 / runs-digest 04:37 / Scorecard 05:00+,ADR-0011 谱系) + workflow_dispatch: {} + +permissions: + contents: read + +concurrency: + group: cnb-audit + cancel-in-progress: false + +jobs: + audit: + runs-on: ubuntu-latest + timeout-minutes: 15 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1(org 白名单 CI-2) + with: + persist-credentials: false + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: Cloudbird-Software/cnb-bridge # L2 可删除层(ADR-0085 决策 6) + ref: main + path: cnb-bridge + persist-credentials: false + # ------------------------------------------------------------------ + # ①配额活性:逐账号查余量;任一低于 policy 阈值(cnb.quota_alert_pct,现值 20) + # → LOW=1(下一步开 cost 类 issue);配额查询失败(含 org secret 缺失) + # = fail-closed 红(INV-04/AC-15)。 + # secret 经 env 映射静态引用(GH Actions 无法动态索引 secrets); + # 加账号 = owner 加 org secret + 本映射加一行(C1 PR)。 + # ------------------------------------------------------------------ + - name: quota(配额活性,fail-closed) + env: + CNB_TOKEN_XUEMEI: ${{ secrets.CNB_TOKEN_XUEMEI }} + CNB_TOKEN_P11: ${{ secrets.CNB_TOKEN_P11 }} + run: | + set -euo pipefail + # 账号清单真源 = cnb-bridge/accounts.yaml(无明文 token,ADR-0085 决策 6) + mapfile -t ACCOUNTS < <(python3 - <<'EOF' + import sys, yaml + with open('cnb-bridge/accounts.yaml', encoding='utf-8') as f: + doc = yaml.safe_load(f) + aliases = [a['alias'] for a in (doc.get('accounts') or [])] + if not aliases: + print('::error::accounts.yaml 无账号条目——清单不可读即红(fail-closed)', file=sys.stderr) + sys.exit(1) + print('\n'.join(aliases)) + EOF + ) + # 进程替换不传递 python 退出码——空清单兜底再拦一次(fail-closed,堵 fail-open) + [[ ${#ACCOUNTS[@]} -gt 0 ]] || { echo '::error::账号清单为空/不可读——fail-closed 红'; exit 1; } + FAIL=0 + : > quota-report.txt + for A in "${ACCOUNTS[@]}"; do + VAR="CNB_TOKEN_$(printf '%s' "$A" | tr '[:lower:]' '[:upper:]')" + TOKEN="${!VAR:-}" + if [[ -z "$TOKEN" ]]; then + echo "::error::org secret ${VAR} 缺失(账号 ${A} 无法查配额)——fail-closed 红" + FAIL=1; continue + fi + # token 已按池契约注入 step env(CNB_TOKEN_,cnb_pool.py 直接读 + # os.environ[secret_ref])——不做二次中转,零冗余变量(INV-06) + if ! python3 cnb-bridge/cnb_pool.py quota --account "$A" | tee -a quota-report.txt; then + echo "::error::账号 ${A} 配额查询失败——fail-closed 红(INV-04:平台失效必须可见)" + FAIL=1 + fi + done + [[ $FAIL -eq 0 ]] || exit 1 + # 告警阈值真源 = automation-limits.yaml cnb.quota_alert_pct(当前 20,IR-0004 AC-15); + # cnb 节缺失即红(policy 为准,不内置缺省) + ALERT_PCT=$(python3 - <<'EOF' + import sys, yaml + with open('governance/policy/automation-limits.yaml', encoding='utf-8') as f: + cnb = (yaml.safe_load(f) or {}).get('cnb') + if not isinstance(cnb, dict): + print('::error::automation-limits.yaml 缺 cnb 节——阈值真源不可少(fail-closed)', file=sys.stderr) + sys.exit(1) + print(int(cnb.get('quota_alert_pct', 20))) + EOF + ) || exit 1 + # 余量 <阈值 → LOW=1;quota 行契约:account= remaining_pct= + grep -E 'remaining_pct=[0-9]+' quota-report.txt | awk -F'remaining_pct=' -v pct="$ALERT_PCT" '{split($2,a," "); if (a[1]+0 < pct) print}' > quota-low.txt || true + LOW=0; [[ -s quota-low.txt ]] && LOW=1 + echo "LOW=$LOW" >> "$GITHUB_ENV" + { + echo '## CNB 周审计(配额活性)' + echo + echo '```'; cat quota-report.txt; echo '```' + echo + [[ $LOW -eq 1 ]] && echo '**告警**:以下账号余量 <20%——已开 cost 类 issue:' && sed 's/^/- /' quota-low.txt || echo '全部账号余量 ≥20%' + } >> "$GITHUB_STEP_SUMMARY" + echo "AUDIT | cnb-audit | quota | accounts=${#ACCOUNTS[@]} | low=$LOW" + # ------------------------------------------------------------------ + # 开 cost 类 issue(.github 仓,cloudbrid-agent App 令牌——archive 仓 + # runs-digest.yml 同款铸币模式:CB_APP_ID/AGENT_APP_SECRET org secrets, + # REPO=.github;本仓即 .github,脚本取自本 checkout,无需二次 sparse)。 + # 开 issue 失败 = 红(消费链停摆必须可见,fail-closed)。 + # ------------------------------------------------------------------ + - name: open-cost-issue(余量 <20% 时) + if: env.LOW == '1' + env: + CB_APP_ID: ${{ secrets.CB_APP_ID }} + AGENT_APP_SECRET: ${{ secrets.AGENT_APP_SECRET }} + run: | + set -euo pipefail + TOKEN=$(REPO=".github" CB_APP_ID="$CB_APP_ID" AGENT_APP_SECRET="$AGENT_APP_SECRET" \ + bash scripts/gh-app-token.sh) + { + echo '## CNB 配额告警(cnb-audit 周审计自动开出,IR-0004 AC-15)' + echo + echo '以下账号余量 <20%(处置前 cnb-dispatch 对该账号降频/换账号):' + echo + sed 's/^/- /' quota-low.txt + echo + echo '---' + echo '来源:.github 仓 cnb-audit 周一 03:41 UTC run(本 issue 存在期间,' + echo 'AGENTS.md 无人值守护栏 (d) 视为存在未决 cost issue——派单前置检查会拦)。' + } > cost-body.md + GH_TOKEN="$TOKEN" gh issue create -R Cloudbird-Software/.github \ + -t 'CNB 配额告警:账号余量 <20%(周审计)' -b "$(cat cost-body.md)" -l cost-infra \ + || { echo '::error::cost issue 创建失败——审计消费链停摆,fail-closed'; exit 1; } + # ------------------------------------------------------------------ + # ②窗口停扰扫描:占用中窗口数与 build logs 交叉(IFACE-05 对账真源=平台 + # build logs,交叉在 cnb_pool.py windows 内完成,本步只消费产出); + # 占用率 ≥阈值(policy cnb.window_warn_pct,缺省 80)→ 输出 WARN 行 + # (观测面非判定面,不红)。脚本失败本身=红(INV-04)。 + # ------------------------------------------------------------------ + - name: windows(停摆扫描,WARN 口径) + run: | + set -euo pipefail + python3 cnb-bridge/cnb_pool.py windows | tee windows.out + TOTAL=$(grep -cE '^window=[0-9]+ status=' windows.out || true) + OCCUPIED=$(grep -cE '^window=[0-9]+ status=occupied' windows.out || true) + WARN_PCT=$(python3 - <<'EOF' + import yaml + try: + with open('governance/policy/automation-limits.yaml', encoding='utf-8') as f: + cnb = (yaml.safe_load(f) or {}).get('cnb') or {} + print(int(cnb.get('window_warn_pct', 80))) + except Exception: + print(80) # 巡检旋钮(warn 不红,非判定面);policy 可加 window_warn_pct 覆盖 + EOF + ) + if [[ "$TOTAL" -gt 0 && $(( OCCUPIED * 100 / TOTAL )) -ge "$WARN_PCT" ]]; then + echo "::warn::WARN | cnb-audit | windows | occupied=${OCCUPIED}/${TOTAL}(≥${WARN_PCT}%,池近饱和——派单侧降频或等回收)" + echo "WARN | cnb-audit | windows | occupied=${OCCUPIED}/${TOTAL} >= ${WARN_PCT}% 池近饱和" >> "$GITHUB_STEP_SUMMARY" + else + echo "AUDIT | cnb-audit | windows | occupied=${OCCUPIED}/${TOTAL}(阈值 ${WARN_PCT}%)" + fi + # ------------------------------------------------------------------ + # ③三接缝隔离 grep(IFACE-01,ADR-0085 决策 6 审计口径): + # 治理仓内操作性引用(cnb.cool / CNB_TOKEN / @CodeBuddy)只允许出现在 + # 接缝本体与既有声明/登记面;命中数必须为 0,非 0 即红并列出命中文件。 + # 排除清单(逐项依据,ADR-0085 决策 6"目录导航性/声明性提及不计入"): + # - governance/GOVERNANCE.yaml EX-1 声明条目本体(三接缝之一) + # - .github/workflows/cnb-dispatch.yml 接缝之二(派单经纪人) + # - .github/workflows/cnb-audit.yml 接缝之三(本审计) + # - governance/expected-state.json secret/工作流登记表(声明面:登记名非使用点) + # - governance/policy/automation-limits.yaml cnb 配置节(EX-1 政策面:加账号=改配置零代码) + # - governance/providers.yaml 工具目录(声明面:secret_ref 登记名非值/非使用点,ADR-0085 决策 7) + # - specs/IR-0004/spec.md 制度文本(DECISION-06 对 token 形态的规范性条款) + # 新增排除项必须走 ADR(C1)——本清单是被审计对象的一部分。 + # ------------------------------------------------------------------ + - name: isolation-grep(三接缝隔离,命中非 0 即红) + run: | + set -euo pipefail + HITS=$(git grep -nE 'cnb\.cool|CNB_TOKEN|@CodeBuddy' -- \ + ':!governance/GOVERNANCE.yaml' \ + ':!.github/workflows/cnb-dispatch.yml' \ + ':!.github/workflows/cnb-audit.yml' \ + ':!governance/expected-state.json' \ + ':!governance/policy/automation-limits.yaml' \ + ':!governance/providers.yaml' \ + ':!specs/IR-0004/spec.md' || true) + if [[ -n "$HITS" ]]; then + echo "$HITS" + echo "::error::操作性引用越界(IFACE-01 接缝与声明面之外,命中 $(echo "$HITS" | grep -c '' ) 行)——上列文件须收敛引用或走 ADR 修订接缝定义" + exit 1 + fi + echo 'AUDIT | cnb-audit | isolation | grep hits=0(接缝与声明面外零操作性引用)' >> "$GITHUB_STEP_SUMMARY" + echo 'AUDIT | cnb-audit | isolation | grep hits=0' diff --git a/.github/workflows/cnb-dispatch.yml b/.github/workflows/cnb-dispatch.yml new file mode 100644 index 0000000..5a72d52 --- /dev/null +++ b/.github/workflows/cnb-dispatch.yml @@ -0,0 +1,177 @@ +name: cnb-dispatch +# ============================================================================= +# CNB 派单经纪人(PM 默认实现主力入口,ADR-0085 裁决 2 / 决策 6;IR-0004 AC-14) +# +# 硬注释(改动前必读): +# - 本工作流是治理仓 CNB 三接缝之一(GOVERNANCE EX-1 声明条目 / IFACE-01, +# ADR-0085 决策 6 / IR-0004):治理仓对 CNB 的操作性引用收敛于 +# GOVERNANCE.yaml + org secrets + cnb-dispatch/cnb-audit 两工作流; +# cnb-audit 第③步隔离 grep 在本文件之外的命中即红。 +# - 判定永不外置(INV-02):本经纪人只派发生成侧任务,NPC 产物进任何判定链 +# 前必经机械核对(INV-03:基准 SHA/差异可应用/格式校验)——沙箱自报数字不采信。 +# - 任务文本禁止包含任何凭据(INV-06):token 只经 org secret 注入本经纪人, +# 永不进入 PM 上下文/任务文本/沙箱环境(DECISION-01 缓解条款)。 +# - 无人值守护栏适用(ADR-0040):派单前查 org 变量 AUTO_MERGE_DISABLED, +# 置位即停(fail-closed,禁止任何绕过尝试)。 +# +# 异步派单:不等待 NPC 回复;产物回收走 cnb-bridge `cnb_pool.py collect` +# 或 cnb-audit 周审计。actions 只用 org 白名单内的 checkout(CI-2), +# 其余逻辑一律 python3/bash——不引入新第三方 Action。 +# ============================================================================= +on: + workflow_dispatch: + inputs: + task: + description: '任务文本(机器可读,IFACE-02:必含清单 ID;std/heavy 须带 tier-reason:/tier-adr: 行)' + required: true + type: string + card: + description: 关联卡 issue 编号(选填,写进派单回执审计面) + required: false + type: string + default: '' + tier: + description: '核时档位:light=1C(默认)/ std=2C / heavy=4C;8C 禁用(BUDGET-01)' + required: false + type: string + default: light + account: + description: 'CNB 池账号别名:xuemei(默认)/ p11' + required: false + type: string + default: xuemei + repository_dispatch: + types: [cnb-dispatch] + +permissions: + contents: read + +concurrency: + group: cnb-dispatch + cancel-in-progress: false # 派单不互相挤占——排队而非取消(审计面完整) + +jobs: + dispatch: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1(org 白名单 CI-2) + with: + persist-credentials: false + # ------------------------------------------------------------------ + # 步骤 1:归一化双入口入参 + 派单前置检查(ADR-0040 护栏 / INV-06 凭据红线) + # ------------------------------------------------------------------ + - name: preflight(入参归一化 + AUTO_MERGE_DISABLED + 凭据嗅探) + env: + I_TASK: '${{ inputs.task || github.event.client_payload.task }}' + I_CARD: '${{ inputs.card || github.event.client_payload.card }}' + I_TIER: '${{ inputs.tier || github.event.client_payload.tier }}' + I_ACCOUNT: '${{ inputs.account || github.event.client_payload.account }}' + # org Actions 变量(visibility=all,全仓可读,automation-limits.yaml circuit_breaker 节) + AUTO_MERGE_DISABLED: '${{ vars.AUTO_MERGE_DISABLED }}' + run: | + set -euo pipefail + [[ -n "${I_TASK:-}" ]] || { echo '::error::task 缺失(必填)——拒发'; exit 1; } + : "${I_TIER:=light}" + : "${I_ACCOUNT:=xuemei}" + : "${I_CARD:=}" + # ADR-0040:熔断标志置位即停一切派发(空=未置位;fail-closed,禁绕过) + if [[ "${AUTO_MERGE_DISABLED,,}" =~ ^(true|1|yes)$ ]]; then + echo '::error::AUTO_MERGE_DISABLED 已置位——成本熔断中,停止一切 CNB 派单(ADR-0040)' + exit 1 + fi + # INV-06:任务文本禁含凭据字样——机械预检,命中即红(拒发即止损) + if grep -qiE 'CNB_TOKEN|ghp_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,}|-----BEGIN [A-Z ]*PRIVATE KEY' <<<"$I_TASK"; then + echo '::error::任务文本疑似包含凭据(INV-06)——拒发;重写任务文本,凭据只走 org secret' + exit 1 + fi + # 多行 TASK 用 heredoc 定界符写入环境面(GITHUB_ENV 多行语法) + { + printf 'TASK<> "$GITHUB_ENV" + # ------------------------------------------------------------------ + # 步骤 2:档位校验——真源 governance/policy/automation-limits.yaml cnb 节 + # (已落地:tiers {light:1, std:2, heavy:4} / default_tier: light,IR-0004 AC-15; + # cnb 节缺失即红:fail-closed,不内置缺省档位——同 test_integrity + # “本节拉取失败 = gate 红”先例,BUDGET-01) + # ------------------------------------------------------------------ + - name: 档位校验(policy 为准,fail-closed) + run: | + python3 - <<'EOF' + import os, re, sys, yaml + with open('governance/policy/automation-limits.yaml', encoding='utf-8') as f: + policy = yaml.safe_load(f) + cnb = policy.get('cnb') + if not isinstance(cnb, dict) or not cnb.get('tiers'): + print('::error::automation-limits.yaml 缺 cnb.tiers 节——IR-0004 D 组落地前 fail-closed,' + '不得内置缺省档位(policy 为准原则)') + sys.exit(1) + tiers = cnb['tiers'] # 期望形如 {light: 1, std: 2, heavy: 4} + tier = os.environ['TIER'] + if tier not in tiers: + print(f'::error::未知/禁用档位 tier={tier};允许集={sorted(tiers)}(8C 禁用,BUDGET-01)') + sys.exit(1) + task = os.environ['TASK'] + if tier == 'std' and not re.search(r'tier-reason:\s*\S.{4,}', task): + print('::error::std=2C 须在任务文本带 tier-reason: <一句话理由>(BUDGET-01)') + sys.exit(1) + if tier == 'heavy' and not re.search(r'tier-adr:\s*ADR-\d{4}', task): + print('::error::heavy=4C 须在任务文本带 tier-adr: ADR-NNNN(BUDGET-01)') + sys.exit(1) + cpus = tiers[tier] + if int(cpus) >= 8: + print(f'::error::档位 {tier}={cpus}C 达禁用线(8C 禁用,BUDGET-01)') + sys.exit(1) + with open(os.environ['GITHUB_ENV'], 'a', encoding='utf-8') as f: + f.write(f'CPUS={cpus}\n') + print(f'tier={tier} cpus={cpus}C(policy cnb.tiers 校验通过)') + EOF + # ------------------------------------------------------------------ + # 步骤 3:checkout cnb-bridge(L2 可删除层,ADR-0085 决策 6;main) + # ------------------------------------------------------------------ + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: Cloudbird-Software/cnb-bridge + ref: main + path: cnb-bridge + persist-credentials: false + # ------------------------------------------------------------------ + # 步骤 4:选定账号令牌 + 派单。 + # secret 引用必须在表达式中静态出现(GH Actions 无法动态索引 secrets)—— + # 故用 env 映射落地 "account=xuemei→CNB_TOKEN_XUEMEI / p11→CNB_TOKEN_P11"; + # 两个 secret 均按 org 侧 required: false 声明(缺失不阻断表达式求值), + # 运行时缺失即 fail-closed 红。加账号 = owner 加 org secret + 本映射加一行(C1 PR)。 + # 令牌只在本步进程内 export,不落 GITHUB_ENV/日志/摘要。 + # ------------------------------------------------------------------ + - name: dispatch(token 仅本步内存面) + env: + CNB_TOKEN_XUEMEI: ${{ secrets.CNB_TOKEN_XUEMEI }} + CNB_TOKEN_P11: ${{ secrets.CNB_TOKEN_P11 }} + run: | + set -euo pipefail + case "$ACCOUNT" in + xuemei) export CNB_TOKEN="${CNB_TOKEN_XUEMEI:-}" ;; + p11) export CNB_TOKEN="${CNB_TOKEN_P11:-}" ;; + *) echo "::error::未知账号 $ACCOUNT(池内:xuemei/p11;新增=owner 加 org secret + 本 env 映射一行,C1)"; exit 1 ;; + esac + [[ -n "${CNB_TOKEN:-}" ]] || { echo "::error::org secret CNB_TOKEN_${ACCOUNT^^} 缺失——fail-closed 红(令牌通道唯一=org secret,INV-06)"; exit 1; } + # [run:] 锚由 cnb_pool.py 在任务文本内附加(IFACE-02:run-id 前缀落 payload.run_id); + # CLI 契约=位置参数 body + --account/--tier(档位→核数映射与 build logs 对账在 cnb-audit 侧) + set -- python3 cnb-bridge/cnb_pool.py dispatch "$TASK" --account "$ACCOUNT" --tier "$TIER" + "$@" | tee dispatch.out + # 审计面:窗口/账号/run_id 回执同步落 run 日志与 step summary(异步派单不等待 NPC) + RUN_ID=$(grep -oE 'run_id=[^ ]+' dispatch.out | head -1 || true) + WINDOW=$(grep -oE 'window=[^ ]+' dispatch.out | head -1 || true) + { + echo '## CNB 派单回执(异步——回收走 collect / cnb-audit 周审计)' + echo + echo "- account=$ACCOUNT tier=$TIER cpus=$CPUS card=${CARD:-(无)} ${WINDOW} ${RUN_ID}" + echo + echo '```' + sed -n '1,60p' dispatch.out + echo '```' + } >> "$GITHUB_STEP_SUMMARY" + echo "AUDIT | cnb-dispatch | account=$ACCOUNT | tier=$TIER | cpus=$CPUS | card=${CARD:-none} | ${WINDOW:-window=n/a} | ${RUN_ID:-run_id=n/a}" diff --git a/.github/workflows/conductor.yml b/.github/workflows/conductor.yml index d08ac83..a41ee7d 100644 --- a/.github/workflows/conductor.yml +++ b/.github/workflows/conductor.yml @@ -395,6 +395,56 @@ jobs: api(E["APP_TOKEN"], f"/repos/{REPO}/issues/{ISSUE}/labels/state%3Awave-planned", "DELETE") raise SystemExit(0) + # ---- ADR-0085:T8 卡完成谓词(存在绑定本卡且已合并的 PR)---- + def check_merged_pr(issue_number): + """卡完成谓词:存在 body 含 Card: # 绑定行且已合并的 PR。 + gate 绿由 PR required checks 承担(合并事实即全 gate 绿的载体); + 不信任标签事件载荷——API 重查(fail-closed:读取失败=拒绝)。""" + target = f"{REPO}#{issue_number}" + st, prs = api(E["APP_TOKEN"], f"/repos/{REPO}/pulls?state=closed&per_page=100") + if st != 200: + return False, f"PR 清单读取失败 HTTP {st}(fail-closed)" + for pr in prs or []: + if pr.get("merged_at") and target in (pr.get("body") or ""): + return True, f"PR #{pr['number']} 已合并({pr['merged_at']})绑定 {target}" + return False, f"无绑定 {target} 且已合并的 PR(近 100 个 closed PR 内)" + + if t["id"] == "T8": + okp, reason = check_merged_pr(ISSUE) + audit(f"T8 合并 PR 谓词: ok={okp} reason={reason}") + if not okp: + audit(f"verdict=DENIED-no-merged-pr T8 拒绝——{reason}") + api(E["APP_TOKEN"], f"/repos/{REPO}/issues/{ISSUE}/labels/state%3Adone", "DELETE") + raise SystemExit(0) + + # ---- ADR-0085:T9 IR 验收谓词(全部子卡收口 + 验收报告存在)---- + def check_ir_acceptance(issue_number, title): + """IR 验收谓词(Gate 4):①无 open 子卡引用本 IR(子卡=body 含 + "父意图: #";全部收口=done+closed)②specs//acceptance.md + 存在于本仓工作区(=事件时点默认分支)。子卡曾存在性由验收报告自证 + (报告须列子卡清单与逐 AC 证据)。""" + st, cards = api(E["APP_TOKEN"], f"/repos/{REPO}/issues?state=open&per_page=100&labels=type%3Acard") + if st != 200: + return False, f"子卡清单读取失败 HTTP {st}(fail-closed)" + pending = [c["number"] for c in cards or [] + if f"父意图: #{issue_number}" in (c.get("body") or "")] + if pending: + return False, f"尚有 open 子卡未收口: {pending}" + m = re.search(r"(IR-\d+)", title or "") + task_id = m.group(1) if m else f"ISSUE-{issue_number}" + acc = os.path.join(os.getcwd(), "specs", task_id, "acceptance.md") + if not os.path.isfile(acc): + return False, f"验收报告缺失: specs/{task_id}/acceptance.md" + return True, f"全部子卡收口 + 验收报告存在({task_id})" + + if t["id"] == "T9": + okp, reason = check_ir_acceptance(ISSUE, iss.get("title") or "") + audit(f"T9 IR 验收谓词: ok={okp} reason={reason}") + if not okp: + audit(f"verdict=DENIED-acceptance-incomplete T9 拒绝——{reason}") + api(E["APP_TOKEN"], f"/repos/{REPO}/issues/{ISSUE}/labels/state%3Adone", "DELETE") + raise SystemExit(0) + # ---- 执行转移(状态标签写=App 身份,INV-02;写失败=fail-closed, # /claim 已建租约时先补偿回滚——杜绝"租约在、卡未变"的不一致面,ADR-0055)---- class WriteFail(Exception): diff --git a/.github/workflows/gate.yml b/.github/workflows/gate.yml index d54dd4f..70ee9e4 100644 --- a/.github/workflows/gate.yml +++ b/.github/workflows/gate.yml @@ -111,7 +111,7 @@ jobs: if: always() permissions: contents: read # job 级最小权限(评审项:防后续新增 job 继承 workflow 级权限); - # 兼读公开仓 agent-registry/decisions(adr-required 存在性校验) + # 兼读公开仓 archive/adr(adr-required 存在性校验,ADR-0085 家园单仓化) pull-requests: read # 仅 adr-required 步骤读取 PR 文件清单 steps: - name: hygiene green? @@ -202,9 +202,9 @@ jobs: # adr-required(红队 #17-B/D、#18-P0 部分;对齐 agent-registry ADR-0013 同名 check): # C1 路径(governance/ standards/ scripts/ .github/ CODEOWNERS)变更的 PR, # title/body 必须引用 ADR-NNNN——GOVERNANCE flows.governance_change "无 ADR 不合并" - # 的机器执行。本仓不落盘 ADR(L1 决策记录在 agent-registry/decisions/, + # 的机器执行。本仓不落盘 ADR(正本+索引在 archive/adr/(ADR-0085 家园单仓化), # 见 REPOS.yaml role)。存在性校验(ADR-0021 升级):ADR-0020 全仓公开后, - # GITHUB_TOKEN 可读公开仓 contents——被引 ADR 必须真实存在于 agent-registry/decisions/ + # GITHUB_TOKEN 可读公开仓 contents——被引 ADR 必须真实存在于 archive/adr/ #(此前只能等 drift-check §10 后验,伪造引用有 24h 窗口;现在 PR 时点即拦)。 # 实体性(防空壳)仍由 drift-check §10 后验;owner-only review 仍是权威人类门禁。 - name: adr-required(C1 变更须引用 ADR) @@ -255,7 +255,7 @@ jobs: exit 1 fi # 存在性校验(ADR-0021)+ 索引世界(ADR-0053/W1-C1 .github#164)双世界兼容: - # 先试拉 agent-registry decisions/INDEX.yaml(墓碑索引)—— + # 先试拉 archive/adr/INDEX.yaml(墓碑索引,ADR-0085 迁入)—— # 200 → 索引世界(迁移后):decisions/ 只留同名墓碑(文件名保留正是为了 # 让本步骤的存在性清单与 org-gate v1.4.2 按文件名校验零改动兼容), # 正本在 archive 仓 adr/。被引 ADR-NNNN 必须在 INDEX entries 且 @@ -265,7 +265,7 @@ jobs: # 404 → 旧世界(迁移未发生/已回滚):现有 decisions/ 清单逻辑原样。 # 其他 → fail-closed(检测器失明不得伪装通过)。 # 两世界共通不变量:引用不存在的编号一律 fail(防幽灵 ADR 语义不回归,#164 AC-2)。 - ADR_INDEX_JSON=$(gh api "repos/Cloudbird-Software/agent-registry/contents/decisions/INDEX.yaml" 2>&1 >/tmp/adr_index.json; echo "rc=$?") + ADR_INDEX_JSON=$(gh api "repos/Cloudbird-Software/archive/contents/adr/INDEX.yaml" 2>&1 >/tmp/adr_index.json; echo "rc=$?") if jq -e '.content' /tmp/adr_index.json >/dev/null 2>&1; then ADR_WORLD="index" base64 -d <<<"$(jq -r .content /tmp/adr_index.json)" >/tmp/adr_index.yaml 2>/dev/null @@ -289,13 +289,13 @@ jobs: elif grep -q "Not Found" <<<"$ADR_INDEX_JSON"; then ADR_WORLD="legacy" else - echo "::error::agent-registry decisions/INDEX.yaml 拉取失败(非 404)——索引世界判定失败,fail-closed" + echo "::error::archive/adr/INDEX.yaml 拉取失败(非 404)——索引世界判定失败,fail-closed(ADR-0085 家园单仓化)" exit 1 fi if [[ "$ADR_WORLD" == "legacy" ]]; then - ADR_LISTING=$(gh api "repos/Cloudbird-Software/agent-registry/contents/decisions?per_page=100" --paginate --jq '.[].name' 2>/dev/null) + ADR_LISTING=$(gh api "repos/Cloudbird-Software/archive/contents/adr?per_page=100" --paginate --jq '.[].name' 2>/dev/null) if [[ -z "$ADR_LISTING" ]]; then - echo "::error::agent-registry/decisions 清单拉取失败——ADR 引用存在性无法校验(fail-closed)" + echo "::error::archive/adr 清单拉取失败——ADR 引用存在性无法校验(fail-closed)" exit 1 fi fi @@ -326,7 +326,7 @@ jobs: MISSING=1 fi elif ! grep -q "^ADR-${num}-" <<<"$ADR_LISTING"; then - echo "::error::引用的 ${ref} 在 agent-registry/decisions/ 无对应文件(幽灵 ADR——C1 决策背书不成立)" + echo "::error::引用的 ${ref} 在 archive/adr/ 无对应文件(幽灵 ADR——C1 决策背书不成立)" MISSING=1 fi done diff --git a/AGENTS.md b/AGENTS.md index 921863f..e92a2f2 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,6 +1,6 @@ # AGENTS.md -AI agent 进入本仓的工作契约(索引型,CG-1;治理仓豁免 ≤40 行——协议块+硬规则+索引三合一,ADR-0055 决策 4)。细节按需读引用文件,不常驻上下文。 +PM(项目经理)工作契约(ADR-0085,索引型;治理仓豁免行数放宽至 60——PM 优先范式的入口即本文件)。**组织只规定四道阶段门禁与红线;门禁之间怎么干、调什么资源,由你自主决定。** 细节按需读引用文件,不常驻上下文。 @@ -14,27 +14,42 @@ AI agent 进入本仓的工作契约(索引型,CG-1;治理仓豁免 ≤40 6. front-desk 命令(卡 issue 评论,conductor 转介 arbiter 处理):/claim 认领 · /release 释放租约 · /retry 隔离回流 +## PM 优先(ADR-0085) + +- **入职三步**:① `governance/REPOS.yaml` 看版图 → ② `docs/pm/PLAYBOOK.md`(阶段手册:资源/用法/代价/报告格式)→ ③ 最近 4 周运行报告(`Cloudbird-Software/archive` 仓 `runs/`) +- **看全局**:`bash ghcb board`(全状态流水线:ir-draft…done 的 IR 与卡,不只 ready 卡) +- **四道门禁**(组织控制的全部过程面):① IR 签署→spec——你自著 spec 合法(PR338 先例),spec-author 流水线=可选快速通道 → ② spec PR 必带 suite/ + 红队 survived → 开卡 → ③ 卡实现 PR 全 gate 绿+合并 → 置 `state:done`(T8 谓词机械查合并事实)→ ④ 全部子卡 done + `specs//acceptance.md` → IR `state:done`(T9 谓词) +- **默认开发主力=CNB 免费算力**:`bash ghcb dispatch <卡#> [--tier light|std] [--account ]`(经 cnb-dispatch 经纪人,key 永不入你上下文);gate 红/语义敏感→你自己接手(上升策略不预设,事后写进运行报告) +- **每次 run 结束**:`bash ghcb report` 生成骨架 → 追加到 archive `runs/YYYY-WNN.md`(三节式:事实/体感/改进点;`[followup]` 行=周度 digest 的机械抓手) +- **验收**:`bash ghcb accept ` 生成验收报告骨架 + +## 红线(自主性的边界——永不触碰、永不试图优化) + +- 判定语义:LLM/沙箱只在生成侧,一切判定锚点机械(INV-01/02);沙箱自报数字不采信 +- fail-closed:任何关卡异常/超时/数据缺失=红,无"默认绿" +- append-only 账本:用量/生命周期/分诊/fan-out 产物只增不改(ADR-0062 hash 链) +- 凭据纪律:一切 key 只存 org secret,你永不接触;调用一律借道 dispatch 工作流 ## 硬规则 -- 治理文件(governance/ standards/ scripts/ .github/ CODEOWNERS profile/ Makefile docs/)= C1 路径:PR 必须引用 ADR-NNNN,owner-only review(GOVERNANCE flows.governance_change;与 gate adr-required 机器检查同路径集) -- agent 写仓库身份 = GitHub App `cloudbrid-agent`(AG-1);令牌经 scripts/gh-app-token.sh,单仓作用域、1h 过期(本仓驻留 agent 直接用 `scripts/ghcb`,等价协议块下载版)。例外:org 级 Project(v2) 写与组织成员判定(App 无 organization_projects/members 权限,ADR-0055 决策 8)用 GOVERNANCE_TOKEN(org admin PAT,仅 workflow secrets 面,不落 agent 手) -- 本仓只读治理声明;ADR 与注册条目落盘 agent-registry(REPOS.yaml L1) -- 红队守门制度(ADR-0082):spec/测试设计路径 PR 必须经红队审计;默认 verifier 范式(ADR-0072/LLM-as-a-Verifier);g060 锁定 specs/*/suite/**(ADR-0061/ADR-0081,scripts/g060-lock.sh + g060-escalation.py,非授权身份 exit 2 开 issue 路由 owner 裁决) +- 治理文件(governance/ standards/ scripts/ .github/ CODEOWNERS profile/ Makefile docs/)= C1 路径:PR 必须引用 ADR-NNNN(家园=archive/adr/,ADR-0085),owner-only review +- 写仓身份 = 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) - 不引入新第三方 Action:白名单见 expected-state.json#actions_policy(CI-2) -- 无人值守护栏(ADR-0040,跨仓生效):(a) 每次任务派发与 `gh pr merge --auto` 前,必须检查 org 变量 `AUTO_MERGE_DISABLED`(`gh api /orgs/Cloudbird-Software/actions/variables/AUTO_MERGE_DISABLED --jq .value`,404=未置位)——置位即停一切派发与 automerge,禁止任何绕过尝试;(b) 同一 PR 的修红重试 ≤ policy/automation-limits.yaml `auto_fix.max_attempts`(默认 3),达上限即停手(auto-fix-limit workflow 会关 PR + 开 issue);(c) 不得 reopen 带 `auto-fix-limit-exhausted` 标签的 PR;计数真源 = Checks API(commit 元数据),删标签/重开不重置计数;(d) 派发前确认 .github 仓无未决 `cost-infra`/`cost-circuit-breaker` issue(用量不可知时同样停) +- 无人值守护栏(ADR-0040,跨仓生效):(a) 派发与 automerge 前查 org 变量 `AUTO_MERGE_DISABLED`——置位即停一切;(b) 同一 PR 修红重试 ≤ automation-limits.yaml `auto_fix.max_attempts`(默认 3);(c) 不得 reopen 带 `auto-fix-limit-exhausted` 标签的 PR;(d) 派发前确认无未决 `cost-infra`/`cost-circuit-breaker` issue ## 常用命令(本仓驻留) -- 校验本仓声明:`.github/workflows/gate.yml`(本地等价:`make gates-pr`——bash -n + yaml 全量解析) -- 漂移检测/漂移修复/新仓初始化(**owner 或 CI 专属**——需 org admin PAT,agent 不得持此令牌,AG-1;agent 需要时提卡转交 owner 或走 workflow_dispatch):`GH_TOKEN= bash governance/drift-check.sh`(每日 CI 自动跑;§17=入口协议块对账)· `GH_TOKEN= bash governance/apply.sh`(幂等;失败 loud 退出)· `bash scripts/new-repo-init.sh `(owner) -- 修复循环上限执法:`GH_TOKEN= bash governance/auto-fix-limit.sh`(小时级;`AUTOFIX_DRY_RUN=1` 只报告;同上 owner/CI 专属) -- 成本熔断检查:`GH_TOKEN= bash governance/cost-check.sh`(6h;`COST_USAGE_MINUTES_OVERRIDE=` 注入测试;同上 owner/CI 专属) -- 取 App 令牌:`GH_TOKEN=$(scripts/ghcb )`(缓存命中零网络;`--refresh` 强刷,ADR-0044) -- factory-floor 板/账本手动刷新:Actions → board-sync(dispatch-only;日常 cron 归 butler-ledger,ADR-0055) +- 校验本仓声明:`.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`(幂等) +- 修复循环上限执法:`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 · 组织地图 governance/REPOS.yaml · 期望状态 governance/expected-state.json -- 政策集 governance/policy/(languages.yaml、testing.yaml、无人值守阈值 automation-limits.yaml ADR-0040;入口协议/卡元数据=ADR-0055) -- agent 标准 schema standards/agent/ · 自动化规范 standards/automation/(ADR-0031/0032/0045)· 注册条目与 ADR → agent-registry 仓 +- 治理总声明 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/docs/pm/PLAYBOOK.md b/docs/pm/PLAYBOOK.md new file mode 100644 index 0000000..dfff386 --- /dev/null +++ b/docs/pm/PLAYBOOK.md @@ -0,0 +1,134 @@ +# PM Playbook(ADR-0085) + +PM(强模型项目经理)的唯一深度文档:按四道阶段门禁组织,阶段内怎么调用资源由你自主决定。 +冷启动顺序见 §1;每次 run 结束写运行报告(§6)。治理总声明 `governance/GOVERNANCE.yaml`, +组织地图 `governance/REPOS.yaml`,状态机 `governance/transitions.yaml`——本手册解释怎么用它们,不复述。 + +## §0 自主边界与红线 + +一句话:**自主的是生成路径,永不自主的是判定语义**——spec 怎么写、卡怎么拆、 +派 CNB 还是自己写、用什么顺序调工具,全是你的裁量;但"什么算通过"由 gate / +conductor / 红队 / 验证者在 GitHub CI 内机械判定,你与外部沙箱都只在生成侧(INV-01/02,IR-0004)。 + +四条不可触碰(违反=违约,无豁免通道): + +1. **判定语义**:LLM/NPC 输出永不直接作为任何 gate 输入;沙箱自报数字一律不采信, + 产物进判定链前必经机械核对(基准 SHA / 差异可应用 / 格式校验,INV-03)。 +2. **fail-closed**:任何判定环节异常、超时、数据缺失一律按红处理,无"未定义默认绿"(INV-04)。 +3. **append-only 账本**:用量账本、运行报告、fan-out 产物目录只增不改;纠错追加 erratum 行(INV-05)。 +4. **凭据纪律**:你的上下文永不出现任何 API key(详见 §7,INV-06)。 + +上升策略不预设(ADR-0085 背景裁决 1):没有"什么情况必须找人"的成文清单——该自己上就上 +(gate 红、语义敏感、机械核对不符),事后把判断过程写进运行报告 §6,经 digest 沉淀后再成文。 + +## §1 入职三步 + +1. **看版图**:读 `governance/REPOS.yaml`——L0 治理三仓(.github / CI-Workflows / archive)、 + L2 产品仓、cnb-bridge(L2 可删除层,ADR-0085 决策 6)各是什么、在哪。 +2. **读本手册**:全文一遍,重点 §0 红线与 §8 速查表。 +3. **读最近 4 周运行报告**:`archive/runs/`(格式见 `archive/runs/README.md`)+ + .github 仓 `pm-digest` 标签的 digest issue——前人踩过的坑不用再踩(ADR-0085 决策 8)。 + +worker 视角的认领/开工协议(AGENTS.md entry-protocol v1 块)对 PM 同样适用——PM 也是执行者之一。 + +## §2 阶段一:IR→spec + +把意图变成可红队审计的 spec + suite。门禁=签署(T1/T2)+ spec PR 过 suite 门与红队。 + +- **开 IR**:用 .github 仓 issue 模板 `intent.yml`(打 `type:intent` 标签)。字段全必填(IFACE-01,IR-0001)。 +- **签署**:owner 评论 `/start`(T2)或打 `state:ir-signed` 标签(T1)——只有 owner 能签,你不代签。 +- **spec 谁写**:两条制度等价路径(ADR-0085 决策 5,PR338 先例追认): + - 你自己写——完全合法,且是深度理解 IR 的最好方式; + - spec-author 快速通道:CI-Workflows `spec-author.yml` 流水线生成骨架你再修。 + 模板:CI-Workflows `pipeline/spec-template.md`(正文条款结构 / AC given-when-then / INV-BEH-IFACE 分节)。 +- **spec PR 必带**: + - `suite/` 目录(ADR-0083 suite 门;至少一个非空可解析测试文件——T5 `suite_ready_required` + 谓词会现场重查,T-14 亦要求);注意 `specs/*/suite/**` 在 g060 锁定集内 + (`g060-guard.yml` + `scripts/g060-lock.sh`,非授权身份 exit 2 开裁决 issue,ADR-0061/0081)。 + - 红队 required check `adversary`(`adversary-gate.yml`,ADR-0082):survived 才绿, + fail-closed——漏配/摘除/跳过即红。 +- spec PR 是治理 C1 路径:引用 ADR-NNNN、owner-only review(AGENTS.md 硬规则)。 + +## §3 阶段二:spec→卡 + +红队放行后把 spec 切成可认领的卡。门禁=survived 审计(T6 三元组)。 + +- **过红队**:spec PR 的 adversary check `verdict=survived` + 打 `adversary:survived` 标签 → + T6 转 `wave-planned`;conductor 校验三元组(卡 ID / specVersion / 审计 run ID 均属本次生命周期, + 禁跨卡/历史短路——transitions.yaml T6 注)。 +- **开卡**:每张卡一个 issue,打 `type:card` + `state:ready`(T7 谓词=卡具备认领条件, + ADR-0085 决策 5 补洞)。**AC 从 spec 逐条派生**,不自由发挥——验收(§5)按 AC 对账。 +- **PR 绑定**:实现 PR body 必带一行卡元数据 `Card: /#` + (`bash ghcb card-meta ` 生成;缺失=后续关卡 exit 3,T-14 卡绑定测试按此路由)。 + +## §4 阶段三:实现 + +默认派 CNB 免费算力,你做编排与机械核对。门禁=卡完成 + 全 gate 绿(T8)。 + +- **派单**(默认路径,IR-0004 低决策密度分支执行者=CNB): + - `bash ghcb dispatch`(ADR-0085 决策 4 子命令),或 + - .github 仓 `cnb-dispatch` 工作流(Actions UI / `gh workflow run`,或 + repository_dispatch `cnb-dispatch`)。两者都经经纪人,你不碰 token(§7)。 + - 任务文本机器可读(IFACE-02):必含档位与清单 ID,输出契约=差异全文+测试原始输出 + (含退出码)+基准 SHA 确认;`[run:]` 锚由派单侧自动附加。 +- **档位**(BUDGET-01,policy 真源=`governance/policy/automation-limits.yaml` cnb 节): + `light=1C` 默认 · `std=2C` 须在任务文本带 `tier-reason: <一句话理由>` · + `heavy=4C` 须带 `tier-adr: ADR-NNNN` · 8C 禁用。档位以 build logs 实际核数交叉证实。 +- **窗口纪律**:派前查窗口占用(`cnb-audit` 周审计/`cnb_pool.py windows`),单账号并发上限默认 8; + 派单是异步的——不等 NPC 回复,回收走 `cnb_pool.py collect` 或等周审计。 +- **护栏**(ADR-0040,无人值守):派单前 org 变量 `AUTO_MERGE_DISABLED` 置位即停; + 同一 PR 修红重试 ≤ `auto_fix.max_attempts`(默认 3)。 +- **产物必须机械核对**:沙箱自报数字不采信(§0 红线 1)——差异可应用、测试输出含退出码、 + 基准 SHA 与 run 开始时动态获取值一致;不符即作废记 infra 失败,不手工"看起来对"放行。 +- **什么时候自己接手**:gate 红、语义敏感、机械核对不符、卡面 AC 歧义——自己 `/claim` + (T3 认领,先到先得)本地做。判断依据写进运行报告。 +- **收卡**:PR 合并后在你认领的卡 issue 评论,`state:done` 由 conductor T8 谓词机械查证 + (存在绑定本卡且已合并的 PR,读 `Card:` 元数据)——不是你说了算。 + +## §5 阶段四:验收 + +IR 级收口,证据可机械回查。门禁=T9 谓词(ADR-0085 决策 5)。 + +- 全部子卡 `state:done` 后,写 `specs/IR-XXXX/acceptance.md`:**逐 AC** 列运行时证据 + (gate run / build log / 红队报告链接)+ SHA 锚(证据所属 commit/产物基准)。 +- 打 `state:done`:conductor T9 谓词=全部子卡 done + acceptance.md 存在,两者缺一即拒绝。 +- 自述不算证据:运行报告(§6)是经验输入,验收只认可机械回查的运行时证据(§0 红线 1)。 + +## §6 运行报告怎么写 + +收口即写,防遗忘锚点=卡 `state:done` 之前。报告是经验输入与改进燃料,**不是验收证据**。 + +- **三节式**(缺节即不合格,格式真源 `archive/runs/README.md`): + - **事实**——做了什么、数据、卡点(客观陈述,可被引用核对); + - **体感**——哪里不顺:门禁/文档/工具/流程(明确标注主观); + - **改进点**——每条一行,机械可抽取:`[followup] <域>: <描述>`, + 域 ∈ {playbook, policy, gate, tool}。一行一提案,只登记意向、不直接改治理。 +- **落点**:追加到 `archive/runs/YYYY-WNN.md`(ISO 周,一周一文件,append-only;纠错追加 erratum 行)。 +- **聚合**:archive 仓 `runs-digest.yml` 周一 04:37 UTC 抽取上周全部 `[followup]` 行自动开 + digest issue(.github 仓,`pm-digest` 标签);owner 逐条处置——转卡 / 转 IR / 否决关掉留痕。 +- **升策略沉淀**(§0):你"该自己上就上"的每次判断,写进事实/体感节——这是上升策略成文的唯一来源。 + +## §7 凭据纪律 + +你永不接触任何 key(ADR-0085 决策 7,INV-06)。 + +- **org secret 保险箱**:一切平台 token(CNB 池账号令牌、LLM_API_KEY、CB_APP_ID/ + AGENT_APP_SECRET)只存 GitHub org secrets,仓库与配置零明文。 +- **dispatch 经纪人**:你调资源一律借道 workflow(如 `cnb-dispatch.yml`)——secret 注入 + 发生在 runner 内,你的上下文、任务文本、PR/issue 里都不出现 token。 +- **无密钥目录**:`governance/providers.yaml` 登记可用资源与入口(ADR-0085 决策 7 落地件), + 目录本身无密钥。 +- **加工具零代码**:owner 加 org secret + 加目录条目,即接入;你发现缺资源时提卡转 owner, + 不要自建凭据通道(含把 key 贴进 issue/任务文本——那是红线 4 违约)。 + +## §8 可调资源速查表 + +| 资源 | 用途 | 入口 | 代价 | +|---|---|---|---| +| ghcb 命令族 | 找活/认领/卡元数据/令牌;board/dispatch/accept/report 为 ADR-0085 决策 4 扩展 | `bash ghcb next\|claim\|status\|card-meta\|board\|dispatch\|accept\|report`(scripts/ghcb,钉 SHA 取用见 AGENTS.md) | 本地零成本;GitHub API 配额 | +| spec-author 流水线 | spec 骨架快速通道(与自著制度等价) | CI-Workflows `spec-author.yml`;模板 `pipeline/spec-template.md` | Actions 分钟(org 内 $0 净额)+ 你复核的时间 | +| 红队窗口 | spec/测试设计 PR 的 required check | .github `adversary-gate.yml`(required check `adversary`);执行层 CI-Workflows `pipeline/adversary/` | CNB 核·秒(多账号池)+ 判定侧 CI 分钟 | +| CNB 池 | 默认实现主力(CodeBuddy NPC 沙箱,多账号) | `cnb-dispatch.yml` 工作流或 `ghcb dispatch`;池真源 cnb-bridge 仓(accounts.yaml / cnb_pool.py)+ 账号令牌 org secrets(登记面:expected-state.json / automation-limits.yaml cnb 节) | 免费算力;档位 BUDGET-01(light=1C 默认) | +| 验证者 | 独立验收/封存场景考试 | 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 重复踩坑 | diff --git a/governance/GOVERNANCE.yaml b/governance/GOVERNANCE.yaml index 99a3b2b..713525b 100644 --- a/governance/GOVERNANCE.yaml +++ b/governance/GOVERNANCE.yaml @@ -150,61 +150,48 @@ domains: strength: advisory agent_runtime: - # 标准本体: standards/agent/{tool,skill,agent,team,event}.schema.yaml - # 注册条目: Cloudbird-Software/agent-registry + # ADR-0085 重写:多 agent 编排声明层退役(agent-registry/agent-platform/agent-tools + # 已归档);角色载体=GitHub Actions 工作流(红队/验证者)+ PM 会话(规划)。 + # 保留有现实载体的条款,声明类条款随退役废止(历史正文见 archive/retired/)。 measures: - id: AR-1 - intent: "agent/skill/tool/team 四类声明 + 模型注册表唯一落盘 agent-registry;本地运行目录仅为部署产物;运行实例可追溯到声明+版本" + intent: "角色治理走 GitHub 原生:开发身份=cloudbrid-agent App(AG-1);验证者=verifier-app(ADR-0076);红队=adversary 工作流(AR-10);PM=任意持凭据会话+ghcb 入口协议。显式 agent/skill/tool/team 声明层退役(ADR-0085)" strength: enforced - platform: {github: {mechanism: repo, name: agent-registry, validate: agent-registry/scripts/validate.py}} - verify: {method: registry-validate, on: PR} - id: AR-2 - intent: "引用 status!=approved 的注册条目 = CI 拒绝;proposed→approved 必须走 PR" + intent: "模型/工具接入一律经计量面:LLM 调用唯一入口=CI-Workflows metering wrapper(ADR-0062);外部工具=key 经 org secret、调用经 dispatch 经纪人(providers.yaml 目录+cnb-dispatch 先例);任何仓库/工作流配置零明文 key(AR-3 第一期形态延续,ADR-0048)" strength: enforced - platform: {github: {mechanism: script, entry: agent-registry/scripts/validate.py}} - id: AR-3 - intent: "模型接入一律经 LLM Gateway:声明只引用 alias;provider key 仅存 gateway secret store;任何仓库/agent 配置零明文 key。第一期形态(IR-0001 DECISION-01/ADR-0048)——无 gateway 时允许直连 provider API,key 仅存 org secret LLM_API_KEY(expected-state §5 登记存在性),一切调用经 CI-Workflows 计量 wrapper(scripts/llm-call.sh)落盘用量;alias 语义与 AR-8 不变,回切触发条件见 ADR-0048" + intent: "PM 自主边界(ADR-0085 决策 1):阶段门禁之间资源调用完全自主;不可触碰=判定语义(生成/裁决分离)、fail-closed、append-only 账本、凭据纪律。上升策略不预设——PM 实践经运行报告(archive/runs/)沉淀后再成文" strength: enforced - - id: AR-4 - intent: "agent 凭据最小权限:per-agent/per-team 作用域 token(衔接 AG-1);声明中只写 env 引用" - strength: enforced - - id: AR-5 - intent: "双层控制:软引导(AGENTS.md/identity/skill 正文)+ 硬边界(工具面裁剪→权限引擎→凭据→平台防线);冲突时硬控制优先;拦截记录进事件流" - strength: enforced - - id: AR-6 - intent: "团队必须声明 lifecycle;ephemeral 团队 archive_to+team 侧 handoff 全部完成才允许销毁(stewardship 侧项由 curator 异步消费归档资产执行,不阻塞销毁——红队 #18 措辞对齐,ADR-0016);persistent 团队(team:stewardship——ADR-0004 规划名 governance-core 的落地形态)对治理资产持续负责" - strength: enforced - platform: {github: {mechanism: script, entry: agent-registry/scripts/validate.py, check: lifecycle}} - - id: AR-7 - intent: "过程数据三分离:声明/决策→git(registry/ADR);事件→数据层(JSONL 起步);原始轨迹→对象存储滚动30天;项目仓仅 AGENTS.md+team 引用行" - strength: enforced - schema: standards/agent/event.schema.yaml - - id: AR-8 - intent: "agent 必须声明 archetype(九 LLM 原型封顶:builder/planner/test-author/judge/curator/adversary/researcher/deployer/responder + 六机制原型不实例化,ADR-0010);builder↔test-author、planner↔builder 不得同一声明;test-author/judge 族级独立(models.yaml family);新增原型须 ADR 论证" - strength: enforced - validate: agent-registry/scripts/validate.py - - id: AR-9 - intent: "可信交付=失败可见:agent 内 guardrails → test-author 出题(owner 已批验收示例→冻结测试树)→ verifier 机制判卷(required check)→ persistent 团队外部审计(escape review/控制测试)→ 平台防线兜底;builder 永不自我信任;意图保真靠 owner 批验收示例(intent ratification)" - strength: enforced - validate: agent-registry/scripts/validate.py - id: AR-10 intent: "红队守门 Veto 强制力:红队审计 verdict=insufficient 时状态变为 needs-human 且无法进入 wave-planned;specs/** 路径 PR 必须包含 adversary check;开发实现路径 PR 按 EXPECTED_SKIP 模式条件化豁免" strength: enforced platform: {github: {mechanism: ruleset-required-status, context: "adversary", workflow: "CI-Workflows/.github/workflows/adversary*.yml"}} verify: {method: adversary-run, on: PR} + external_compute: + # ADR-0085 / IR-0004 D 组(cnb-bridge-design 三平面):发散与执行侧可外置免费算力; + # 判定与裁决永不外置。CNB=当前唯一实例(cnb-bridge 仓,可删除层)。 + measures: + - id: EX-1 + intent: "外部免费算力(当前=CNB CodeBuddy NPC 沙箱池)仅用于生成与执行侧(默认实现引擎:常规卡优先派发,强模型 PM 处理 gate 红/语义敏感);判定锚点全部在 GitHub CI(沙箱自报数字不采信,INV-01/02 语义)" + strength: enforced + platform: {external: {bridge: cnb-bridge, removable: true, + removal_drill: "cnb-bridge/REMOVAL.md 单页清单;删除后 gate/org-gate/conductor 语义不变(ADR-0085 决策 6 断言)", + seams: "GOVERNANCE 本条目 + org secrets CNB_TOKEN_ + .github cnb-dispatch/cnb-audit 两工作流(隔离审计=cnb-audit 周扫,操作性引用越界即红)"}} + flows: governance_change: # 治理仓(.github governance|standards、agent-registry、CI-Workflows workflows/)内容变更的唯一合法路径 # 授权凭证 = ADR + PR 记录;破玻璃保留但被监控 classes: - id: C1 - scope: ["governance/(整目录——含 GOVERNANCE/REPOS.yaml、rulesets、expected-state、apply/drift 脚本、policy)", standards/, models.yaml, decisions/, scripts/, ".github/(CI 门禁自身——改门禁=改治理)", CODEOWNERS, profile/, "tests/(agent-registry 验证器元测试——验证器之验证)", "template-service 脚手架面(.github/、AGENTS.md、Makefile、docs/、zizmor.yml——供应链入口:模板防线被污染=全部新仓继承后门,红队修复 P0;src/ 业务面属 C3。ADR-0021 收敛范围:整仓 C1 会与 SC-3 依赖 automerge 冲突——依赖 PR 无 ADR 不可自动合并)"] + scope: ["governance/(整目录——含 GOVERNANCE/REPOS.yaml、rulesets、expected-state、apply/drift 脚本、policy)", standards/, specs/, scripts/, ".github/(CI 门禁自身——改门禁=改治理)", CODEOWNERS, profile/, "tests/(agent-registry 验证器元测试——验证器之验证)", "template-service 脚手架面(.github/、AGENTS.md、Makefile、docs/、zizmor.yml——供应链入口:模板防线被污染=全部新仓继承后门,红队修复 P0;src/ 业务面属 C3。ADR-0021 收敛范围:整仓 C1 会与 SC-3 依赖 automerge 冲突——依赖 PR 无 ADR 不可自动合并)"] requires: [PR, "ADR(新建或引用编号)", "drift-check 本地预检", owner-merge] rationale: "治理意图变更必须可追溯到一个决策记录;无 ADR 不合并。scope 与机器执法路径全集一致(.github gate:governance/standards/scripts/.github/CODEOWNERS/profile;agent-registry validate:standards/decisions/scripts/.github/CODEOWNERS/tests——评审项:声明与执行不得互斥);template-service 的脚手架面是新仓派生源头,视同治理意图变更(gate adr-required 机器检查 + owner-only review;ADR-0021 范围收敛后与 CI-Workflows ci.yml 的 C1 路径判定一致)" - id: C2 - scope: [agent-registry/registry/, 业务仓 AGENTS.md/CODEOWNERS] - requires: [PR, "validate.py 通过"] + scope: ["业务仓 AGENTS.md/CODEOWNERS"] # agent-registry/registry/ 随 ADR-0085 退役;C2 validate 门随声明层废止 + requires: [PR] - id: C3 scope: [文档/注释/README/模板] requires: [PR] diff --git a/governance/REPOS.yaml b/governance/REPOS.yaml index 94da19d..ebbef11 100644 --- a/governance/REPOS.yaml +++ b/governance/REPOS.yaml @@ -13,7 +13,7 @@ org: Cloudbird-Software # ============================================================================ layers: L0: 标准与治理(intent:GOVERNANCE / 标准 schema / 工作流实现) - L1: 注册条目(声明:agent / skill / tool / team / 模型别名 / ADR) + L1: 记忆与试卷(ADR 正本+索引 / 运行报告 / 验收场景封存)——ADR-0085:声明层退役 L2: 实现(代码:框架、工具、业务项目) L3: 数据(不进 git:事件流 / 轨迹 / 团队运行态) @@ -36,21 +36,16 @@ repos: - name: agent-registry layer: L1 visibility: public - status: active - role: 注册层——agent/skill/tool/team 声明 + 模型注册表(models.yaml) + ADR;AR-2 状态门禁校验器 - key_paths: [registry/, decisions/, standards/, scripts/validate.py, scripts/simulate-wave.py] - # ADR-0021:standards/(协作标准)与 simulate-wave.py(CI required - # 门禁,ADR-0015)此前不在组织地图——门禁自身逃逸治理地图 - policies: [agent_runtime] + status: retired # ADR-0085:多 agent 编排声明层退役,GitHub 归档只读;快照=archive/retired/;ADR 家园已迁 archive/adr/ + role: (退役)注册层——声明快照见 archive/retired/agent-registry/;git 历史即正本存档 - name: archive layer: L1 visibility: public status: active - role: 记忆层——ADR 归档正本(append-only,字节保真)+规划回归集+事件 JSONL+红队报告 - 落位(宪法 §1/§13 推论二,W1-C1 .github#164/ADR-0053);ADR 状态真源=agent-registry - decisions/INDEX.yaml 墓碑索引(ADR-0053) - key_paths: [adr/, scripts/verify_migration.py] + role: 记忆层——ADR 家园(正本+INDEX 索引同仓,ADR-0085 单仓化)+退役快照 retired/ + +PM 运行报告 runs/([followup] 周度 digest 消费)+规划回归集+事件 JSONL+红队报告落位 + key_paths: [adr/, runs/, retired/, scripts/verify_migration.py] - name: template-service layer: L2 @@ -63,18 +58,13 @@ repos: - name: agent-tools layer: L2 visibility: public - status: active - role: 自研工具实现仓——被 tool 声明的 implementation 字段引用(ADR-0021:approved 工具 - gitcode-pr 引用本仓,planned=悬空供应链引用;已按 flows.new_repo 从 template-service - 派生并跑 new-repo-init) + status: retired # ADR-0085:TS 工具服务器随编排框架退役,GitHub 归档只读 + role: (退役)工具实现仓——指针页 archive/retired/agent-tools.md - name: agent-platform layer: L2 visibility: public - status: active - role: 执行层仓——agent-registry 声明渲染到 openjiuwen/jiuwenswarm 运行时(ADR-0025): - 渲染器/SwarmFlow workflow 编译器/机制原型执行面/TUI 可观测+agentctl 干预/渲染漂移门禁; - Python 仓(languages.yaml PY-* 规则,声明适配现实——openjiuwen SDK 为 Python) - key_paths: [src/agentplatform/, tests/, docker/, Makefile] + status: retired # ADR-0085:渲染层随编排框架退役,GitHub 归档只读 + role: (退役)执行层仓——指针页 archive/retired/agent-platform.md - name: arbiter layer: L2 @@ -143,18 +133,15 @@ repos: 仅用于测试/验证路径写权(DECISION-02 隔离不变量,drift-check §18 断言) key_paths: [entries/, schema/, canary/] -# 上游依赖(不属于本组织;不 fork、不 submodule) -# 治理方式:声明于此 + 部署渲染时 clone 并 pin tag(ADR-0002 rev1) -external_upstreams: - - name: openjiuwen-runtime - repo: openJiuwen-ai/jiuwenswarm - role: 多智能体编排框架运行时(AgentServer/Gateway/Team/权限引擎/A2A) - consumed_by: [agent-registry 的声明渲染] - - name: openjiuwen-sdk - repo: openJiuwen-ai/agent-core - role: agent 内核 SDK(openjiuwen:DeepAgent/TeamAgent/SwarmFlow 引擎;PyPI 钉版消费,ADR-0025) - consumed_by: [agent-platform 渲染产物/ArchetypeWorkerBackend/治理 rails] - - name: openjiuwen-a2x - repo: openJiuwen-ai/agent-protocol - role: A2X 注册中心(分布式 Team 控制面) - consumed_by: [分布式 team 部署] + - name: cnb-bridge + layer: L2 + visibility: public + status: active + role: CNB 免费算力桥接仓(可删除层,EX-1/ADR-0085/IR-0004 D 组)——多账号池 + accounts.yaml(无明文 token)+派单协议 cnb_pool.py+work-inbox PM 自起协议+ + REMOVAL.md 单页删除清单;判定永不外置(INV-02) + key_paths: [accounts.yaml, cnb_pool.py, work-inbox.yaml, REMOVAL.md] + +# 上游依赖(ADR-0085:openjiuwen 双上游随编排框架退役停止消费;未来引入新上游时 +# 恢复本节声明——部署渲染 pin tag 的治理方式不变) +external_upstreams: [] diff --git a/governance/drift-check.sh b/governance/drift-check.sh index 352f6b1..3a1b8b1 100644 --- a/governance/drift-check.sh +++ b/governance/drift-check.sh @@ -298,7 +298,7 @@ done # 起进一步校验 archive 正本可达性。本节后验实体性(防空壳)并独立复核存在性: # 窗口内合并 PR 的 ADR-NNNN 引用必须有实质决策结构——伪造/幽灵 ADR 最长 24h 内 # 检出(与 §8 直推检测同为 post-hoc 防线;C1 的权威人类门禁仍是 owner-only review)。 -# 内容源双世界(ADR-0053):agent-registry/decisions/INDEX.yaml(墓碑索引)存在 → +# 内容源双世界(ADR-0053;ADR-0085 家园单仓化后唯一世界):archive/adr/INDEX.yaml(索引随正本同居)存在 → # decisions/ 只剩同名墓碑(无实质结构),实体性改验 INDEX 指向的 archive 仓正本 # (字节保真原件);INDEX 404(迁移前/回滚)→ 旧逻辑对 decisions/ 本体验证。 ADR_RE='ADR-[0-9]{4}' @@ -333,7 +333,7 @@ adr_substantive() { # $1=四位编号 → stdout: missing|ok|shell|unreadable [[ -n "$_c" ]] && decoded=$(base64 -d <<<"$_c" 2>/dev/null || true) fi else - content=$(api "https://api.github.com/repos/$ORG/agent-registry/contents/$apath" | jq -r '.content // empty') + content=$(api "https://api.github.com/repos/$ORG/archive/contents/$apath" | jq -r '.content // empty') [[ -z "$content" ]] && continue decoded=$(base64 -d <<<"$content" 2>/dev/null || true) fi @@ -354,16 +354,16 @@ adr_substantive() { # $1=四位编号 → stdout: missing|ok|shell|unreadable # 独立 7 天窗口(不用 §8 的 SINCE——那是 policy_effective 起算的直推检测窗口, # 而 ADR 引用后验须覆盖 policy 生效前已合并、引用了伪造 ADR 的 PR) ADR_SINCE=$(date -u -d '7 days ago' +%Y-%m-%dT%H:%M:%SZ) -ADR_DIR_LISTING=$(api "https://api.github.com/repos/$ORG/agent-registry/contents/decisions") +ADR_DIR_LISTING=$(api "https://api.github.com/repos/$ORG/archive/contents/adr") if ! jq -e 'type == "array"' <<<"$ADR_DIR_LISTING" >/dev/null 2>&1; then - drift "ADR 真源 agent-registry/decisions 读取失败,引用存在性无法后验(fail-closed): $(jq -r '.message // "非数组"' <<<"$ADR_DIR_LISTING" 2>/dev/null || echo 传输失败)" + drift "ADR 真源 archive/adr 读取失败,引用存在性无法后验(fail-closed,ADR-0085): $(jq -r '.message // "非数组"' <<<"$ADR_DIR_LISTING" 2>/dev/null || echo 传输失败)" else # 墓碑索引探测(ADR-0053):200 → 索引世界(map:编号→archive 正本路径); # 404 → 旧世界;其他失败 → 报漂移(fail-closed:检测器失明不得伪装无漂移), # 并降级旧世界继续跑完本节(漂移行已置红,后续节不受影响)。 ADR_INDEX_MODE="" ADR_INDEX_MAP="" - ADR_INDEX_JSON=$(api "https://api.github.com/repos/$ORG/agent-registry/contents/decisions/INDEX.yaml") + ADR_INDEX_JSON=$(api "https://api.github.com/repos/$ORG/archive/contents/adr/INDEX.yaml") if jq -e '.content' <<<"$ADR_INDEX_JSON" >/dev/null 2>&1; then ADR_INDEX_MAP=$(base64 -d <<<"$(jq -r '.content' <<<"$ADR_INDEX_JSON")" 2>/dev/null | awk ' /^[[:space:]]*-[[:space:]]*number:/ { n=$3; gsub(/[^0-9]/, "", n); cur=sprintf("%04d", n+0); ap="" } @@ -400,7 +400,7 @@ else if [[ -n "$ADR_INDEX_MODE" ]]; then where="INDEX entries 无此编号" else - where="agent-registry/decisions/ 无 ADR-${num}-*.md" + where="archive/adr/ 无 ADR-${num}-*.md" fi drift "repo '$r' PR#$pnum 引用幽灵 ADR ${ref}(${where}——C1 变更的决策背书不成立)" GHOST=1 ;; diff --git a/governance/expected-state.json b/governance/expected-state.json index 320412b..187f00a 100644 --- a/governance/expected-state.json +++ b/governance/expected-state.json @@ -101,7 +101,9 @@ "CB_APP_ID", "AGENT_APP_SECRET", "GOVERNANCE_TOKEN", - "LLM_API_KEY1" + "LLM_API_KEY1", + "CNB_TOKEN_XUEMEI", + "CNB_TOKEN_P11" ], "github_app": { "name": "cloudbrid-agent", @@ -139,18 +141,16 @@ "repositories": [ ".github", "CI-Workflows", - "agent-registry", "archive", "template-service", - "agent-tools", - "agent-platform", "arbiter", "Shorts_Director", "Script_Writer", "Use-up-Plan", "AI_Web_School", "mutual", - "holdout" + "holdout", + "cnb-bridge" ] }, "direct_push_exemptions": { @@ -213,5 +213,8 @@ "path": ".github/workflows/adversary-gate.yml" } ] + }, + "notes": { + "adr_0085": "ADR-0085:agent-registry/agent-platform/agent-tools 退役(GitHub 归档只读);CNB 双账号 secret 登记(EX-1 三接缝);cnb-bridge 入 verifier 挂载面" } -} +} \ No newline at end of file diff --git a/governance/policy/automation-limits.yaml b/governance/policy/automation-limits.yaml index 447cf4d..8bb196c 100644 --- a/governance/policy/automation-limits.yaml +++ b/governance/policy/automation-limits.yaml @@ -1,77 +1,90 @@ -version: 1 -# ============================================================================ -# automation-limits.yaml —— 无人值守自动化生存护栏(auto-fix 上限 + 额度/成本熔断) -# 真源: 本文件(阈值声明);执行: governance/auto-fix-limit.sh + governance/cost-check.sh -# (.github/workflows/auto-fix-limit.yml / cost-check.yml 周期调用) -# 决策: ADR-0040(P2-8,.github issue #93 / 计划 #81) -# 注: 熔断标志(org 变量 AUTO_MERGE_DISABLED)是运行态标志,刻意不纳入 -# expected-state.json——置位/复位是运行动作而非期望状态,纳入会造成 -# "人工复位后 drift-check 反向改回去"的对账冲突(ADR-0040 后果节)。 -# ============================================================================ - -auto_fix: - # 修复循环上限:PR 的 gate 失败次数达到 max_attempts → 撤 auto-merge + 关 PR + 开 issue - max_attempts: 3 - # 计数对象:required check 名(BP-2,全组织统一 gate) - check_name: gate - # 默认作用域:agent 身份(AG-1 App slug)创建的 open PR;其他 PR 打 opt_in_label 纳入同路径 - # (脚本按 slug 宽容匹配 REST "[bot]" 与 GraphQL "app/" 两种作者登录名形态) - author: cloudbrid-agent - opt_in_label: auto-fix-loop - # 视为一次失败尝试的 check run 结论(取每 commit 最新一次 gate run) - # fail-closed 方向:多计早关优于少计烧额度;非头 commit 上的未完成(null)亦计 - failed_conclusions: [failure, timed_out, startup_failure, cancelled] - # 超限处置标记(打在目标仓 PR 上 / 说明 issue 打在 .github 仓) - exhausted_label: auto-fix-limit-exhausted - issue_label: auto-fix-limit - -cost: - # Actions 分钟月预算(声明值,非账单——全仓公开计费净额 $0,此处防的是失控烧穿速率; - # 当月用量 = /orgs/{org}/settings/billing/usage 按 product=actions & unitType=Minutes 汇总) - actions_minutes: - # BUDGET-03(spec IR-0001 v3,PR#129,owner 已拍板):Actions 分钟本期不设上限 - # ——2026-08-21 曾按旧值 20000 在故障指标上误触 P0 硬停(#149 用量不可知+ - # billing 端点 410),按 spec 明示的 C1 调整路径改为等效无上限大数。 - # 恢复上限=回改本值(ADR-0040 复位流程不变)。 - quota_per_month: 200000000 - warn_pct: 80 # ≥80% 开告警 issue(同日去重,不硬停) - hardstop_pct: 100 # ≥100% 置熔断 + 撤全部 open PR auto-merge + P0 issue - # LLM token 月预算(W2-C3 .github#216,ADR-0062:数据源接入——CI-Workflows - # pipeline/metering 计量账本,按 invoke 聚合的 JSONL hash 链;cost-check 拉取 - # metering-ledger 分支经 metering.py aggregate 按角色档归账。链断/归账失败= - # infra fail-closed(exit 2),不静默归零不盲熔断。pending 形态保留为回滚值 - # (ADR-0040 决策 6);注入通道 COST_LLM_TOKENS_USED 仍优先于真实数据源) - llm_tokens: - quota_per_month: 200000000 - warn_pct: 80 - hardstop_pct: 100 - data_source: ciw-metering - metering: # data_source=ciw-metering 时消费(复用本段现有结构,不新建配置面) - repo: Cloudbird-Software/CI-Workflows - branch: metering-ledger - code_path: pipeline/metering # 归账引擎 metering.py 所在(cost-check.yml sparse checkout) - # verifier 档口径(AC-11):W3-C3 llm-verifier 接入后,verifier run 的 token 账 - # 落盘并与 LLM 响应 usage 字段交叉核对。偏差超出任一阈值 → 该 run 判定作废转人工 - # (非仅告警)。K 与 pivots 为暴露成本旋钮,降档须留痕且不得改动判定语义与阈值。 - # 计量数据源复用上方 ciw-metering(records-*.jsonl role=verifier 切片),本段仅 - # 声明 verifier 档独立阈值与 fail-closed 行为,不新建计量通道。 - verifier: # data_source=ciw-metering role=verifier 时的 fail-closed 阈值 - deviation_pct: 5 # 相对偏差阈值(%),超出即作废 - deviation_abs: 50 # 绝对偏差阈值(tokens),超出即作废 - role: verifier # metering 记录 role 字段过滤值 - fail_closed_action: void_and_escalate - void_reason: "token 账与 metering 偏差超阈值(相对>5% 或绝对>50 tokens)——作废转人工" - # 成本旋钮(BUDGET-01 / AC-11):K 与 pivots 为暴露的成本旋钮,降档须留痕且 - # 不得改动判定语义与阈值。默认取保守高值(ASSUMPTION-03)。 - cost_knobs: - k_default: 3 # 每 criterion 重复评估次数 - pivots_default: 1 # 成本旋钮 pivots(记录用) - downgrade_requires_log: true - downgrade_preserves_semantic: true - -circuit_breaker: - # 熔断标志:org Actions 变量(visibility=all,全仓 workflow/agent 可读) - variable: AUTO_MERGE_DISABLED - # 人工复位:owner PATCH org 变量为 false(或 DELETE)+ 在 P0 issue 留复位评论(留痕); - # cost-check 确认"变量已复位且用量 <100%"后自动关闭 P0 issue - reset_by: randypanding +version: 1 +# ============================================================================ +# automation-limits.yaml —— 无人值守自动化生存护栏(auto-fix 上限 + 额度/成本熔断) +# 真源: 本文件(阈值声明);执行: governance/auto-fix-limit.sh + governance/cost-check.sh +# (.github/workflows/auto-fix-limit.yml / cost-check.yml 周期调用) +# 决策: ADR-0040(P2-8,.github issue #93 / 计划 #81) +# 注: 熔断标志(org 变量 AUTO_MERGE_DISABLED)是运行态标志,刻意不纳入 +# expected-state.json——置位/复位是运行动作而非期望状态,纳入会造成 +# "人工复位后 drift-check 反向改回去"的对账冲突(ADR-0040 后果节)。 +# ============================================================================ + +auto_fix: + # 修复循环上限:PR 的 gate 失败次数达到 max_attempts → 撤 auto-merge + 关 PR + 开 issue + max_attempts: 3 + # 计数对象:required check 名(BP-2,全组织统一 gate) + check_name: gate + # 默认作用域:agent 身份(AG-1 App slug)创建的 open PR;其他 PR 打 opt_in_label 纳入同路径 + # (脚本按 slug 宽容匹配 REST "[bot]" 与 GraphQL "app/" 两种作者登录名形态) + author: cloudbrid-agent + opt_in_label: auto-fix-loop + # 视为一次失败尝试的 check run 结论(取每 commit 最新一次 gate run) + # fail-closed 方向:多计早关优于少计烧额度;非头 commit 上的未完成(null)亦计 + failed_conclusions: [failure, timed_out, startup_failure, cancelled] + # 超限处置标记(打在目标仓 PR 上 / 说明 issue 打在 .github 仓) + exhausted_label: auto-fix-limit-exhausted + issue_label: auto-fix-limit + +cost: + # Actions 分钟月预算(声明值,非账单——全仓公开计费净额 $0,此处防的是失控烧穿速率; + # 当月用量 = /orgs/{org}/settings/billing/usage 按 product=actions & unitType=Minutes 汇总) + actions_minutes: + # BUDGET-03(spec IR-0001 v3,PR#129,owner 已拍板):Actions 分钟本期不设上限 + # ——2026-08-21 曾按旧值 20000 在故障指标上误触 P0 硬停(#149 用量不可知+ + # billing 端点 410),按 spec 明示的 C1 调整路径改为等效无上限大数。 + # 恢复上限=回改本值(ADR-0040 复位流程不变)。 + quota_per_month: 200000000 + warn_pct: 80 # ≥80% 开告警 issue(同日去重,不硬停) + hardstop_pct: 100 # ≥100% 置熔断 + 撤全部 open PR auto-merge + P0 issue + # LLM token 月预算(W2-C3 .github#216,ADR-0062:数据源接入——CI-Workflows + # pipeline/metering 计量账本,按 invoke 聚合的 JSONL hash 链;cost-check 拉取 + # metering-ledger 分支经 metering.py aggregate 按角色档归账。链断/归账失败= + # infra fail-closed(exit 2),不静默归零不盲熔断。pending 形态保留为回滚值 + # (ADR-0040 决策 6);注入通道 COST_LLM_TOKENS_USED 仍优先于真实数据源) + llm_tokens: + quota_per_month: 200000000 + warn_pct: 80 + hardstop_pct: 100 + data_source: ciw-metering + metering: # data_source=ciw-metering 时消费(复用本段现有结构,不新建配置面) + repo: Cloudbird-Software/CI-Workflows + branch: metering-ledger + code_path: pipeline/metering # 归账引擎 metering.py 所在(cost-check.yml sparse checkout) + # verifier 档口径(AC-11):W3-C3 llm-verifier 接入后,verifier run 的 token 账 + # 落盘并与 LLM 响应 usage 字段交叉核对。偏差超出任一阈值 → 该 run 判定作废转人工 + # (非仅告警)。K 与 pivots 为暴露成本旋钮,降档须留痕且不得改动判定语义与阈值。 + # 计量数据源复用上方 ciw-metering(records-*.jsonl role=verifier 切片),本段仅 + # 声明 verifier 档独立阈值与 fail-closed 行为,不新建计量通道。 + verifier: # data_source=ciw-metering role=verifier 时的 fail-closed 阈值 + deviation_pct: 5 # 相对偏差阈值(%),超出即作废 + deviation_abs: 50 # 绝对偏差阈值(tokens),超出即作废 + role: verifier # metering 记录 role 字段过滤值 + fail_closed_action: void_and_escalate + void_reason: "token 账与 metering 偏差超阈值(相对>5% 或绝对>50 tokens)——作废转人工" + # 成本旋钮(BUDGET-01 / AC-11):K 与 pivots 为暴露的成本旋钮,降档须留痕且 + # 不得改动判定语义与阈值。默认取保守高值(ASSUMPTION-03)。 + cost_knobs: + k_default: 3 # 每 criterion 重复评估次数 + pivots_default: 1 # 成本旋钮 pivots(记录用) + downgrade_requires_log: true + downgrade_preserves_semantic: true + +circuit_breaker: + # 熔断标志:org Actions 变量(visibility=all,全仓 workflow/agent 可读) + variable: AUTO_MERGE_DISABLED + # 人工复位:owner PATCH org 变量为 false(或 DELETE)+ 在 P0 issue 留复位评论(留痕); + # cost-check 确认"变量已复位且用量 <100%"后自动关闭 P0 issue + reset_by: randypanding + +# ---- CNB 免费算力池参数(EX-1 / ADR-0085 / IR-0004 AC-15;配置面:加账号=改配置零代码)---- +cnb: + accounts: # 与 cnb-bridge/accounts.yaml 对账(漂移即红) + - alias: xuemei + secret: CNB_TOKEN_XUEMEI # org secret,仅 cnb-dispatch/cnb-audit 可见 + - alias: p11 + secret: CNB_TOKEN_P11 + tiers: {light: 1, std: 2, heavy: 4} # 核数;8C 禁用;heavy 须 ADR + default_tier: light + max_concurrent_per_account: 8 + quota_alert_pct: 20 # 余量低于此值自动开 cost 类 issue + ledger_reconcile_deviation_pct: 10 # 快照差分 vs build logs 实耗对账偏差告警线 diff --git a/governance/policy/contracts.yaml b/governance/policy/contracts.yaml index ac1d847..8a50680 100644 --- a/governance/policy/contracts.yaml +++ b/governance/policy/contracts.yaml @@ -55,7 +55,7 @@ repos: migrations: null # 盘点备注(不声明、不接线): - # agent-registry 之外的注册表消费方 agent-platform —— 仅 vendor/ 拷贝, + # (ADR-0085:agent-platform 退役,本条历史注释保留备查)agent-platform —— 仅 vendor/ 拷贝, # 源头真身在 agent-registry(vendor 漂移属另一关注面,不在本卡范围) # mutual —— spec/01-schemas.md 为散文 schema,不可机判;落地 JSON Schema 后再声明 # Script_Writer / agent-tools / Use-up-Plan / .github / CI-Workflows —— 无契约面 diff --git a/governance/policy/languages.yaml b/governance/policy/languages.yaml index 534c7a3..5f21a04 100644 --- a/governance/policy/languages.yaml +++ b/governance/policy/languages.yaml @@ -6,7 +6,7 @@ layers: allowed: - {language: go, when: "default"} - {language: typescript, when: "frontend-isomorphic only", requires: [TS-1, TS-2]} - - {language: python, when: "agent-runtime integration only(agent-platform:openjiuwen SDK 渲染/治理执行面——不写 Python 无法落地,ADR-0025 声明适配现实)", requires: [PY-1, PY-2]} + # ADR-0085:agent-platform 退役,Python 准入回归默认拒绝(存量豁免先例:ADR-0023 AI_Web_School、ADR-0084 QW_Arena1) forbidden: [rust, java, csharp, cpp, new_language_without_org_approval] rationale: "训练数据海量、写法单一、编译反馈快 → LLM 产出可靠" llm_prompt: diff --git a/governance/providers.yaml b/governance/providers.yaml new file mode 100644 index 0000000..8b4b41b --- /dev/null +++ b/governance/providers.yaml @@ -0,0 +1,37 @@ +version: 1 +# ============================================================================ +# providers.yaml —— 外部工具/模型资源目录(ADR-0085 决策 7:池化资源的最小凭据方案) +# 铁律:本文件【无任何密钥】——secret_ref 只登记 org secret 名;key 永不进仓库、 +# 永不进 agent/PM 上下文。调用一律借道 dispatch 经纪人工作流(工作流内取 +# org secret → 调用 → 产物写回 run 日志/issue/artifact)。 +# 加工具 = owner 加一条 org secret + 本目录一条登记,零代码。 +# CNB 池的账号级配置在 cnb-bridge/accounts.yaml(可删除层,EX-1 隔离);本目录 +# 只登记入口(三接缝口径:目录条目属导航性提及,不计入操作性引用)。 +# ============================================================================ +entries: + - name: cnb-pool + kind: compute # 免费沙箱算力池(默认实现引擎) + entry: cnb-dispatch(.github 仓 workflow)/ ghcb dispatch <卡#> + broker: .github/workflows/cnb-dispatch.yml + secrets: [CNB_TOKEN_XUEMEI, CNB_TOKEN_P11] + catalog: Cloudbird-Software/cnb-bridge accounts.yaml + limits: automation-limits.yaml#cnb(档位 light/std/heavy、并发、配额告警线) + removal: cnb-bridge/REMOVAL.md(可删除层,删除后核心治理语义不变) + - name: llm-verifier + kind: model # 红队/verifier 判定模型(kimi k2.7) + entry: adversary 工作流(repository_dispatch / workflow_dispatch) + secrets: [LLM_API_KEY1] + vars: [LLM_ENDPOINT1, MODEL1] + metering: CI-Workflows pipeline/metering/metering-wrapper.sh(ADR-0062 账本) + note: 判定链唯一 LLM 通道;NPC 无 logprobs 永不做判定(ADR-0082) + - name: llm-spec-author + kind: model # spec 起草快速通道(可选——PM 自著等价,ADR-0085) + entry: CI-Workflows spec-author.yml(workflow_dispatch) + secrets: [LLM_API_KEY] + metering: 同上 + note: 冷上下文流水线(INV-04);产出仍须过 suite 门+红队 + - name: github-apps + kind: identity # 写仓身份(AG-1:开发/验证分离) + entry: scripts/gh-app-token.sh(单仓作用域 1h 令牌) + secrets: [CB_APP_ID, AGENT_APP_SECRET] + note: cloudbrid-agent(开发)+ verifier-app(仅测试/验证路径写权,ADR-0076) diff --git a/governance/transitions.yaml b/governance/transitions.yaml index 9bfa06a..a9885b2 100644 --- a/governance/transitions.yaml +++ b/governance/transitions.yaml @@ -1,128 +1,156 @@ -version: 1 -# 状态机转移表(IR-0001 IFACE-03 / ADR-0049)——conductor 唯一定义源, -# 只解释不内嵌逻辑。本文件是 C1 资产:改动走 PR+ADR。 -# -# schema: -# from_state = 转移前状态(当前 issue 无 state:* 标签时视为 ir-draft) -# event = label:state: | comment:/start | comment:/claim | comment:/retry -# | label:<非state判定标签>(W3-C1 Bug 流扩展:label:bug/label:reproduced -# ——判定标签驱动的转移,见 B1/B2 注) -# to_state = 转移后状态(conductor 以 App 身份换标签:移除 from、置上 to) -# action = invoke:spec-author | claim | noop(W0 全集) -# guard = 布尔表达式;变量白名单:sender_role(owner|agent|none)、 -# author_association(OWNER|MEMBER|COLLABORATOR|CONTRIBUTOR|NONE)、 -# label_set(当前标签名集合) -# 未列出的 (from_state, event) 组合 = 禁止转移(conductor no-op;重复投递/ -# 跳态/反向天然被 from_state 匹配拒绝——幂等键 {issue, from, to} 的落盘形态)。 -# W3-C1(ADR-0064):append bug 流状态 bug/reproduced/fixed 与转移 B1-B5。 -# Bug 流与 Feature 流的差异:入口免签(bug 标签即 reproduce 阶段)、 -# 判定标签(reproduced/cannot-reproduce/inconclusive)是事实标签而非状态—— -# cannot-reproduce 不产生状态转移(保留人裁,ADR-0064 决策 4); -# bugflow 的状态写序=先移旧态再置新态(防双 state 并存窗口触发 -# conductor 多状态 abort)。 -states: [ir-draft, ir-signed, spec, redteam, wave-planned, ready, in-progress, quarantine, needs-human, done, bug, reproduced, fixed] - -transitions: - - id: T1 # 主通路:owner/agent 打签署标签 → 进 spec 阶段(BEH-01)。 - # from_state=ir-signed:label 事件送达时签署标签已生效(当前态即 ir-signed)—— - # 首版误写 ir-draft 导致必然 no-op(#143 实测 AUDIT: from=ir-signed verdict=noop)。 - # 守卫不过=回退该标签(退回 ir-draft);幂等:已在 spec 态则无匹配=no-op。 - from_state: ir-signed - event: label:state:ir-signed - to_state: spec - action: invoke:spec-author - guard: "sender_role in ['owner', 'agent'] and 'type:intent' in label_set" - - id: T2 # owner 评论 /start 等价签署(AC-1 的 comment 路径) - from_state: ir-draft - event: comment:/start - to_state: spec - action: invoke:spec-author - guard: "sender_role == 'owner' and 'type:intent' in label_set" - - id: T3 # 认领:先到先得(当前态必须 ready,否则 no-op)(BEH-08) - from_state: ready - event: comment:/claim - to_state: in-progress - action: claim - guard: "sender_role == 'agent' or author_association in ['OWNER', 'MEMBER', 'COLLABORATOR']" - - id: T4 # 隔离重判(BEH-07 的 /retry 回流) - from_state: quarantine - event: comment:/retry - to_state: ready - action: noop - guard: "sender_role in ['owner', 'agent']" - # T5/T6 suite 就绪谓词(ISSUE-263 AC-12 / W4-C1 .github#282): - # suite 就绪 = 确定性谓词,conductor 在转移前重新断言(不信任 dispatch 载荷): - # 1. suite/ 目录存在; - # 2. suite/ 含至少一个非空测试文件(字节数 >0 且含有效断言语法); - # 3. 测试文件可解析(python -m py_compile / node --check / 等语言原生语法校验通过)。 - # 任一不满足 → 转移拒绝并告警(fail-closed:不放行不确定的红队/波次规划)。 - # 该谓词由 conductor 在运行时对目标 spec 仓做确定性检查,非仅标签面断言。 - - id: T5 # spec PR 合并 + suite 就绪后进入红队阶段(ISSUE-263 AC-12 / ADR-0049 / W4-C1) - from_state: spec - event: label:state:redteam - to_state: redteam - action: noop - guard: "sender_role in ['owner', 'agent'] and 'type:intent' in label_set" - suite_ready_required: true # conductor 在转移前执行 suite 就绪谓词(存在+非空+可解析) - # T6 三元组校验(ISSUE-263 AC-12 / W4-C1 .github#282): - # 进入 wave-planned 必须存在该卡本次生命周期内、卡 ID + specVersion + 审计 run ID - # 三元组对应的 survived 审计记录。禁止跨卡/历史记录短路: - # - 卡 ID 必须与当前 issue 匹配; - # - specVersion 必须与当前 spec PR 合并后的 spec 版本一致; - # - 审计 run ID 必须来自本次生命周期(非历史 run)。 - # 三元组任一缺失或不匹配 → 转移拒绝并告警。 - - id: T6 # redteam 产出 survived 审计记录后进入波次规划(ISSUE-263 AC-12 / ADR-0079 / W4-C1) - from_state: redteam - event: label:state:wave-planned - to_state: wave-planned - action: noop - guard: "sender_role == 'agent' and 'adversary:survived' in label_set" - triple_check: # conductor 在转移前校验三元组 survived 记录 - card_id: issue # 卡 ID 键 = 当前 issue number - spec_version: spec # specVersion 键 = spec 文件版本(SHA 或 semver) - audit_run_id: adversary-run # 审计 run ID 键 = adversary 报告 run_id - source_label: "adversary:survived" # 触发本转移的 survived 标签 - # needs-human 不可直跳 wave-planned(ISSUE-263 AC-12 / AC-14 / W4-C1): - # needs-human → wave-planned 转移不在本表列 = 禁止(conductor no-op)。 - # 该断言作为 required check 在 CI 常驻强制运行(conductor-negtest 模式), - # 确保没有任何路径可从 needs-human 直跳 wave-planned。 - # ---------- Bug 流(W3-C1 .github#218 / ADR-0064,宪法 §3)---------- - # 执行者:CI-Workflows pipeline/bugflow(bug-reproduce.yml 以 App 身份写标签)。 - # conductor 对 label:bug/label:reproduced 等非 state 标签事件静默 no-op—— - # B1/B2 是状态机声明(唯一转移定义源原则:bug 流的合法转移也必须落表), - # 由 bugflow 执行转移本体;B5 的人工/流水线双路径均满足 from_state 幂等。 - - id: B1 # Bug 流入口(免签——区别于 Feature 流签署前置):bug form 落 bug - # 标签 → 进 reproduce 阶段(state:bug)。无 state 标签按约定视为 ir-draft。 - from_state: ir-draft - event: label:bug - to_state: bug - action: noop - guard: "'bug' in label_set" - - id: B2 # 三值判定之【复现】:base 稳定 fail(+fix 候选 pass)→ reproduced - # 标签 + state:reproduced;reproduced 后产 failing repro test 待 owner 签 - # (锁定 ADR-0061 → 修复 → 关卡 → 合并)。证伪(label:cannot-reproduce) - # 不在此列:不转移状态、不关单——保留人裁。 - from_state: bug - event: label:reproduced - to_state: reproduced - action: noop - guard: "sender_role in ['owner', 'agent']" - - id: B3 # 修复合入:owner/agent 置 state:fixed(fix PR 的合并事实回写 bug 单) - from_state: reproduced - event: label:state:fixed - to_state: fixed - action: noop - guard: "sender_role in ['owner', 'agent']" - - id: B4 # 终态关单:fixed → done(owner/agent 置 state:done 后关单) - from_state: fixed - event: label:state:done - to_state: done - action: noop - guard: "sender_role in ['owner', 'agent']" - - id: B5 # 三值判定之【不可判定】兜底:环境错/超时/翻转,换新环境重试一次仍 - # 不可判定 → label:inconclusive + state:needs-human(转人裁) - from_state: bug - event: label:state:needs-human - to_state: needs-human - action: noop - guard: "sender_role in ['owner', 'agent']" +version: 1 +# 状态机转移表(IR-0001 IFACE-03 / ADR-0049)——conductor 唯一定义源, +# 只解释不内嵌逻辑。本文件是 C1 资产:改动走 PR+ADR。 +# +# schema: +# from_state = 转移前状态(当前 issue 无 state:* 标签时视为 ir-draft) +# event = label:state: | comment:/start | comment:/claim | comment:/retry +# | label:<非state判定标签>(W3-C1 Bug 流扩展:label:bug/label:reproduced +# ——判定标签驱动的转移,见 B1/B2 注) +# to_state = 转移后状态(conductor 以 App 身份换标签:移除 from、置上 to) +# action = claim | noop(W0 全集;invoke:spec-author 已随 ADR-0085 退役——产出方式不再由状态机指定) +# guard = 布尔表达式;变量白名单:sender_role(owner|agent|none)、 +# author_association(OWNER|MEMBER|COLLABORATOR|CONTRIBUTOR|NONE)、 +# label_set(当前标签名集合) +# 未列出的 (from_state, event) 组合 = 禁止转移(conductor no-op;重复投递/ +# 跳态/反向天然被 from_state 匹配拒绝——幂等键 {issue, from, to} 的落盘形态)。 +# W3-C1(ADR-0064):append bug 流状态 bug/reproduced/fixed 与转移 B1-B5。 +# Bug 流与 Feature 流的差异:入口免签(bug 标签即 reproduce 阶段)、 +# 判定标签(reproduced/cannot-reproduce/inconclusive)是事实标签而非状态—— +# cannot-reproduce 不产生状态转移(保留人裁,ADR-0064 决策 4); +# bugflow 的状态写序=先移旧态再置新态(防双 state 并存窗口触发 +# conductor 多状态 abort)。 +states: [ir-draft, ir-signed, spec, redteam, wave-planned, ready, in-progress, quarantine, needs-human, done, bug, reproduced, fixed] + +transitions: + - id: T1 # 主通路:owner/agent 打签署标签 → 进 spec 阶段(BEH-01)。 + # from_state=ir-signed:label 事件送达时签署标签已生效(当前态即 ir-signed)—— + # 首版误写 ir-draft 导致必然 no-op(#143 实测 AUDIT: from=ir-signed verdict=noop)。 + # 守卫不过=回退该标签(退回 ir-draft);幂等:已在 spec 态则无匹配=no-op。 + # ADR-0085:action 降级 noop——spec 产出方式不再由状态机指定(PM 自著与 + # spec-author 流水线制度等价,PR338 先例;流水线保留为可选快速通道)。 + from_state: ir-signed + event: label:state:ir-signed + to_state: spec + action: noop + guard: "sender_role in ['owner', 'agent'] and 'type:intent' in label_set" + - id: T2 # owner 评论 /start 等价签署(AC-1 的 comment 路径;ADR-0085 同 T1 降级 noop) + from_state: ir-draft + event: comment:/start + to_state: spec + action: noop + guard: "sender_role == 'owner' and 'type:intent' in label_set" + - id: T3 # 认领:先到先得(当前态必须 ready,否则 no-op)(BEH-08) + from_state: ready + event: comment:/claim + to_state: in-progress + action: claim + guard: "sender_role == 'agent' or author_association in ['OWNER', 'MEMBER', 'COLLABORATOR']" + - id: T4 # 隔离重判(BEH-07 的 /retry 回流) + from_state: quarantine + event: comment:/retry + to_state: ready + action: noop + guard: "sender_role in ['owner', 'agent']" + # T5/T6 suite 就绪谓词(ISSUE-263 AC-12 / W4-C1 .github#282): + # suite 就绪 = 确定性谓词,conductor 在转移前重新断言(不信任 dispatch 载荷): + # 1. suite/ 目录存在; + # 2. suite/ 含至少一个非空测试文件(字节数 >0 且含有效断言语法); + # 3. 测试文件可解析(python -m py_compile / node --check / 等语言原生语法校验通过)。 + # 任一不满足 → 转移拒绝并告警(fail-closed:不放行不确定的红队/波次规划)。 + # 该谓词由 conductor 在运行时对目标 spec 仓做确定性检查,非仅标签面断言。 + - id: T5 # spec PR 合并 + suite 就绪后进入红队阶段(ISSUE-263 AC-12 / ADR-0049 / W4-C1) + from_state: spec + event: label:state:redteam + to_state: redteam + action: noop + guard: "sender_role in ['owner', 'agent'] and 'type:intent' in label_set" + suite_ready_required: true # conductor 在转移前执行 suite 就绪谓词(存在+非空+可解析) + # T6 三元组校验(ISSUE-263 AC-12 / W4-C1 .github#282): + # 进入 wave-planned 必须存在该卡本次生命周期内、卡 ID + specVersion + 审计 run ID + # 三元组对应的 survived 审计记录。禁止跨卡/历史记录短路: + # - 卡 ID 必须与当前 issue 匹配; + # - specVersion 必须与当前 spec PR 合并后的 spec 版本一致; + # - 审计 run ID 必须来自本次生命周期(非历史 run)。 + # 三元组任一缺失或不匹配 → 转移拒绝并告警。 + - id: T6 # redteam 产出 survived 审计记录后进入波次规划(ISSUE-263 AC-12 / ADR-0079 / W4-C1) + from_state: redteam + event: label:state:wave-planned + to_state: wave-planned + action: noop + guard: "sender_role == 'agent' and 'adversary:survived' in label_set" + triple_check: # conductor 在转移前校验三元组 survived 记录 + card_id: issue # 卡 ID 键 = 当前 issue number + spec_version: spec # specVersion 键 = spec 文件版本(SHA 或 semver) + audit_run_id: adversary-run # 审计 run ID 键 = adversary 报告 run_id + source_label: "adversary:survived" # 触发本转移的 survived 标签 + # needs-human 不可直跳 wave-planned(ISSUE-263 AC-12 / AC-14 / W4-C1): + # needs-human → wave-planned 转移不在本表列 = 禁止(conductor no-op)。 + # 该断言作为 required check 在 CI 常驻强制运行(conductor-negtest 模式), + # 确保没有任何路径可从 needs-human 直跳 wave-planned。 + # ---------- PM 优先范式收口(ADR-0085:T7/T8/T9——卡生命周期补洞 + IR 验收门)---------- + - id: T7 # 卡就绪:波次卡进入可认领态(ADR-0085 决策 5;此前 wave-planned→ready + # 无转移=conductor 永不执行,卡就绪靠手动标签——收口为机器路径) + from_state: wave-planned + event: label:state:ready + to_state: ready + action: noop + guard: "sender_role in ['owner', 'agent'] and 'type:card' in label_set" + - id: T8 # 卡完成:谓词=存在绑定本卡且已合并的 PR(body 含 Card: 行)—— + # "完成工作卡+测试通过"的机械定义(gate 绿由 PR required checks 承担, + # 本谓词只认合并事实,ADR-0085 决策 5) + from_state: in-progress + event: label:state:done + to_state: done + action: noop + guard: "sender_role in ['owner', 'agent'] and 'type:card' in label_set" + merged_pr_required: true # conductor 运行时重查(不信任标签事件载荷) + - id: T9 # IR 全面验收(Gate 4):谓词=①全部子卡 state:done ②验收报告 + # specs//acceptance.md 存在于本仓——两条件 conductor 运行时重查; + # 子卡=body 含"父意图: #<本 IR>"的 open issue(ADR-0085 决策 5) + from_state: wave-planned + event: label:state:done + to_state: done + action: noop + guard: "sender_role in ['owner', 'agent'] and 'type:intent' in label_set" + acceptance_required: true # conductor 运行时重查(全子卡 done + 验收报告存在) + # ---------- Bug 流(W3-C1 .github#218 / ADR-0064,宪法 §3)---------- + # 执行者:CI-Workflows pipeline/bugflow(bug-reproduce.yml 以 App 身份写标签)。 + # conductor 对 label:bug/label:reproduced 等非 state 标签事件静默 no-op—— + # B1/B2 是状态机声明(唯一转移定义源原则:bug 流的合法转移也必须落表), + # 由 bugflow 执行转移本体;B5 的人工/流水线双路径均满足 from_state 幂等。 + - id: B1 # Bug 流入口(免签——区别于 Feature 流签署前置):bug form 落 bug + # 标签 → 进 reproduce 阶段(state:bug)。无 state 标签按约定视为 ir-draft。 + from_state: ir-draft + event: label:bug + to_state: bug + action: noop + guard: "'bug' in label_set" + - id: B2 # 三值判定之【复现】:base 稳定 fail(+fix 候选 pass)→ reproduced + # 标签 + state:reproduced;reproduced 后产 failing repro test 待 owner 签 + # (锁定 ADR-0061 → 修复 → 关卡 → 合并)。证伪(label:cannot-reproduce) + # 不在此列:不转移状态、不关单——保留人裁。 + from_state: bug + event: label:reproduced + to_state: reproduced + action: noop + guard: "sender_role in ['owner', 'agent']" + - id: B3 # 修复合入:owner/agent 置 state:fixed(fix PR 的合并事实回写 bug 单) + from_state: reproduced + event: label:state:fixed + to_state: fixed + action: noop + guard: "sender_role in ['owner', 'agent']" + - id: B4 # 终态关单:fixed → done(owner/agent 置 state:done 后关单) + from_state: fixed + event: label:state:done + to_state: done + action: noop + guard: "sender_role in ['owner', 'agent']" + - id: B5 # 三值判定之【不可判定】兜底:环境错/超时/翻转,换新环境重试一次仍 + # 不可判定 → label:inconclusive + state:needs-human(转人裁) + from_state: bug + event: label:state:needs-human + to_state: needs-human + action: noop + guard: "sender_role in ['owner', 'agent']" diff --git a/profile/README.md b/profile/README.md index e234676..805fef5 100644 --- a/profile/README.md +++ b/profile/README.md @@ -2,6 +2,8 @@ 一人软件公司,AI Agent 驱动开发。所有仓库公开,质量与安全靠制度 + 自动化门禁,不靠人盯。 +**运行范式(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 消费)。 + ## 我们的工程制度 - 每个仓库唯一的必需 check 是 **`gate`**:聚合 hygiene(密钥/大文件扫描)+ `make check`(lint + test)+ 依赖审查。 diff --git a/scripts/ghcb b/scripts/ghcb old mode 100755 new mode 100644 index f4addea..deb8be0 --- a/scripts/ghcb +++ b/scripts/ghcb @@ -10,6 +10,13 @@ # # (仅 holder 本人或 owner 可成,ADR-0054 §4) # ghcb status [repo] # 只读:卡标签态(真相源)+ arbiter 租约持有者/到期 # ghcb card-meta [repo] # 输出 PR body 应贴的卡元数据行(Card: /#) +# —— PM 子命令(ADR-0085)—— +# ghcb board [repo] # PM 全流水线视图:全部 state:* 的 IR/卡(不只 ready) +# ghcb dispatch [repo] [--tier light|std] [--account ] +# # 派 CNB:触发 .github 仓 cnb-dispatch 工作流(EX-1 经纪人, +# # key 永不入 PM 上下文);=卡 issue 号 +# ghcb accept # 生成 IR 验收报告骨架(specs/IR-XXXX/acceptance.md)到 stdout +# ghcb report [周号] # 生成运行报告骨架(archive/runs/YYYY-WNN.md 追加段)到 stdout # # next/claim/release 用调用方自己的 gh 凭据(gh auth login);认领合法性单一真源=conductor guard # + arbiter CAS(ADR-0055 转介)。status 读 arbiter 仓 refs/leases/*(只读,无写权要求)。 @@ -84,6 +91,77 @@ case "$CMD" in echo "Card: $(norm_repo "$REPO_ARG")#$N" echo "提示:把上面一行原样贴进 PR body(缺失=后续关卡 exit 3)" >&2 ;; + board) + # PM 全流水线视图(ADR-0085):全状态 IR/卡一览——修复冷上下文 PM"只看得到 ready 卡"的断面 + REPO_ARG="${2:-$(origin_repo)}" + [[ -n "$REPO_ARG" ]] || { echo "错误:不在 git 仓内且未指定仓(用法: ghcb board [repo])" >&2; exit 2; } + R="$(norm_repo "$REPO_ARG")" + for s in ir-draft ir-signed spec redteam wave-planned ready in-progress quarantine needs-human done; do + echo "== state:$s ==" + gh issue list --repo "$R" --label "state:$s" --state open --json number,title --jq '.[] | "#\(.number) \(.title)"' 2>/dev/null || true + done + echo "== 无状态标签的 type:intent/card(=ir-draft 约定态)==" + gh issue list --repo "$R" --label type:intent --state open --json number,title,labels --jq '.[] | select([.labels[].name | startswith("state:")] | any | not) | "#\(.number) \(.title)"' 2>/dev/null || true + ;; + dispatch) + # CNB 派单(EX-1 三接缝的 PM 面):key 留在 org secret,本命令只发 workflow_dispatch + N="${2:?用法: ghcb dispatch [repo] [--tier light|std] [--account ]}" + REPO_ARG="${3:-$(origin_repo)}"; shift 3 2>/dev/null || shift $# + TIER="light"; ACCOUNT="" + while [[ $# -gt 0 ]]; do + case "$1" in + --tier) TIER="$2"; shift 2 ;; + --account) ACCOUNT="$2"; shift 2 ;; + *) shift ;; + esac + done + CARD_REPO="$(norm_repo "$REPO_ARG")" + TITLE=$(gh issue view "$N" --repo "$CARD_REPO" --json title --jq .title 2>/dev/null) || { echo "错误:卡 #$N 读取失败" >&2; exit 2; } + echo "派单 → CNB(卡 #$N「${TITLE}」|档位 $TIER${ACCOUNT:+|账号 $ACCOUNT})" + echo "任务文本=卡正文+本提示;经 .github 仓 cnb-dispatch 经纪人执行(key 永不出 org secret)" + MSYS2_ARG_CONV_EXCL='*' MSYS_NO_PATHCONV=1 gh workflow run cnb-dispatch.yml -R "$ORG/.github" -f card="$CARD_REPO#$N" -f tier="$TIER" ${ACCOUNT:+-f account="$ACCOUNT"} -f task="@card" && echo "已触发(几分钟后在 .github 仓 Actions→cnb-dispatch 看 run;回收=gh workflow run cnb-dispatch.yml -f ... 或 cnb-audit 周扫)" || { echo "错误:workflow 触发失败(检查 gh 凭据对 $ORG/.github 的写权)" >&2; exit 2; } + ;; + accept) + # IR 验收报告骨架(T9 谓词消费 specs//acceptance.md)——PM 填证据后 PR 入仓 + N="${2:?用法: ghcb accept }" + IRID=$(gh issue view "$N" -R "$ORG/.github" --json title --jq .title 2>/dev/null | grep -oE 'IR-[0-9]+' | head -1) + [[ -n "$IRID" ]] || IRID="IR-$(printf '%04d' "$N")" + cat < 验收日期: $(date -u +%F) +- 判据: IR 正文的"期望的可观察变化"逐条 + 运行时证据链接(SHA 锚定) + +## 子卡清单(曾存在性自证;全部须 state:done) +- #<卡号> <标题> → PR <链接> + + +## 逐条证据 +| # | 期望变化 | 证据(链接+SHA) | 结论 | +|---|---------|-----------------|------| +| 1 | <从 IR 抄写> | | ✅ | + +## 残留与移交 +- <已知未竟项/后续 IR 建议——诚实申报,验收不掩盖> +SKEL + ;; + report) + # 运行报告骨架(ADR-0085 决策 8)——追加到 archive/runs/YYYY-WNN.md + python3 - <<'PY' +import datetime +t = datetime.date.today(); y, w, _ = t.isocalendar() +print(f"### run-{y}w{w:02d}-{int(datetime.datetime.now().timestamp())%100000} · {t.isoformat()} · ") +print("scope: [, #]") +print() +print("**事实** ——") +print("**体感** ——") +print("**改进点** ——") +print("[followup] : <一行一提案>") +PY + echo "↑ 追加到 archive/runs/$(date -u +%G)-W$(date -u +%V).md(三节式缺节即不合格;格式见 archive/runs/README.md)" >&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