-
Notifications
You must be signed in to change notification settings - Fork 0
feat: 波次 schema v1——卡模板扩展 + 解析器(W2-C3 前半,IR-0006) #432
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| name: 工作卡(wave) | ||
| description: 波次工作卡——wave-planner 产卡用(IR-0006 W2-C3 / IFACE-03:budget/capabilities/evidence 可选块) | ||
| labels: [type:card] | ||
| body: | ||
| - type: markdown | ||
| attributes: | ||
| value: | | ||
| 卡正文是机器解析契约(conductor T7 解析 / cost-check 波次视图消费)—— | ||
| 标题与块名不可改写。budget/capabilities/evidence 三块可选:不写=无预算 | ||
| 约束(缺省语义);写了则必须合法(非法=T7 拒绝就绪,fail-closed)。 | ||
| 预算硬停语义:on_exceed: hard-stop 超限置熔断+撤 auto-merge+开 P0 | ||
| (ADR-0040 复位流程不变);warn 只告警。 | ||
| - type: textarea | ||
| id: header | ||
| attributes: | ||
| label: 卡头(父意图/Spec/波次/依赖) | ||
| description: 单行引用块——父意图: #n | Spec: specs/IR-NNNN/spec.md vN | 波次: W2 | 依赖: … | ||
| placeholder: | | ||
| > 父意图: #402 | Spec: `specs/IR-0006/spec.md` v1 | 波次: W2 | 依赖: W1 退出判据 | ||
| validations: | ||
| required: true | ||
| - type: textarea | ||
| id: task | ||
| attributes: | ||
| label: 任务 | ||
| description: 一段话说清本卡要交付什么 | ||
| validations: | ||
| required: true | ||
| - type: textarea | ||
| id: ac | ||
| attributes: | ||
| label: AC(Given-When-Then) | ||
| description: 逐条 id + given/when/then——验收谓词的锚点 | ||
| validations: | ||
| required: true | ||
| - type: textarea | ||
| id: blast | ||
| attributes: | ||
| label: blastRadius(预测) | ||
| description: 预计触碰面(仓:路径 列表) | ||
| validations: | ||
| required: true | ||
| - type: textarea | ||
| id: budget | ||
| attributes: | ||
| label: budget(波次预算) | ||
| description: >- | ||
| 四元组+on_exceed(YAML)。至少声明一项;on_exceed 缺省 hard-stop。 | ||
| cost-check 按统一账本 subject 聚合对账(BEH-07);human_minutes 暂无账本源(只报告)。 | ||
| placeholder: | | ||
| usd: 5.0 | ||
| tokens: 200000 | ||
| wallclock_sec: 7200 | ||
| human_minutes: 120 | ||
| on_exceed: hard-stop | ||
| - type: textarea | ||
| id: capabilities | ||
| attributes: | ||
| label: capabilities(能力 allowlist) | ||
| description: >- | ||
| allowlist 式引用,仅两形态:org-secret:<大写名>(org secret)或 | ||
| vault:<路径>(内网域 Vault——值永不进 Git)。 | ||
| placeholder: | | ||
| - org-secret:CNB_POOL_KEY | ||
| - vault:secret/wave/w2c1 | ||
| - type: textarea | ||
| id: evidence | ||
| attributes: | ||
| label: evidence(证据要求) | ||
| description: 本卡收口必须在本卡生命周期内落进统一账本的 action 列表 | ||
| placeholder: | | ||
| - gate.merge-verdict | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,146 @@ | ||
| #!/usr/bin/env bash | ||
| # test-wave-schema.sh —— 波次 schema v1 解析/校验/wave-check 自测(IR-0006 W2-C3 / 卡 #414) | ||
| # | ||
| # 覆盖(卡 AC 对应): | ||
| # parse:h2/h3(issue 表单产 h3)双形态块提取;缺块=空对象(缺省语义) | ||
| # validate:四元组数值/on_exceed 词表/capabilities 两形态 allowlist/evidence 非空 | ||
| # 负向:非法键/负值/坏 on_exceed/裸 secret 名 → exit 3(fail-closed) | ||
| # wave-check:按 subject.card 聚合(tenant 归因分离);超限 exit 4; | ||
| # 非法 budget 块=行级 error 不炸整批;无预算卡=跳过 | ||
| # 用法: bash governance/tests/test-wave-schema.sh(gate.yml 自动纳入) | ||
| set -uo pipefail | ||
| DIR="$(cd "$(dirname "$0")/../.." && pwd)" | ||
| WS="$DIR/governance/wave_schema.py" | ||
| FAILS=0 | ||
| pass() { echo "PASS $1"; } | ||
| fail() { echo "FAIL $1"; FAILS=$((FAILS+1)); } | ||
|
|
||
| TMP=$(mktemp -d); trap 'rm -rf "$TMP"' EXIT | ||
|
|
||
| # ---- 正向:h2 手写卡形态 ---- | ||
| cat >"$TMP/card-h2.md" <<'EOF' | ||
| > 父意图: #402 | Spec: `specs/IR-0006/spec.md` v1 | 波次: W2 | 依赖: W1 | ||
|
|
||
| ## 任务 | ||
| 调度器 v0。 | ||
|
|
||
| ## budget(波次预算) | ||
| usd: 5.0 | ||
| tokens: 200000 | ||
| wallclock_sec: 7200 | ||
| human_minutes: 120 | ||
| on_exceed: hard-stop | ||
|
|
||
| ## capabilities(能力 allowlist) | ||
| - org-secret:CNB_POOL_KEY | ||
| - vault:secret/wave/w2c1 | ||
|
|
||
| ## evidence(证据要求) | ||
| - gate.merge-verdict | ||
| EOF | ||
| python3 "$WS" validate --body-file "$TMP/card-h2.md" >/dev/null \ | ||
| && pass "validate h2 形态绿(三块齐全)" || fail "validate h2 形态" | ||
| OUT=$(python3 "$WS" parse --body-file "$TMP/card-h2.md" --card "Cloudbird-Software/.github#414") | ||
| python3 - "$OUT" <<'PYEOF' && pass "parse h2:wave-meta 三块齐全+card 回填" || fail "parse h2 断言" | ||
| import json, sys | ||
| m = json.loads(sys.argv[1]) | ||
| assert m["card"] == "Cloudbird-Software/.github#414", m | ||
| assert m["budget"]["usd"] == 5.0 and m["budget"]["on_exceed"] == "hard-stop", m | ||
| assert m["capabilities"] == ["org-secret:CNB_POOL_KEY", "vault:secret/wave/w2c1"], m | ||
| assert m["evidence"] == ["gate.merge-verdict"], m | ||
| PYEOF | ||
|
|
||
| # ---- 正向:h3 issue 表单形态 + on_exceed 缺省 hard-stop ---- | ||
| cat >"$TMP/card-h3.md" <<'EOF' | ||
| ### 任务 | ||
| 某卡。 | ||
|
|
||
| ### budget(波次预算) | ||
| tokens: 1000 | ||
|
|
||
| ### capabilities(能力 allowlist) | ||
| - vault:secret/x | ||
| EOF | ||
| OUT=$(python3 "$WS" parse --body-file "$TMP/card-h3.md") | ||
| python3 - "$OUT" <<'PYEOF' && pass "parse h3:表单形态+缺省 on_exceed=hard-stop" || fail "parse h3 断言" | ||
| import json, sys | ||
| m = json.loads(sys.argv[1]) | ||
| assert m["budget"] == {"tokens": 1000, "on_exceed": "hard-stop"}, m | ||
| assert "evidence" not in m, m | ||
| PYEOF | ||
|
|
||
| # ---- 缺块=空对象(缺省语义)---- | ||
| cat >"$TMP/card-none.md" <<'EOF' | ||
| > 父意图: #402 | ||
|
|
||
| ## 任务 | ||
| 无预算卡。 | ||
| EOF | ||
| OUT=$(python3 "$WS" parse --body-file "$TMP/card-none.md") | ||
| [[ "$OUT" == "{}" ]] && pass "缺块 → 空对象(无预算约束缺省语义)" || fail "缺块应空对象:$OUT" | ||
|
|
||
| # ---- 负向:非法形态逐项 exit 3 ---- | ||
| neg() { # neg <名> <内容> | ||
| printf '%s\n' "$2" >"$TMP/neg.md" | ||
| python3 "$WS" validate --body-file "$TMP/neg.md" >/dev/null 2>&1 | ||
| [[ $? -eq 3 ]] && pass "负向:$1 → exit 3" || fail "负向:$1 未拒绝" | ||
| } | ||
| neg "非法键" '## budget(波次预算) | ||
| usd: 1 | ||
| euro: 2' | ||
| neg "负值" '## budget(波次预算) | ||
| usd: -5' | ||
| neg "四元组全缺" '## budget(波次预算) | ||
| on_exceed: warn' | ||
| neg "坏 on_exceed" '## budget(波次预算) | ||
| tokens: 1 | ||
| on_exceed: explode' | ||
| neg "裸 secret 名(非 allowlist 形态)" '## capabilities(能力 allowlist) | ||
| - CNB_POOL_KEY' | ||
| neg "capabilities 非列表" '## capabilities(能力 allowlist) | ||
| foo: bar' | ||
| neg "evidence 空列表" '## evidence(证据要求) | ||
| []' | ||
| neg "budget 坏 YAML 映射" '## budget(波次预算) | ||
| - just | ||
| - list' | ||
|
|
||
| # ---- wave-check:统一账本按 subject 聚合 + 超限判定 ---- | ||
| cat >"$TMP/cards.json" <<'EOF' | ||
| [ | ||
| {"number": 500, "body": "## budget(波次预算)\nusd: 10.0\ntokens: 100000\non_exceed: hard-stop"}, | ||
| {"number": 501, "body": "## budget(波次预算)\ntokens: 999999\non_exceed: warn"}, | ||
| {"number": 502, "body": "## 任务\n无预算卡"}, | ||
| {"number": 503, "body": "## budget(波次预算)\neuro: 1"} | ||
| ] | ||
| EOF | ||
| mkdir -p "$TMP/ledger" | ||
| cat >"$TMP/ledger/shadow-evidence-2026-W35.jsonl" <<'EOF' | ||
| {"ts":"2026-08-29T01:00:00Z","kind":"cost","action":"cost.dispatch-burst","verdict":"pass","subject":{"card":"Cloudbird-Software/.github#500","tenant":"cloudbird-internal"},"actor":{"identity":"x","role":"bot","model":null},"cost":{"tokens":40000,"usd":4.0,"wall_sec":600.0},"seq":1,"prev_hash":null,"hash":"aa"} | ||
| {"ts":"2026-08-29T02:00:00Z","kind":"cost","action":"cost.dispatch-burst","verdict":"pass","subject":{"card":"Cloudbird-Software/.github#500","tenant":"tenant-b"},"actor":{"identity":"x","role":"bot","model":null},"cost":{"tokens":30000,"usd":7.5,"wall_sec":100.0},"seq":2,"prev_hash":"aa","hash":"bb"} | ||
| {"ts":"2026-08-29T03:00:00Z","kind":"cost","action":"cost.dispatch-burst","verdict":"pass","subject":{"card":"Cloudbird-Software/.github#501","tenant":"cloudbird-internal"},"actor":{"identity":"x","role":"bot","model":null},"cost":{"tokens":500,"usd":0.1,"wall_sec":10.0},"seq":3,"prev_hash":"bb","hash":"cc"} | ||
| EOF | ||
| OUT=$(python3 "$WS" wave-check --cards "$TMP/cards.json" --ledger-dir "$TMP/ledger" 2>"$TMP/wc.err"); RC=$? | ||
| python3 - "$OUT" <<'PYEOF' && pass "wave-check 聚合断言(tenant 分离+非法块行级 error+无预算跳过)" || fail "wave-check 聚合断言" | ||
| import json, sys | ||
| rows = {r["card"]: r for r in json.loads(sys.argv[1])} | ||
| c500 = rows["Cloudbird-Software/.github#500"] | ||
| assert c500["usage_by_tenant"]["cloudbird-internal"]["usd"] == 4.0, c500 | ||
| assert c500["usage_by_tenant"]["tenant-b"]["usd"] == 7.5, c500 | ||
| assert c500["usage_total"] == {"usd": 11.5, "tokens": 70000, "wall_sec": 700.0}, c500 | ||
| assert c500["exceeded_dims"] == ["usd"], c500 | ||
| c501 = rows["Cloudbird-Software/.github#501"] | ||
| assert c501["exceeded_dims"] == [] and c501["on_exceed"] == "warn", c501 | ||
| assert "error" in rows["Cloudbird-Software/.github#503"], rows | ||
| assert "Cloudbird-Software/.github#502" not in rows, rows | ||
| PYEOF | ||
| [[ $RC -eq 4 ]] && pass "hard-stop 卡超限 → exit 4(BEH-07 熔断触发位)" || fail "超限应 exit 4(rc=$RC)" | ||
|
|
||
| # 无账本目录 → 全零用量不超限 | ||
| OUT=$(python3 "$WS" wave-check --cards "$TMP/cards.json" --ledger-dir "$TMP/no-such-dir" 2>/dev/null); RC=$? | ||
| [[ $RC -eq 0 ]] && grep -q '"exceeded_dims": \[\]' <<<"$OUT" \ | ||
| && pass "账本目录缺失 → 零用量不误熔断" || fail "空账本误判(rc=$RC)" | ||
|
|
||
| echo "----------------------------------------" | ||
| if [[ $FAILS -eq 0 ]]; then echo "test-wave-schema: PASS"; exit 0; fi | ||
| echo "test-wave-schema: $FAILS 处失败"; exit 1 |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
修复 Issue Form 的 YAML 语法。
Line 17 的 plain scalar 中
父意图:后有空格。YAML 将其解释为映射分隔符并拒绝整个模板。GitHub 将无法加载此工作卡表单。请使用块标量或引号包裹描述文本。建议修改
📝 Committable suggestion
🧰 Tools
🪛 YAMLlint (1.37.1)
[error] 17-17: syntax error: mapping values are not allowed here
(syntax)
🤖 Prompt for AI Agents
Source: Linters/SAST tools