Skip to content

feat(registry): verifier 执照 schema+注册校验(W5-C3 .github#226,ADR-0072) - #82

Merged
randypanding merged 1 commit into
mainfrom
w5c3-registry
Aug 21, 2026
Merged

feat(registry): verifier 执照 schema+注册校验(W5-C3 .github#226,ADR-0072)#82
randypanding merged 1 commit into
mainfrom
w5c3-registry

Conversation

@randypanding

Copy link
Copy Markdown
Contributor

动机

宪法 §4C 持证上岗 / ADR-0072 决策 2/6:考试通过才有 registry 条目——执照面=
registry 的 verifier 声明结构 + 注册校验脚本。配套 PR:CI-Workflows w5c3-verifier-exam
(考试 CI 与成绩存档)、archive w5c3-evalsets(考试集冻结正本)。

变更清单(全部 append,不动既有条目)

  • registry/schemas/verifier-license.json:执照声明契约(新增 schema)。必填:
    成绩引用块(archive_key/exam_version/prompt_hash/frozen_exam_sha256/overall_pass/judge_mode=api)、
    标注负债申报块(annual_hours≥1+committed+covers)、enforcement(veto 必须 false=shadow 起步)。
  • scripts/verifier-license.py:注册校验器(C1 面)——条目必须对上 CI-Workflows 成绩存档
    JSONL(键=judge_id@exam_version@prompt_hash12,逐字段核验);replay 回放成绩不可注册
    未配标注预算/新发 veto=true 一律拒绝;--self-test 内置 10 组正负向断言。
  • registry/verifiers/README.md:执照面说明(首版无条目——尚无真实判官通过 api 模式考试)。
  • tests/test_verifier_license.py:10 例(正例放行 + 逐项缺陷注入必拒 + CLI fail-closed)。
  • tests/golden/declarations.json:快照同步(新增 1 个声明文件的语义面)。

AC 映射(.github#226)

  • AC-1(注册侧) Given 新 judge 候选 When 考试 CI 运行 Then 考试集全过才注册进
    agent-registry——本 PR 是"才注册"的执行点:verifier-license.py 对照成绩存档核验
    test_missing_archive_rejected:无成绩=拒;test_failed_exam_rejected:分项不过=拒;
    test_replay_result_rejected:回放成绩=拒)。
  • AC-4(负债) 标注负债有申报记录——schema 必填 annotation_budget +
    rubric.annotation_debt(insufficient-data+reason 结构化),未配预算不许上岗(§10.4)。

测试方法(本地已跑,零网络)

  • python -m pytest tests/test_verifier_license.py -v → 10 例全绿;
    python scripts/verifier-license.py --self-test → 10 组 PASS。
  • 全仓门禁预跑:scripts/validate.py OK(tools=5 skills=2 agents=9 teams=3 models=5);
    scripts/snapshot.py --check OK(62 个声明文件);python -m pytest tests/ 70 passed;
    scripts/simulate-wave.py exit 0。

风险与回滚

  • 风险:成绩存档暂在 CI artifact(90 天 retention)→ 执照登记需先下载 artifact 再校验
    (README 有操作说明);长期存档转仓库为 follow-up。
  • 回滚:纯 append(1 schema+1 脚本+1 测试+1 README+golden 同步),revert 无连带影响;
    registry 既有条目零改动。

Card: Cloudbird-Software/.github#226
ADR: ADR-0072(archive adr/ADR-0072-verifier-entrance-exam-calibration.md

registry/schemas/verifier-license.json(执照声明契约:考试成绩引用/标注负债
申报/shadow 起步必填)+ scripts/verifier-license.py(考试通过才有条目——对照
CI-Workflows 成绩存档核验;replay 成绩不可注册)+ registry/verifiers/ 执照面
(首版无条目)+ tests 10 例正负向 + golden 快照同步。
Copilot AI lite review requested due to automatic review settings August 21, 2026 20:18
@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@randypanding, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 6 minutes

Limit details: You’ve used all 10 included reviews currently available.

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

Wait for the limit to reset, then comment @coderabbitai review or push new commits to the PR.

An organization admin can change what happens after included review limits in Billing.

How do review limits work?

CodeRabbit enforces per-developer PR review limits within each organization.

For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 60242b82-075f-4ff7-b359-1a648f04f368

📥 Commits

Reviewing files that changed from the base of the PR and between 2346987 and 80cbcb6.

📒 Files selected for processing (5)
  • registry/schemas/verifier-license.json
  • registry/verifiers/README.md
  • scripts/verifier-license.py
  • tests/golden/declarations.json
  • tests/test_verifier_license.py

Comment @coderabbitai help to get the list of available commands.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Registry: add verifier license schema and exam-backed registration validator

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 40+ Minutes

Grey Divider

AI Description

• Add verifier license JSON schema requiring exam proof, annotation budget, and shadow-only
 enforcement.
• Introduce verifier-license.py to cross-check registry entries against CI exam-result archives;
 reject replays.
• Add README + pytest coverage and update golden snapshot to lock the new declaration semantics.
Diagram

graph TD
  entry_yaml["registry/verifiers/*.yaml"] --> validator["scripts/verifier-license.py"] --> gate["CI validate/gate"]
  schema_json["verifier-license schema"] --> validator
  results_db[("Exam results JSONL") ] --> validator
  tests_py["tests/test_verifier_license.py"] --> validator
  readme_md["registry/verifiers/README.md"] --> entry_yaml
  subgraph Legend
    direction LR
    _file["File"] ~~~ _proc["Validator"] ~~~ _db[("Artifact/Data")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Fold verifier-license checks into scripts/validate.py
  • ➕ Single canonical entrypoint for all registry validation in CI
  • ➕ Avoids separate CLI discovery/usage overhead
  • ➖ Mixes cross-artifact exam verification logic into a broader validator, increasing coupling and review surface
  • ➖ Harder to run/iterate locally when results artifacts are optional inputs
2. Use jsonschema for structural validation + keep custom provenance checks
  • ➕ Makes schema compliance enforcement explicit and reusable
  • ➕ Reduces hand-written required-field checks and edge cases
  • ➖ Adds a runtime dependency/behavior surface (draft/version nuances)
  • ➖ Still requires custom logic for exam JSONL cross-checking and anti-replay enforcement
3. Store exam results in-repo (or dedicated archive repo) and validate without manual artifact download
  • ➕ Deterministic, long-lived provenance; easier reviewer verification
  • ➕ Removes reliance on CI artifact retention windows
  • ➖ Potentially large data footprint and governance considerations for result publication
  • ➖ Requires additional process/automation changes beyond this PR’s scope

Recommendation: Current approach (schema + dedicated validator CLI + tests) is appropriate because the core requirement is cross-checking against external exam-result archives, which naturally lives outside pure schema validation. Consider a follow-up to (1) optionally invoke the validator from the main validate pipeline for uniform CI wiring, and (2) migrate result storage out of ephemeral CI artifacts to a durable archive once the workflow stabilizes.

Files changed (5) +545 / -1

Enhancement (2) +394 / -0
verifier-license.jsonAdd verifier license declaration JSON Schema +165/-0

Add verifier license declaration JSON Schema

• Introduces a new Draft 2020-12 JSON Schema defining the verifier-license contract. Enforces required exam provenance (archive key/version/hash, overall_pass=true, judge_mode=api), mandatory annotation budget/debt declaration, and enforcement fields (shadow-first semantics).

registry/schemas/verifier-license.json

verifier-license.pyAdd fail-closed verifier license registration validator CLI +229/-0

Add fail-closed verifier license registration validator CLI

• Adds a CLI that validates verifier license entries (YAML) by cross-checking them against a CI-produced exam results JSONL archive keyed by judge_id@exam_version@prompt_hash12. Enforces anti-replay (judge_mode must be api), exam pass requirements, mandatory annotation budget, and shadow-only issuance (veto=false), plus a built-in self-test suite for local verification.

scripts/verifier-license.py

Tests (2) +133 / -1
test_verifier_license.pyAdd pytest coverage for verifier-license validator allow/deny rules +132/-0

Add pytest coverage for verifier-license validator allow/deny rules

• Adds a focused test suite that exercises positive registration and a set of negative cases (missing archive, failed exam, replay results, hash mismatch, missing budget, veto shortcut, invalid key format). Also asserts fail-closed CLI behavior when --results is absent and checks the new schema file is well-formed and semantically constrained (api-only judge_mode, enforcement/budget required).

tests/test_verifier_license.py

declarations.jsonUpdate golden declarations snapshot to include verifier-license schema +1/-1

Update golden declarations snapshot to include verifier-license schema

• Refreshes the golden snapshot so the declarations registry now includes the newly added verifier-license schema document, keeping snapshot-based validation in sync with the appended contract.

tests/golden/declarations.json

Documentation (1) +18 / -0
README.mdDocument the verifier license registry face and validation workflow +18/-0

Document the verifier license registry face and validation workflow

• Adds operator-facing documentation describing how verifier license entries are registered, which schema applies, and how to run the validator against downloaded CI artifacts. Clarifies that the initial directory contains no entries until a real API-mode exam pass exists.

registry/verifiers/README.md

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a “verifier license” contract and enforcement point so that new registry/verifiers/*.yaml entries can only be registered when they can be mechanically reconciled against archived CI exam results (ADR-0072 / W5-C3 governance requirement).

Changes:

  • Add registry/schemas/verifier-license.json to define the license declaration contract (exam result reference + annotation budget + shadow enforcement).
  • Add scripts/verifier-license.py to validate license entries by cross-checking them against a JSONL exam-results archive (including a --self-test mode).
  • Add regression tests + golden snapshot update, plus an initial README for registry/verifiers/.

Reviewed changes

Copilot reviewed 4 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
scripts/verifier-license.py New CLI validator that enforces “pass exam before registration” by reconciling YAML entries against JSONL result records.
registry/schemas/verifier-license.json New JSON Schema defining the verifier license declaration shape and required fields.
registry/verifiers/README.md Documentation for the new verifier license registry directory and how to validate entries.
tests/test_verifier_license.py Test suite covering positive/negative validation cases and CLI fail-closed behavior.
tests/golden/declarations.json Golden snapshot updated to include the newly added schema declaration.
Suppressed comments (1)

scripts/verifier-license.py:77

  • 成绩对账目前未核验 model_alias,这会允许用同一 judge_id 的其它模型成绩存档来注册执照条目(条目自报的 model_alias 与成绩不一致也不会被拒)。建议补充对 rec.model_aliasentry.model_alias 的逐字段校验。
    if rec.get("judge_id") != entry.get("judge_id"):
        fail(f"{eid}: 成绩 judge_id 不符({rec.get('judge_id')})")
    if rec.get("exam_version") != exam.get("exam_version"):
        fail(f"{eid}: 成绩 exam_version 不符({rec.get('exam_version')})")

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment on lines +147 to +150
for ep in entries:
entry = yaml.safe_load(Path(ep).read_text(encoding="utf-8"))
validate_entry(entry, results)
print(f"校验 {ep}: {'OK' if not errors else 'REJECTED'}")
Comment on lines +12 to +16
"issued_at",
"exam",
"annotation_budget",
"enforcement"
],
Comment on lines +65 to +68
exam = entry.get("exam") or {}
# ---- 核心规则:考试通过才有条目(成绩存档对账)----
key = exam.get("archive_key") or entry.get("license_id")
rec = results.get(key)
@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (5) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Schema misses enforced rules 🐞 Bug ≡ Correctness
Description
registry/schemas/verifier-license.json does not require rubric (but the validator rejects missing
rubric), and it does not constrain enforcement.veto to false even though the validator requires
shadow start.
Code

registry/schemas/verifier-license.json[R8-16]

+  "required": [
+    "license_id",
+    "judge_id",
+    "model_alias",
+    "issued_at",
+    "exam",
+    "annotation_budget",
+    "enforcement"
+  ],
Relevance

●●● Strong

Accepted precedents favor aligning schemas with validator-enforced invariants and preventing
fail-open contracts.

PR-#5
PR-#21

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
Schema required does not include rubric and enforcement.veto has no const:false, while the
validator explicitly fails when rubric keys are missing and when veto is not false.

registry/schemas/verifier-license.json[8-16]
registry/schemas/verifier-license.json[109-147]
scripts/verifier-license.py[102-115]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The JSON schema and the registrar validator disagree:
- The schema’s top-level `required` list omits `rubric`, but `scripts/verifier-license.py` treats rubric fields as mandatory and rejects entries without them.
- The schema allows any boolean for `enforcement.veto`, but the validator requires `false` for new licenses.

This contract drift means schema-only validation and registrar validation can disagree, causing confusing failures and weakening the schema as a normative contract.

### Issue Context
The PR description calls out rubric.annotation_debt and shadow discipline as required semantics. The schema already uses `const` for `exam.overall_pass`, so it can encode `enforcement.veto` similarly.

### Fix Focus Areas
- registry/schemas/verifier-license.json[8-16]
- registry/schemas/verifier-license.json[109-149]
- scripts/verifier-license.py[102-115]

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


2. License key mismatch allowed 🐞 Bug ≡ Correctness
Description
scripts/verifier-license.py does not verify that license_id’s exam_version/prompt_hash match
exam.archive_key / the archived exam record; it only checks the judge_id portion, so inconsistent
license_id values can be registered while still “passing” against a different archive_key.
Code

scripts/verifier-license.py[R58-68]

+    m = KEY_RE.match(entry.get("license_id", ""))
+    if not m:
+        fail(f"{eid}: license_id 须为 judge_id@exam_version@prompt_hash12 形式")
+        return
+    if m.group("judge") != entry.get("judge_id"):
+        fail(f"{eid}: license_id 的 judge 侧与 judge_id 不一致")
+
+    exam = entry.get("exam") or {}
+    # ---- 核心规则:考试通过才有条目(成绩存档对账)----
+    key = exam.get("archive_key") or entry.get("license_id")
+    rec = results.get(key)
Relevance

●● Moderate

Identifier consistency is plausible correctness hardening, but historical evidence lacks a close
archive-key mismatch precedent.

PR-#10
PR-#21

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The validator only compares the judge component from license_id to judge_id and never compares
license_id’s version/hash to exam.exam_version/exam.prompt_hash or to exam.archive_key; it
uses exam.archive_key to select the record, so a mismatched license_id can still validate. The
schema descriptions indicate these should be identical.

scripts/verifier-license.py[58-79]
registry/schemas/verifier-license.json[8-21]
registry/schemas/verifier-license.json[47-50]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`scripts/verifier-license.py` validates `license_id` format but only cross-checks the `judge` part against `judge_id`. It then looks up results by `exam.archive_key` (falling back to `license_id`) without enforcing:
- `exam.archive_key == license_id`
- `license_id`’s `exam_version` equals `exam.exam_version`
- `license_id`’s `prompt_hash12` equals `exam.prompt_hash[:12]` and the archived record’s `prompt_hash[:12]`

This allows a YAML entry to present a misleading/incorrect `license_id` while still passing validation by pointing `exam.archive_key` at a different archived record.

### Issue Context
The schema text describes `license_id` must match the archive key (`judge_id@exam_version@prompt_hash12`), so the validator should enforce that as a core integrity rule.

### Fix Focus Areas
- scripts/verifier-license.py[53-83]
- registry/schemas/verifier-license.json[8-50]

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


3. Model alias not verified 🐞 Bug ⛨ Security
Description
scripts/verifier-license.py requires model_alias on the entry but never checks it against the
archived exam record, allowing a license entry to claim a different model than the one that actually
passed the exam.
Code

scripts/verifier-license.py[R31-33]

+REQUIRED_TOP = ("license_id", "judge_id", "model_alias", "issued_at", "exam",
+                "annotation_budget", "enforcement")
+HEX64 = re.compile(r"^[0-9a-f]{64}$")
Relevance

●● Moderate

Alias consistency checks are accepted, but no close precedent requires cross-checking archived exam
identity.

PR-#3
PR-#10

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
model_alias is listed in REQUIRED_TOP, but the subsequent record-vs-entry comparisons do not
include it.

scripts/verifier-license.py[31-33]
scripts/verifier-license.py[72-79]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The validator treats `model_alias` as required, but it never compares `entry["model_alias"]` to `rec["model_alias"]` from the archived exam results. This is an integrity gap: a registrant can misrepresent which model passed the exam.

### Issue Context
The script already compares multiple fields from the archived record (`judge_id`, `exam_version`, `prompt_hash`, `frozen_exam_sha256`, `judge_mode`, `overall_pass`). `model_alias` should be part of this “逐字段核验” set.

### Fix Focus Areas
- scripts/verifier-license.py[31-33]
- scripts/verifier-license.py[72-86]

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



Remediation recommended

4. Uncaught parse crashes 🐞 Bug ☼ Reliability
Description
scripts/verifier-license.py can crash on malformed JSONL results or malformed/empty YAML entries
(e.g., json.loads errors, missing archive_key, or yaml.safe_load returning None), producing stack
traces instead of structured fail-closed errors.
Code

scripts/verifier-license.py[R45-50]

+    for line in path.read_text(encoding="utf-8").splitlines():
+        if not line.strip():
+            continue
+        rec = json.loads(line)
+        out[rec["archive_key"]] = rec   # 同键多行=多次考试,最新覆盖(历史在行序里)
+    return out
Relevance

●●● Strong

Recent accepted findings explicitly require malformed containers and parser inputs to fail through
controlled diagnostics.

PR-#10

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
json.loads and direct indexing rec["archive_key"] are unguarded; YAML is loaded and immediately
treated as a dict without type checks.

scripts/verifier-license.py[42-50]
scripts/verifier-license.py[147-150]
scripts/verifier-license.py[53-55]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The script assumes inputs are well-formed:
- `load_results()` calls `json.loads()` and then `rec["archive_key"]` without exception handling.
- `main()` passes the result of `yaml.safe_load()` directly into `validate_entry()`, which assumes a dict (`entry.get(...)`). Empty YAML yields `None` and will raise `AttributeError`.

This can crash the validator instead of emitting clear `::error::...` messages.

### Issue Context
The script is used as a gate-like validator (“fail-closed”); crashing is still non-zero but makes diagnosis harder and can mask multiple errors.

### Fix Focus Areas
- scripts/verifier-license.py[42-50]
- scripts/verifier-license.py[147-155]
- scripts/verifier-license.py[53-61]

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



Informational

5. Per-entry status misleading 🐞 Bug ◔ Observability
Description
When validating a directory, scripts/verifier-license.py prints each entry’s status based on the
global accumulated errors list, so once any entry fails, subsequent entries will be printed as
REJECTED even if they are valid.
Code

scripts/verifier-license.py[R147-151]

+    for ep in entries:
+        entry = yaml.safe_load(Path(ep).read_text(encoding="utf-8"))
+        validate_entry(entry, results)
+        print(f"校验 {ep}: {'OK' if not errors else 'REJECTED'}")
+    if errors:
Relevance

●●● Strong

This is a deterministic per-entry reporting bug, matching the team’s preference for precise
fail-closed validation behavior.

PR-#10
PR-#22

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The loop does not reset errors (or track a per-entry delta) before printing the status line.

scripts/verifier-license.py[135-155]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`errors` is a global list that accumulates across entries. The per-entry log line uses `not errors` to decide OK/REJECTED, so later entries inherit earlier failures and get printed as REJECTED regardless of their own validity.

### Issue Context
The overall exit code behavior (reject if any entry fails) is fine, but the per-entry line should reflect each entry’s own validation result.

### Fix Focus Areas
- scripts/verifier-license.py[135-155]

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


Grey Divider

Context sources
✅ Compliance rules (platform): 11 rules
Review mode: ⚖️ Balanced: This is a security- and contract-sensitive registry validation change with substantial new logic, but it is concentrated in one validator/schema path and is sufficiently reviewable in a careful single pass; the tests and documentation are supporting edits rather than multiple independent code paths.
ⓘ  4 issues published inline · 5 in summary

Grey Divider

Tip of the day
💡 Did you know, you can tweak Display preferences with a live preview to see your comment before it ships

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment on lines +58 to +68
m = KEY_RE.match(entry.get("license_id", ""))
if not m:
fail(f"{eid}: license_id 须为 judge_id@exam_version@prompt_hash12 形式")
return
if m.group("judge") != entry.get("judge_id"):
fail(f"{eid}: license_id 的 judge 侧与 judge_id 不一致")

exam = entry.get("exam") or {}
# ---- 核心规则:考试通过才有条目(成绩存档对账)----
key = exam.get("archive_key") or entry.get("license_id")
rec = results.get(key)

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. License key mismatch allowed 🐞 Bug ≡ Correctness

scripts/verifier-license.py does not verify that license_id’s exam_version/prompt_hash match
exam.archive_key / the archived exam record; it only checks the judge_id portion, so inconsistent
license_id values can be registered while still “passing” against a different archive_key.
Agent Prompt
### Issue description
`scripts/verifier-license.py` validates `license_id` format but only cross-checks the `judge` part against `judge_id`. It then looks up results by `exam.archive_key` (falling back to `license_id`) without enforcing:
- `exam.archive_key == license_id`
- `license_id`’s `exam_version` equals `exam.exam_version`
- `license_id`’s `prompt_hash12` equals `exam.prompt_hash[:12]` and the archived record’s `prompt_hash[:12]`

This allows a YAML entry to present a misleading/incorrect `license_id` while still passing validation by pointing `exam.archive_key` at a different archived record.

### Issue Context
The schema text describes `license_id` must match the archive key (`judge_id@exam_version@prompt_hash12`), so the validator should enforce that as a core integrity rule.

### Fix Focus Areas
- scripts/verifier-license.py[53-83]
- registry/schemas/verifier-license.json[8-50]

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

Comment on lines +31 to +33
REQUIRED_TOP = ("license_id", "judge_id", "model_alias", "issued_at", "exam",
"annotation_budget", "enforcement")
HEX64 = re.compile(r"^[0-9a-f]{64}$")

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. Model alias not verified 🐞 Bug ⛨ Security

scripts/verifier-license.py requires model_alias on the entry but never checks it against the
archived exam record, allowing a license entry to claim a different model than the one that actually
passed the exam.
Agent Prompt
### Issue description
The validator treats `model_alias` as required, but it never compares `entry["model_alias"]` to `rec["model_alias"]` from the archived exam results. This is an integrity gap: a registrant can misrepresent which model passed the exam.

### Issue Context
The script already compares multiple fields from the archived record (`judge_id`, `exam_version`, `prompt_hash`, `frozen_exam_sha256`, `judge_mode`, `overall_pass`). `model_alias` should be part of this “逐字段核验” set.

### Fix Focus Areas
- scripts/verifier-license.py[31-33]
- scripts/verifier-license.py[72-86]

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

Comment on lines +8 to +16
"required": [
"license_id",
"judge_id",
"model_alias",
"issued_at",
"exam",
"annotation_budget",
"enforcement"
],

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. Schema misses enforced rules 🐞 Bug ≡ Correctness

registry/schemas/verifier-license.json does not require rubric (but the validator rejects missing
rubric), and it does not constrain enforcement.veto to false even though the validator requires
shadow start.
Agent Prompt
### Issue description
The JSON schema and the registrar validator disagree:
- The schema’s top-level `required` list omits `rubric`, but `scripts/verifier-license.py` treats rubric fields as mandatory and rejects entries without them.
- The schema allows any boolean for `enforcement.veto`, but the validator requires `false` for new licenses.

This contract drift means schema-only validation and registrar validation can disagree, causing confusing failures and weakening the schema as a normative contract.

### Issue Context
The PR description calls out rubric.annotation_debt and shadow discipline as required semantics. The schema already uses `const` for `exam.overall_pass`, so it can encode `enforcement.veto` similarly.

### Fix Focus Areas
- registry/schemas/verifier-license.json[8-16]
- registry/schemas/verifier-license.json[109-149]
- scripts/verifier-license.py[102-115]

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

Comment on lines +45 to +50
for line in path.read_text(encoding="utf-8").splitlines():
if not line.strip():
continue
rec = json.loads(line)
out[rec["archive_key"]] = rec # 同键多行=多次考试,最新覆盖(历史在行序里)
return out

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

4. Uncaught parse crashes 🐞 Bug ☼ Reliability

scripts/verifier-license.py can crash on malformed JSONL results or malformed/empty YAML entries
(e.g., json.loads errors, missing archive_key, or yaml.safe_load returning None), producing stack
traces instead of structured fail-closed errors.
Agent Prompt
### Issue description
The script assumes inputs are well-formed:
- `load_results()` calls `json.loads()` and then `rec["archive_key"]` without exception handling.
- `main()` passes the result of `yaml.safe_load()` directly into `validate_entry()`, which assumes a dict (`entry.get(...)`). Empty YAML yields `None` and will raise `AttributeError`.

This can crash the validator instead of emitting clear `::error::...` messages.

### Issue Context
The script is used as a gate-like validator (“fail-closed”); crashing is still non-zero but makes diagnosis harder and can mask multiple errors.

### Fix Focus Areas
- scripts/verifier-license.py[42-50]
- scripts/verifier-license.py[147-155]
- scripts/verifier-license.py[53-61]

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

@randypanding
randypanding added this pull request to the merge queue Aug 21, 2026
Merged via the queue into main with commit b8c5ec2 Aug 21, 2026
14 checks passed
@randypanding
randypanding deleted the w5c3-registry branch August 21, 2026 20:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants