Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions standards/evidence/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# 统一证据账本·标准(v1)

> IR-0006 W1-B1 / ADR-0103。判定层记录 schema:[record.schema.yaml](record.schema.yaml)
> (`$id: cloudbird/evidence-standard/record@1`)。

## 三层纪律(宪法 §14a / INV-06)

| 层 | 载体 | 内容 | 纪律 |
|---|---|---|---|
| 判定层 | archive 仓 `evidence/` | gate 裁决/成本/审批/决策四类事件(BEH-01) | append-only + 链式 hash(INV-03);payload 内联 ≤4096 字节,超限拒写 |
| 轨迹层 | 云内网 blob | 大体积原始数据 | git 侧只存 `payload_ref`(sha256+store+retention,W1-B3) |
| 丢弃层 | GitHub 事件面 | transient 事件 | 不承诺持久 |

## OTel gen_ai.* 映射(字段命名对齐语义约定)

| 本 schema 字段 | OTel 语义约定对应 | 说明 |
|---|---|---|
| `ts` | event timestamp(ISO 8601 UTC) | 事件时间戳 |
| `subject.card` | `gen_ai.conversation.id`(关联语义) | 卡 issue 三源统一 join key(AC-4) |
| `subject.commit` | `github.pull_request.head.sha`(拉 PR head) | 被判定对象 |
| `actor.identity` | `gen_ai.system`(责任主体语义) | 行为者 |
| `actor.model` | `gen_ai.request.model` | LLM 判定模型;非 LLM 判定为 null(INV-01 机械锚点) |
| `cost.tokens` | `gen_ai.usage.input_tokens`+`output_tokens` 汇总 | token 归账 |
| `cost.usd` / `cost.wall_sec` | metering 归账口径 | 成本/墙钟 |
| `inputs_digest` | provenance 锚点 | 判定输入摘要 sha256 |

命名对齐目的:W1-B2 三源(metering/butler/drill)按 schema v1 双写时可直接复用
OTel 采集面,无需字段翻译层。

## 写入与验证(执法面在 archive 仓)

- 写入:`archive scripts/write_evidence.py`——唯一合法写入器。执法项:
payload ≤4096 字节(UTF-8)超限拒写;`subject.tenant` 必填;append-only
(行内禁改,纠错追加 erratum 事件);prev_hash 链式续接。
- 验证:`archive scripts/verify_evidence.py`——独立复算(不信任写入器自报):
seq 连续、prev_hash 链、hash 重算、tenant 字段在场;任一断裂=红(fail-closed)。
- checkpoint:月度快照 `evidence/checkpoints/YYYY-MM.json`(当月链头 hash + 记录数),
验证脚本对账 checkpoint 与链头——不匹配=红(BEH-02)。

## 消费方

- cost-check 波次视图(W2-C3):`subject` 聚合自本账本。
- 三源统一查询(W1-B2):`subject.card` 为 join key。
- 飞书投影/SLI 周报(W3/W4):只读消费。
128 changes: 128 additions & 0 deletions standards/evidence/record.schema.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
$schema: "https://json-schema.org/draft/2020-12/schema"
$id: "cloudbird/evidence-standard/record@1"
title: Evidence Ledger Record (v1, IR-0006 W1-B1 / ADR-0103)
description: |
统一证据账本·判定层记录 schema v1。一切判定事件(gate 裁决/成本/审批/决策)
经统一发射器写 archive 仓 evidence/(BEH-01);append-only + 链式 hash
(INV-03,ADR-0062 平移);月度 checkpoint(BEH-02)。

三层纪律(宪法 §14a / INV-06):
- 判定层(本 schema):payload 内联上限 4096 字节(UTF-8 编码后),超限拒写;
- 轨迹层:大数据落云内网 blob,git 侧只存 payload_ref(sha256 指针)+保留策略字段;
- 丢弃层:GitHub 事件面,transient 不承诺持久。

字段命名对齐 OTel gen_ai.* 语义约定(映射表见同目录 README.md)。
消费方:archive scripts/write_evidence.py(写入执法:4KB/tenant/append-only/hash 链)
与 scripts/verify_evidence.py(独立复算:链断=红,fail-closed)。
type: object
required: [seq, ts, kind, action, verdict, subject, actor, prev_hash, hash]
properties:
seq:
type: integer
minimum: 1
description: "链内序号,从 1 连续递增(断号=红)"
ts:
type: string
pattern: "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(\\.[0-9]+)?Z$"
description: "ISO 8601 UTC(gen_ai 事件时间戳语义)"
Comment on lines +24 to +27

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

3. Invalid timestamps pass schema 🐞 Bug ≡ Correctness

The ts regex checks only digit placement, so impossible values such as 2026-99-99T99:99:99Z
validate even though the field is declared to be an ISO 8601 UTC timestamp. This undermines
chronological queries and the spec's planned timestamp property tests.
Agent Prompt
## Issue description
The timestamp regex accepts impossible calendar and clock values.

## Issue Context
The schema declares draft 2020-12 and the repository's event schema already uses `format: date-time`; UTC-only semantics still need the `Z` restriction.

## Fix Focus Areas
- standards/evidence/record.schema.yaml[24-27]

Use `format: date-time` with a validator configured to assert formats, retain an explicit UTC-only constraint, and add negative tests for invalid dates and times.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

kind:
enum: [gate, cost, approval, decision]
description: "判定事件四类(BEH-01):gate 裁决 / 成本 / 审批 / 决策"
action:
type: string
minLength: 1
description: "事件名(受控词表由发射器侧维护,如 gate.merge-verdict / cost.dispatch-burst)"
verdict:
type: string
minLength: 1
description: "判定结论(如 pass/fail/blocked/survived/insufficient——语义由 action 域定义)"
subject:
type: object
description: "判定对象四元组(IFACE-01);card 为三源统一查询 join key(AC-4)"
required: [card, tenant]
properties:
wave:
type: string
description: "波次(如 W1-B1);波次内事件可缺省 subject.wave(卡即波次成员)"
card:
type: string
pattern: "^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+#[0-9]+$"
description: "owner/repo#issue——卡绑定(≈ gen_ai.conversation.id 关联语义)"
tenant:
type: string
minLength: 1
description: "计量租户(必填,宪法 §14a:每条判定记录必含 tenant;多租户计量分离)"
commit:
type: string
pattern: "^[0-9a-f]{7,40}$"
description: "被判定对象 commit SHA(github 拉 PR head 语义)"
actor:
type: object
required: [identity, role]
properties:
identity:
type: string
minLength: 1
description: "行为者标识(GitHub login / App slug;≡ gen_ai.system 责任主体语义)"
role:
enum: [owner, agent, bot, human]
description: "行为者角色(conductor guard 同词表)"
model:
type:
- "null"
- string
description: "LLM 判定模型(≡ gen_ai.request.model;非 LLM 判定为 null——机械判定锚点 INV-01)"
cost:
type: object
description: "本次判定消耗(≡ gen_ai.usage.* 归账语义;无消耗可省略整节)"
properties:
tokens:
type: integer
minimum: 0
description: "token 总量(≡ gen_ai.usage.input_tokens+output_tokens 汇总)"
usd:
type: number
minimum: 0
description: "美元成本(metering 归账口径)"
wall_sec:
type: number
minimum: 0
description: "墙钟秒数"
inputs_digest:
type: string
# 校验执法在写入器(write_evidence.py):必须为 64 位十六进制 sha256
description: "判定输入摘要 sha256(provenance 锚点;witness 不可伪造性由链 hash 承担)"
Comment on lines +91 to +94

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

4. Provenance digest accepts garbage 🐞 Bug ≡ Correctness

inputs_digest is only typed as a string, so values such as not-a-digest pass schema validation
despite the field being defined as a 64-character SHA-256 provenance anchor. Invalid provenance
identifiers can consequently enter the ledger and cannot be used to correlate or verify decision
inputs.
Agent Prompt
## Issue description
`inputs_digest` accepts arbitrary strings instead of the documented SHA-256 representation.

## Issue Context
The intended 64-character hexadecimal rule is already stated in the adjacent comment and can be expressed directly in JSON Schema.

## Fix Focus Areas
- standards/evidence/record.schema.yaml[91-94]

Add the same `^[0-9a-f]{64}$` pattern used by the other SHA-256 fields and add positive/negative schema fixtures.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

payload:
type:
- "null"
- string
description: |
内联判定载荷(摘要/结论正文)。软上限 4096 字节(UTF-8 编码长度):
schema 层文档化,写入器硬执法(超限拒写,INV-06/AC-3);大数据走轨迹层
payload_ref。payload 与 payload_ref 可同时为空(纯判定事实记录)。
payload_ref:
type:
- "null"
- object
Comment on lines +103 to +106

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Payload limit bypass via ref 🐞 Bug ≡ Correctness

Because payload_ref does not reject unknown properties, a schema-valid record can place
arbitrarily large inline content in a field such as payload_ref.data, bypassing the stated rule
that Git stores only the pointer and that inline payload is capped at 4096 UTF-8 bytes. A writer
that enforces only the documented payload check will therefore admit the exact large trace data
this three-layer split is intended to keep out of Git.
Agent Prompt
## Issue description
`payload_ref` currently permits arbitrary extra properties, allowing large inline trace data to bypass the `payload` size guard and Git-storage discipline.

## Issue Context
JSON Schema's root `additionalProperties: false` does not apply recursively to nested objects.

## Fix Focus Areas
- standards/evidence/record.schema.yaml[103-118]

Add `additionalProperties: false` to `payload_ref` and tests proving unknown/inline-content fields are rejected.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

description: "轨迹层指针(W1-B3 协议):blob sha256+保留策略字段;git 侧零 payload 本体"
required: [sha256, store]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

5. Retention policy can be omitted 🐞 Bug ☼ Reliability

payload_ref.required omits retention, so a trajectory pointer without any retention policy
validates even though both the three-layer contract and INV-06 define the pointer as SHA-256 plus
location plus retention policy. Such records leave lifecycle enforcement unable to determine how
long referenced evidence must remain available.
Agent Prompt
## Issue description
A non-null `payload_ref` can omit the required retention-policy declaration.

## Issue Context
The README and governing spec define retention as part of every trajectory-layer pointer.

## Fix Focus Areas
- standards/evidence/record.schema.yaml[103-118]

Add `retention` to the nested `required` list and constrain it to the vocabulary that W1-B3 consumers will implement.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

properties:
sha256:
type: string
pattern: "^[0-9a-f]{64}$"
store:
type: string
description: "存储位置标识(如 self-cloud-blob://<bucket>/<key>)"
retention:
type: string
description: "保留策略(如 90d / 1y / forever——EL-2 执行面)"
prev_hash:
type:
- "null"
- string
description: "链前记录 hash(首条为 null;断链=红,fail-closed)"
Comment on lines +119 to +123

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Malformed chain links validate 🐞 Bug ≡ Correctness

prev_hash accepts any string and allows null at every sequence number, so records such as `seq:
2, prev_hash: null or prev_hash: x` pass the v1 schema despite not being valid SHA-256 chain
links. This allows malformed ledger records through contract validation before the later verifier
can detect them.
Agent Prompt
## Issue description
`prev_hash` accepts malformed strings and permits null on non-first records, contrary to the documented chain invariant.

## Issue Context
The schema can enforce the local constraints even though equality with the preceding record remains verifier responsibility.

## Fix Focus Areas
- standards/evidence/record.schema.yaml[119-127]
- standards/evidence/record.schema.yaml[17-18]

Require non-null values to match 64 lowercase hexadecimal characters, and add an `if`/`then` constraint making `prev_hash` null exactly when `seq` is 1.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

hash:
type: string
pattern: "^[0-9a-f]{64}$"
description: "本记录内容 hash(sha256,canonical JSON 序列化后计算——不含自身字段)"
Comment on lines +124 to +127

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

6. Hash bytes are undefined 🐞 Bug ☼ Reliability

The schema says to hash “canonical JSON” but defines neither a canonicalization standard nor the
exact UTF-8 bytes, leaving property ordering, number rendering, Unicode escaping, and
omitted-versus-null handling ambiguous. Independent writers and verifiers can therefore derive
different hashes for the same logical record and falsely report a broken chain.
Agent Prompt
## Issue description
The hash protocol does not define the exact byte representation to hash, so independent implementations need not produce the same digest.

## Issue Context
BEH-02 requires an independent verifier to recompute the chain from archived records.

## Fix Focus Areas
- standards/evidence/record.schema.yaml[124-127]
- standards/evidence/README.md[30-38]

Normatively select a canonicalization algorithm such as RFC 8785, specify UTF-8 encoding and exclusion of only the top-level `hash` member, and provide golden record/byte/digest vectors.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

additionalProperties: false