Skip to content

Close the v0.6.3 reward-verifier, trust-boundary, sidecar and streaming gaps - #46

Merged
DaoyuanLi2816 merged 4 commits into
mainfrom
v0.6.3-final-release-fix
Aug 6, 2026
Merged

Close the v0.6.3 reward-verifier, trust-boundary, sidecar and streaming gaps#46
DaoyuanLi2816 merged 4 commits into
mainfrom
v0.6.3-final-release-fix

Conversation

@DaoyuanLi2816

Copy link
Copy Markdown
Owner

Final correctness and trust-boundary pass before v0.6.3 is tagged. Every
defect below was reproduced against b769251 first; the reproducers are kept as
regressions.

The static reward verifier accepted definition-time code

PR #45 stopped bridge doctor importing an untrusted reward scaffold, but the
static policy only audited decorators and default arguments. Everything else
that runs when a module is imported passed with status: ok, findings: [] and
the strongest level:

def exploit():
    Path("PWNED.txt").write_text("executed")
    return object

class Hidden(exploit()):          # accepted, no finding
    pass

Class bases, metaclass= and other class keywords, parameter and return
annotations, annotated-assignment annotations and Python 3.12 type-parameter
bounds are now audited. Only expressions that actually evaluate — calls,
lambdas, comprehensions, await, walrus — are rejected, so ordinary
annotations such as -> float and bases such as (object) still pass.

Keyword-only parameters were never inspected either, so this passed as a
verified interface despite raising TypeError on verl's three-argument call:

def compute_score(data_source, solution_str, ground_truth, *, extra_info): ...

The strongest default level is renamed interface_statically_verified
interface_shape_verified: the old name read as a safety claim the check
cannot make. Imports are listed with import_runtime_safety: not_verified,
relative bundle-local imports are refused, and source bytes, AST nodes, AST
depth and finding count are bounded so a hostile file yields a bounded
diagnostic. 20 new regressions; none of them ever creates the marker file.

A bundle's claims were reported as results

The doctor read upstream_config_parse_passed, distributed_execution_tested
and algorithm_semantic_parity out of the bundle's own
compatibility-report.json and exposed them as top-level fields.
provenance/SHA256SUMS ships inside the bundle it describes, so anyone who
edits a claim can reseal it — the test builds exactly that bundle.

Output is now split into bundle_declared_claims (testimony,
trust: unsigned_self_consistent), locally_recomputed_checks (this process,
against bytes on disk) and provenance_trust
(signature_verification: not_available). Top-level flags reflect only what was
recomputed. --require-verl now performs the upstream OmegaConf parse and
structured merge locally instead of comparing an installed commit id.

Portable metadata privacy ran one regex

A manifest holding an API key, a bearer token and a database URL with inline
credentials passed, because the only detector was an absolute-path pattern.
Structured JSON/YAML is now walked so a finding names a JSON path
($.nested.credentials.api_key), unstructured text reports a line number, and
the scan is bounded by file size, total bytes and finding count. Matched text is
still never returned. Status is heuristic_passed/heuristic_failed — it is a
detector, not de-identification proof.

Malformed sidecars were read as empty

{}, {"rows": []}, a wrong namespace and schema_version: 999 were all
accepted as "this dataset has no extensions", silently discarding the provenance
the sidecar exists to carry. Schema version, exact namespace, a rows mapping,
canonical integer keys within the source row count, JSON-compatible values,
unknown top-level fields and optional source_sha256/source_rows binding are
now validated. Sidecars published by 0.6.0–0.6.2 still read. Diagnostics name
the location, never the payload.

Conversion materialized the whole dataset

convert-dataset called pq.read_table(source).to_pylist(). It now streams
record batches through a ParquetWriter, with the output schema derived once
from the source schema so an optional nested field appearing only in a later
row group cannot produce a second incompatible schema. Publication still happens
only at commit, so strict conversion stays complete-or-nothing, and a rejected
row stops the run before the next row group is read — both proven by
instrumented tests that fail if read_table is called or if a later batch is
fetched.

Text integrity

docs/release-checklist.md shipped — not applicable and CHANGELOG.md
shipped base → SFT as GBK-mangled sequences; three files carried a BOM. The
result is valid UTF-8, so nothing ever raised. All repaired, and
scripts/check_text_integrity.py now gates CI on the mis-decoding leaders,
U+FFFD and unexpected BOMs. Its allowlist is exactly two files: the detector and
its own tests, which must spell the sequences out.

Validation

Gate Result
CPU (not gpu and not network) 1830 passed, 2 skipped, 6 deselected
Branch coverage 86.10%
GPU (RTX 4080) 5 passed
Network 3 passed
ruff / format / mypy clean, 107 source files
mkdocs build --strict clean
Browser visual gate 28 rendered SVG instances across 4 viewports
Frozen artifacts all byte-identical

No frozen scientific result, task JSONL or published adapter is touched. No new
benchmark, algorithm, distributed claim or OPD/PPO parity claim is added. Issue
#39 remains out of scope.

The static reward verifier audited only decorators and default arguments, so
every other definition-time expression passed with no finding at all: class
bases, metaclass and other class keywords, parameter and return annotations,
annotated-assignment annotations and type-parameter bounds. `class
Hidden(exploit())` was reported as a verified interface. Keyword-only
parameters were never inspected either, so a required keyword-only
`extra_info` -- which raises TypeError on verl's three-argument call -- passed
as correct.

Definition-time positions are now audited for expressions that would run where
they appear, while ordinary type annotations and base classes stay acceptable.
The strongest default level is renamed `interface_shape_verified`, because
`interface_statically_verified` read as a safety claim the check cannot make;
imports are recorded with `import_runtime_safety: not_verified`, relative
bundle-local imports are rejected, and source bytes, AST nodes, AST depth and
finding count are bounded so a hostile file yields a bounded diagnostic.

The doctor also read `upstream_config_parse_passed`,
`distributed_execution_tested` and `algorithm_semantic_parity` out of the
bundle's own compatibility report and exposed them as top-level results.
SHA256SUMS ships inside the bundle it describes, so anyone who edits a claim
can reseal it. Bundle testimony now lives under `bundle_declared_claims` with
an explicit `unsigned_self_consistent` trust level, the top-level flags reflect
only what this process recomputed, and `--require-verl` performs the upstream
parse/merge locally instead of comparing an installed commit id.

Portable metadata privacy ran a single absolute-path regex, so a manifest
holding an API key, a bearer token and a database URL with inline credentials
passed. Structured metadata is now walked so a finding can name a JSON path,
unstructured text reports a line number, and matched text is still never
returned.
…grity

An existing .miniverl.json sidecar was accepted whenever it happened to parse
as a JSON object. An empty object, a list-valued rows field, a wrong namespace
and an unsupported schema version all read as "this dataset has no extensions",
so the file that existed to carry miniVERL provenance silently discarded it. A
sidecar is now validated for schema version, exact namespace, a rows mapping,
canonical non-negative integer keys inside the source row count,
JSON-compatible values and unknown top-level fields, plus optional
source_sha256/source_rows binding. Sidecars published by 0.6.0-0.6.2 still
read. Diagnostics name the location and never the extension payload, which can
hold teacher targets.

Conversion called pq.read_table(source).to_pylist(), so a real verl dataset was
materialized twice -- once as Arrow, once as Python objects -- before the first
row was converted. It now streams record batches through a ParquetWriter into
the staging file, with the output schema derived once from the source schema so
an optional nested field appearing only in a later row group cannot produce a
second, incompatible schema. Publication still happens only at commit, so
strict conversion stays complete-or-nothing, and a rejected row now stops the
run before the next row group is read. Rejection counts stay exact while their
per-row detail is sampled.

The release checklist shipped an em dash and the changelog shipped arrows as
GBK-mangled CJK sequences: UTF-8 punctuation written back through a non-UTF-8
console. The result is valid UTF-8, so nothing ever raised. Three files also
carried a byte-order mark. All are repaired, and scripts/check_text_integrity.py
now fails CI on the mis-decoding leaders, on U+FFFD and on an unexpected BOM.
The bridge page now states what the reward check actually proves. The level is
named interface_shape_verified rather than interface_statically_verified,
because the old name read as a safety claim; the page lists every
definition-time position that is audited and says plainly that imported modules
are neither run nor proven safe.

A new section separates what a bundle declares from what a doctor run
recomputes, and states that a bundle's own SHA256SUMS can prove internal
consistency only. Both READMEs carry the same scope in English and Chinese.

Quality record: 1830 non-GPU/non-network tests, 2 skipped, 6 deselected, 86.10%
branch coverage, 5 GPU tests on the RTX 4080 and 3 network tests, measured at
e8a36db.
@DaoyuanLi2816
DaoyuanLi2816 merged commit 005a454 into main Aug 6, 2026
14 checks passed
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.

1 participant