diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..b1543d2 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# 治理仓所有权:全部路径归 owner(GM-2/C1 分级的评审归属) +* @randypanding diff --git a/.github/workflows/gate.yml b/.github/workflows/gate.yml new file mode 100644 index 0000000..6df4807 --- /dev/null +++ b/.github/workflows/gate.yml @@ -0,0 +1,43 @@ +name: gate +on: + pull_request: + push: + branches: [main] + +permissions: {contents: read} + +jobs: + gate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + with: + persist-credentials: false + - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 + with: + python-version: "3.12" + - name: YAML 全量解析(governance + standards) + run: | + pip install pyyaml -q + python3 - <<'EOF' + import glob, sys, yaml + files = glob.glob("governance/**/*.yaml", recursive=True) + glob.glob("standards/**/*.yaml", recursive=True) + assert files, "未找到任何 yaml" + for f in files: + yaml.safe_load(open(f, encoding="utf-8")); print("OK", f) + EOF + - name: ruleset JSON 解析 + run: | + sudo apt-get -qq update && sudo apt-get -qq install -y jq >/dev/null + for f in governance/rulesets/*.json; do jq -e . "$f" >/dev/null || exit 1; echo "OK $f"; done + - name: 脚本语法检查 + run: | + bash -n governance/apply.sh && bash -n governance/drift-check.sh && bash -n scripts/new-repo-init.sh && bash -n scripts/gh-app-token.sh + echo "OK scripts" + - name: REPOS.yaml 引用自检(active 仓的 key_paths 存在于本仓声明语境) + run: python3 -c " + import yaml + m = yaml.safe_load(open('governance/REPOS.yaml')) + names = [r['name'] for r in m['repos']] + assert len(names) == len(set(names)), 'REPOS.yaml 有重名仓' + print('OK REPOS.yaml', len(names), 'repos')" diff --git a/governance/GOVERNANCE.yaml b/governance/GOVERNANCE.yaml index 6c526e3..f399174 100644 --- a/governance/GOVERNANCE.yaml +++ b/governance/GOVERNANCE.yaml @@ -23,7 +23,7 @@ domains: strength: enforced platform: {github: {mechanism: org-ruleset, name: main-protection, definition: rulesets/main-protection.json}} verify: {method: drift-check, frequency: weekly} - exception: [AI_Web_School, .github] + exception: [AI_Web_School] - id: BP-2 intent: "合并前置:唯一 required check = gate(聚合检查)" strength: enforced @@ -119,8 +119,9 @@ domains: platform: {github: {mechanism: workflow, file: .github/workflows/governance-drift.yml, cron: "Mon 03:00 UTC"}} verify: {method: self, state: expected-state.json} - id: GM-2 - intent: "配置变更走 PR→合并→apply.sh(禁止网页手改)" - strength: advisory + intent: "治理仓变更一律走 flows.governance_change 分级流程(C1 附 ADR / C2 过 validate / C3 走 PR);破玻璃=直推后 24h 内回填;owner 与 AI 同受约束" + strength: enforced + verify: {method: drift-check, part: section-8, frequency: weekly} - id: GM-3 intent: "政策文件机器可判定(本目录);agent 按需读取,不常驻上下文" strength: advisory @@ -174,8 +175,34 @@ domains: intent: "过程数据三分离:声明/决策→git(registry/ADR);事件→数据层(JSONL 起步);原始轨迹→对象存储滚动30天;项目仓仅 AGENTS.md+team 引用行" strength: enforced schema: standards/agent/event.schema.yaml + - id: AR-8 + intent: "agent 必须声明 archetype(builder/checker/orchestrator/curator/interface/observer/operator);同一声明不得兼任 builder 与 checker;checker 强制 private workspace;分类决定权限基线与审计强度" + strength: enforced + validate: agent-registry/scripts/validate.py + - id: AR-9 + intent: "可信交付=失败可见:agent 内 guardrails(输出校验+必跑命令+禁自证)→ team 内独立 checker 验收(不同声明/不同模型别名)→ persistent 团队外部审计(governance-core 周期)→ 平台防线兜底;builder 永不自我信任" + strength: enforced + validate: agent-registry/scripts/validate.py flows: + governance_change: + # 治理仓(.github governance|standards、agent-registry、CI-Workflows workflows/)内容变更的唯一合法路径 + # 授权凭证 = ADR + PR 记录;破玻璃保留但被监控 + classes: + - id: C1 + scope: [GOVERNANCE.yaml, rulesets/, expected-state.json, standards/, models.yaml, decisions/] + requires: [PR, "ADR(新建或引用编号)", "drift-check 本地预检", owner-merge] + rationale: "治理意图变更必须可追溯到一个决策记录;无 ADR 不合并" + - id: C2 + scope: [agent-registry/registry/, 业务仓 AGENTS.md/CODEOWNERS] + requires: [PR, "validate.py 通过"] + - id: C3 + scope: [文档/注释/README/模板] + requires: [PR] + break_glass: + rule: "紧急回滚允许 admin 直推(ruleset bypass),24h 内必须补:回填 PR + ADR + 在漂移 issue 说明原因" + detection: "drift-check §8:策略生效日之后默认分支的非 PR commit = 漂移" + policy_effective: "2026-08-19T00:00:00Z" new_repo: steps: - {cmd: "gh repo create / --template /template-service --public --clone"} diff --git a/governance/apply.sh b/governance/apply.sh index 50f4348..a539f21 100755 --- a/governance/apply.sh +++ b/governance/apply.sh @@ -4,7 +4,7 @@ # 什么时候跑: # - drift-check 报漂移后,修复用 # - 修改 governance/ 文件后,发布用 -# 变更通过 PR 进入 governance/(main-protection 排除了本仓库,可直接合并), +# 变更按 flows.governance_change 分级走 PR 进入 governance/(C1 须附 ADR), # 合并后本地跑一次本脚本 = "基础设施即代码"的 apply。 # # 用法: GH_TOKEN= bash apply.sh diff --git a/governance/drift-check.sh b/governance/drift-check.sh index 882593c..9c2949b 100755 --- a/governance/drift-check.sh +++ b/governance/drift-check.sh @@ -136,6 +136,28 @@ else echo "SKIP REPOS.yaml 校验(环境缺 python3+pyyaml;GitHub runner 自带)" fi +# ---------- 8. 直推检测(GM-2 破玻璃监控)---------- +# flows.governance_change.policy_effective 之后,受治仓默认分支上的非 PR commit = 漂移 +SINCE=$(python3 - <<'EOF' +from datetime import datetime, timezone, timedelta +eff = datetime(2026, 8, 19, tzinfo=timezone.utc) # policy_effective +lo = datetime.now(timezone.utc) - timedelta(days=7) # 检测窗口 +print(max(eff, lo).strftime("%Y-%m-%dT%H:%M:%SZ")) +EOF +) +for r in $REPOS; do + jq -e --arg r "$r" '($r as $x | . | index($x)) != null' <<<"$EXCLUDES" >/dev/null && continue + DIRECT=$(api "https://api.github.com/repos/$ORG/$r/commits?sha=main&since=$SINCE&per_page=100" \ + | jq -r '[.[] | select(.commit.message | test("[(]#[0-9]+[)]\$") | not) | .sha[0:8] + " " + (.commit.message | split("\n")[0])] | .[]') + if [[ -n "$DIRECT" ]]; then + while IFS= read -r line; do + drift "repo '$r' 存在非 PR 直推 commit: $line(破玻璃须 24h 内回填 ADR+PR,见 flows.governance_change)" + done <<<"$DIRECT" + else + ok "no-direct-push '$r' (since $SINCE)" + fi +done + echo "----------------------------------------" if [[ $DRIFTS -gt 0 ]]; then echo "结果: $DRIFTS 项漂移。修复: bash governance/apply.sh 或手动改回" diff --git a/governance/rulesets/main-protection.json b/governance/rulesets/main-protection.json index 319a692..7611975 100644 --- a/governance/rulesets/main-protection.json +++ b/governance/rulesets/main-protection.json @@ -4,7 +4,7 @@ "enforcement": "active", "conditions": { "ref_name": { "include": ["~DEFAULT_BRANCH"], "exclude": [] }, - "repository_name": { "include": ["~ALL"], "exclude": ["AI_Web_School", ".github"] } + "repository_name": { "include": ["~ALL"], "exclude": ["AI_Web_School"] } }, "bypass_actors": [ { "actor_id": null, "actor_type": "OrganizationAdmin", "bypass_mode": "always" } diff --git a/standards/agent/agent.schema.yaml b/standards/agent/agent.schema.yaml index b822589..98295bd 100644 --- a/standards/agent/agent.schema.yaml +++ b/standards/agent/agent.schema.yaml @@ -6,11 +6,23 @@ description: | 条目落盘 agent-registry/registry/agents/.yaml。 约束:所有引用一律用注册表 id(model 用 alias),禁止内联实现细节与密钥。 type: object -required: [id, version, role, model, capabilities, permissions] +required: [id, version, role, archetype, model, capabilities, permissions] properties: id: {type: string, pattern: "^[a-z0-9][a-z0-9-]*$", description: "引用形式 agent:"} version: {type: string, pattern: "^[0-9]+\\.[0-9]+\\.[0-9]+$"} status: {enum: [proposed, approved, deprecated, rejected]} + archetype: + enum: [builder, checker, orchestrator, curator, interface, observer, operator] + description: | + 原型(信任边界分类,AR-8)。分类决定默认权限基线/凭据策略/审计强度: + builder 生产者:写代码/文档/artifacts;产出必须被独立 checker 验收 + checker 检查者:只读为主,输出=判决(pass/fail+理由);强制 private workspace + orchestrator 编排者:任务分解/分配/汇总,不直接产 artifacts;高审计 + curator 保管者:治理仓/registry 维护、归档审核(persistent 团队主力) + interface 接口者:对外通道接收需求/汇报;低权限转发+翻译 + observer 观测者:只读采集度量/事件分析;只写数据层 + operator 运维者:部署/回滚;strict 模式,生产动作全 ask+人工审批(RL-1) + 约束:同一声明单 archetype;builder 与 checker 不得同一声明(利益分离) role: {type: string, description: "一句话职责;决定团队装配"} identity: type: object @@ -57,6 +69,30 @@ properties: properties: types: {type: array, items: {enum: [user_profile, episodic, semantic]}, description: "记忆类别;敏感信息过滤默认开启"} retention: {type: string, description: "如 30d;episodic 默认随 ephemeral team 归档"} + guardrails: + type: object + description: "输出侧硬约束(AR-9 第一层):交付前置条件,由编排框架在产出提交前强制" + properties: + output_schema_strict: + type: boolean + description: "输出必须通过 io_contract 的 schema 校验才可提交" + must_run: + type: array + items: {type: string} + description: "交付前必须全部退出码 0 的命令(如 make check / scripts/validate.py)" + forbidden: + type: array + items: {enum: [no-self-test, no-force-push, no-secret-output, no-bypass-gate]} + description: | + 禁则(防同义反复/越权): + no-self-test 同一 run 内不得既写实现又写其验收测试(fake_tests 风险,测试须来自 checker 或既有 golden) + no-force-push 不得改写已推送历史 + no-secret-output 输出/artifacts 不得含密钥 + no-bypass-gate 不得跳过或降级 gate 检查 + post_conditions: + type: array + items: {type: string} + description: "交付后置断言(机器可判定优先,标注 [ci]/[llm])" io_contract: type: object description: "被作为 tool / A2A 调用时必需;未声明则不可被 expose" diff --git a/standards/agent/team.schema.yaml b/standards/agent/team.schema.yaml index 66f665d..2198435 100644 --- a/standards/agent/team.schema.yaml +++ b/standards/agent/team.schema.yaml @@ -33,6 +33,21 @@ properties: assign: {enum: [leader-dispatch, claim, hybrid], description: "任务分配方式"} merge: {enum: [leader-aggregate, shared-store, debate], description: "结果合并方式"} workflow_ref: {type: string, description: "团队级流程文件(阶段/波次/验收门);复杂流程引用,不在此内联"} + verification: + type: object + description: "验证链(AR-9):team 内独立验收 + team 外周期审计;含 builder 成员的团队必须声明" + properties: + in_team_check: + type: object + properties: + checkers: {type: array, items: {type: string}, description: "验收 checker 的 agent 引用;必须 archetype=checker 且与被检 builder 不同声明、不同模型别名"} + coverage: {enum: [all-outputs, sampled], description: "all-outputs=每件产出必验收;sampled=抽样(附比例说明)"} + external_audit: + type: object + properties: + team: {type: string, description: "审计方 persistent 团队(如 team:governance-core)"} + frequency: {enum: [per-delivery, weekly, monthly]} + method: {type: array, items: {enum: [metrics-anomaly, re-check-sample, drift-review, retrospective-review]}, description: "指标异常检测/抽样复检/漂移复盘/回溯评审"} workspace: type: object description: "团队共享工作区;中间产物可追溯"