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
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ jobs:
python scripts/build_pypi_readme.py --check
python scripts/release_state.py --check
python scripts/check_markdown_links.py
python scripts/check_text_integrity.py
miniverl --help
miniverl --version
miniverl doctor --json > doctor.json
Expand Down
48 changes: 43 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,47 @@ All notable changes to miniVERL are recorded here. The format follows
they asked for a diagnosis, while the report described the result as a
"side-effect-free import". The scaffold is now parsed with `ast.parse` and
verified statically, reporting `not_present`, `syntax_valid`,
`interface_statically_verified` or `trusted_dynamic_import_verified`. Top-level
calls, non-literal assignments, decorators, class-body statements and
call-valued default arguments are rejected because each runs at import time.
`interface_shape_verified` or `trusted_dynamic_import_verified`. Every
definition-time position is audited, not only top-level statements: class
bases, `metaclass=` and other class keywords, parameter and return
annotations, annotated-assignment annotations, type-parameter bounds,
decorators and call-valued defaults all run when a module is imported, so
`class Hidden(exploit())` is rejected rather than reported as verified.
Keyword-only parameters are checked too, so a required keyword-only
`extra_info` — which raises `TypeError` on verl's three-argument call — no
longer passes. Source bytes, AST nodes, AST depth and finding count are
bounded, imports are listed with `import_runtime_safety: not_verified`, and
relative bundle-local imports are refused.
- `bridge doctor` separates what a bundle *says* from what this process
*recomputed*. `SHA256SUMS` ships inside the bundle it describes, so anyone who
edits a claim can reseal it; matching hashes prove internal consistency only.
Bundle testimony now appears under `bundle_declared_claims` with a
`provenance_trust` level of `unsigned_self_consistent`, the top-level flags
reflect only locally recomputed results, and `--require-verl` performs the
upstream OmegaConf parse and structured merge locally instead of comparing an
installed commit id.
- Portable metadata privacy ran one absolute-path regex, so a manifest holding
an API key, a bearer token or a database URL with inline credentials passed.
Structured JSON/YAML is now walked so a finding can name a JSON path,
unstructured text reports a line number, the scan is bounded by file size,
total bytes and finding count, and matched text is still never reported. The
status is named `heuristic_passed`/`heuristic_failed` because it is a
detector, not de-identification proof.
- An extension sidecar that exists but does not validate now fails the
conversion instead of being treated as absent. 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 all checked; sidecars published by
0.6.0–0.6.2 still read.
- `convert-dataset` streams record batches through a `ParquetWriter` instead of
calling `read_table(...).to_pylist()`, which materialized the entire dataset
before converting a row. The output schema is derived once from the source
schema, so an optional nested field that appears only in a later row group
cannot produce a second incompatible schema. Strict conversion remains
complete-or-nothing and now stops before reading the next row group.
- `scripts/check_text_integrity.py` fails CI on GBK-mangled UTF-8 punctuation,
U+FFFD and unintended byte-order marks. The release checklist's `— not
applicable` and the changelog's `base → SFT` arrows are repaired.
- Added `miniverl bridge doctor --trust-and-import-reward-code` for bundles you
produced yourself. It warns before executing anything, reports
`untrusted_code_executed: true`, and does not claim to be a sandbox.
Expand Down Expand Up @@ -210,7 +248,7 @@ All notable changes to miniVERL are recorded here. The format follows
provenance, a versioned verifier-gated selector, deterministic tool-policy
evaluation and privacy-safe JSON/Markdown Alignment Cards.
- A preregistered three-seed Alignment Lab result, 864 task-level records, a
matched State × Supervision diagnostic, four data-bound figures, technical
matched State Supervision diagnostic, four data-bound figures, technical
report, article and reproducible short demo.

### Changed
Expand Down Expand Up @@ -256,7 +294,7 @@ All notable changes to miniVERL are recorded here. The format follows

### Results

- Batch-4 improved end-to-end throughput by 1.63× for dual ownership and 1.54×
- Batch-4 improved end-to-end throughput by 1.63脳 for dual ownership and 1.54脳
for shared ownership on the declared Qwen3-0.6B workload. Shared batch-4 used
2.227 GiB peak reserved memory versus 3.035 GiB for dual, but was 10.1% slower.
- Identical trajectory and teacher-target digests held across all eight cells;
Expand Down
11 changes: 8 additions & 3 deletions PYPI.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,14 @@ with in-process rollback, not multi-file crash atomicity.

An exported bundle is untrusted input. `bridge doctor` inspects its reward
scaffold statically with `ast.parse` and **never executes it** unless you pass
`--trust-and-import-reward-code`; adapter weights are validated past the
header; and tokenizer, safetensors and privacy results each report how far
verification actually got rather than a single pass or fail.
`--trust-and-import-reward-code` — class bases, `metaclass=`, annotations and
type-parameter bounds are audited too, because all of them run at import.
Adapter weights are validated past the header, a malformed extension sidecar
fails the conversion instead of being read as empty, and dataset conversion
streams row groups rather than materializing the table. What a bundle *claims*
is reported separately from what was *recomputed* locally: its own `SHA256SUMS`
can only prove internal consistency. Tokenizer, safetensors and privacy results
each report how far verification actually got rather than a single pass or fail.

## One measured alignment result

Expand Down
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,14 @@ with in-process rollback, not multi-file crash atomicity.

An exported bundle is untrusted input. `bridge doctor` inspects its reward
scaffold statically with `ast.parse` and **never executes it** unless you pass
`--trust-and-import-reward-code`; adapter weights are validated past the
header; and tokenizer, safetensors and privacy results each report how far
verification actually got rather than a single pass or fail.
`--trust-and-import-reward-code` — class bases, `metaclass=`, annotations and
type-parameter bounds are audited too, because all of them run at import.
Adapter weights are validated past the header, a malformed extension sidecar
fails the conversion instead of being read as empty, and dataset conversion
streams row groups rather than materializing the table. What a bundle *claims*
is reported separately from what was *recomputed* locally: its own `SHA256SUMS`
can only prove internal consistency. Tokenizer, safetensors and privacy results
each report how far verification actually got rather than a single pass or fail.

## One measured alignment result

Expand Down
10 changes: 7 additions & 3 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,13 @@ calculator 环境,也不会创建身份不明确的同基座教师。未解析

导出的 bundle 属于不可信输入。`bridge doctor` 用 `ast.parse` 静态检查其
reward scaffold,**默认绝不执行它**,除非显式传入
`--trust-and-import-reward-code`;adapter 权重的校验会越过文件头验证实际
载荷;tokenizer、safetensors 与隐私三项各自报告验证真正到达的层级,而不是
笼统的通过或失败。
`--trust-and-import-reward-code`;基类、`metaclass=`、类型注解与类型参数
边界同样会被审查,因为它们都在 import 时求值。adapter 权重的校验会越过
文件头验证实际载荷;格式非法的 extension sidecar 会让转换失败,而不是被
当成空文件读过去;数据集转换按 row group 流式处理,不再整表物化。bundle
自己**声称**的内容与本地**实际重算**的结果分开报告:它自带的 `SHA256SUMS`
只能证明内部一致性。tokenizer、safetensors 与隐私三项各自报告验证真正
到达的层级,而不是笼统的通过或失败。

## 一项对齐实测结果

Expand Down
8 changes: 4 additions & 4 deletions docs/generated/quality.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
"schema_version": 1,
"release": "0.6.3",
"status": "validated",
"measured_commit": "1d2b43fbb93deac5d64b4f40b8ea6121f2f67ed0",
"measured_at": "2026-08-05T15:40:00-07:00",
"measured_commit": "e8a36db60b2146b684204df8a8bea3e20c11d8e4",
"measured_at": "2026-08-05T18:20:00-07:00",
"platform": "Windows 11 Pro 10.0.22631, CPython 3.12, coverage branch mode",
"cpu_non_gpu_non_network": {
"passed": 1763,
"passed": 1830,
"skipped": 2,
"deselected": 6,
"branch_coverage_percent": 86.02,
"branch_coverage_percent": 86.10,
"skip_reason": "symlink creation requires privileges on Windows; hard-link and case aliases cover the same guard"
},
"gpu": {
Expand Down
2 changes: 1 addition & 1 deletion docs/recoverybench/recoverybench-v1.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# RecoveryBench v1
# RecoveryBench v1

RecoveryBench asks a narrow mechanism question: when the starting checkpoint,
teacher, task schedule, optimizer and update count are controlled, does scoring
Expand Down
4 changes: 2 additions & 2 deletions docs/release-checklist.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Release checklist
# Release checklist

This is the release gate and publication record for miniVERL. A checked item
names an invariant exercised on the stated source. Publication begins only
Expand Down Expand Up @@ -43,7 +43,7 @@ after the exact release commit and its remote checks are green.
- [x] Alignment Lab publication is generated as one diverging forest chart and
two row matrices: every arm exposes all three frozen seeds and its mean,
quantitative marks remain in-domain, and non-teacher query ratios remain
`鈥?not applicable` rather than being coerced to zero.
`not applicable` rather than being coerced to zero.
- [x] The scoped safety figure states that both sandbox checks tied at zero
while utility regressed, records the external endpoints as not run, and
does not imply a broad safety benchmark.
Expand Down
71 changes: 64 additions & 7 deletions docs/verl-bridge.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,15 +224,44 @@ merely by being diagnosed:
| --- | --- |
| `not_present` | No scaffold file, or it is unreadable. |
| `syntax_valid` | It parses as Python, but the interface check failed. |
| `interface_statically_verified` | A top-level `compute_score(data_source, solution_str, ground_truth, extra_info=None)` exists, is synchronous, is undecorated, is not bound by assignment, and the module contains no top-level executable statement. |
| `interface_shape_verified` | A top-level `compute_score(data_source, solution_str, ground_truth, extra_info=None)` exists, is synchronous, is not bound by assignment, and no definition-time expression forbidden by this policy was found. |
| `trusted_dynamic_import_verified` | The module was actually imported. Reached only through an explicit opt-in. |

The default path stops at `interface_statically_verified`. Top-level calls,
non-literal assignments, decorators, class-body statements and call-valued
default arguments are all rejected, because each of them runs at import time.
Static verification proves the interface is present and that importing would
not obviously run code; it proves nothing about whether the reward logic is
correct or safe to run later.
The default path stops at `interface_shape_verified`. The level is named for
what it proves: the interface has the expected *shape*. It is not a statement
that the file is safe to import.

Importing a module runs more than its top-level statements, so the check covers
every definition-time position:

| Position | Example |
| --- | --- |
| Top-level statements | `exploit()`, non-literal assignments, loops |
| Class bases | `class Hidden(exploit())` |
| Class keywords | `class Hidden(object, metaclass=exploit())` |
| Annotations | `def compute_score(data_source: exploit())`, `-> exploit()`, `VALUE: exploit() = 1` |
| Type parameters | Python 3.12 bounds and defaults |
| Decorators and defaults | `@exploit()`, `extra_info=exploit()` |

Ordinary type annotations and base classes are unaffected: only expressions
that would actually evaluate — calls, lambdas, comprehensions, `await`, walrus
— are rejected.

The signature contract is enforced including keyword-only parameters, so a
required keyword-only `extra_info` is refused: verl calls `compute_score` with
three positional arguments and that signature would raise `TypeError`.

Inspection is bounded — source bytes, AST node count, AST depth and the number
of reported findings — so a hostile scaffold produces a bounded diagnostic
rather than exhausting the process that asked for a diagnosis. Imports are
listed under `imports_present` with `import_runtime_safety: not_verified`,
because this check never runs them and an imported third-party module can do
anything; a relative, bundle-local import is refused outright.

What this proves is narrow: the interface is present and no forbidden
definition-time expression exists. It proves nothing about whether the reward
logic is correct, whether imported modules are side-effect free, or whether the
file is safe to run later.

If you produced the bundle yourself and want the historical behaviour:

Expand All @@ -244,6 +273,34 @@ This executes the bundle's Python in your process with your privileges. It
prints a warning first and reports `untrusted_code_executed: true`. A
subprocess would not be a security sandbox either, so none is claimed.

### What the bundle claims versus what was recomputed

`provenance/SHA256SUMS` lives inside the bundle it describes. Anyone who edits
`compatibility-report.json` can regenerate it, so agreement between them proves
internal consistency and nothing else. miniVERL implements no signature or
transparency-log verification, and does not pretend otherwise.

The diagnosis therefore reports three separate things:

| Field | Meaning |
| --- | --- |
| `bundle_declared_claims` | Copied from the bundle. Events this run did not observe. |
| `locally_recomputed_checks` | Performed in this process against the bytes on disk. |
| `provenance_trust` | `unsigned_self_consistent` at best; `signature_verification: not_available`. |

Historical smoke results, distributed execution and algorithm parity can only
ever be *declared*: no doctor run launches a job or compares algorithms, so the
top-level `distributed_execution_tested` and `algorithm_semantic_parity` flags
are always `false` regardless of what a bundle asserts. Checksum consistency,
config structure, the pinned requirement file, tokenizer load, adapter
structure, Parquet schema, the reward interface and the metadata privacy
heuristic are recomputed every time.

`--require-verl` recomputes the upstream check rather than trusting a record of
it: it loads the installed pinned verl's generated PPO config, parses the
bundle's `verl-overrides.yaml` and performs the structured merge in this
process. It still launches nothing.

### Adapter weights are validated past the header

`adapter_model.safetensors` is checked structurally, not just parsed:
Expand Down
Loading