# 0.2.0 可直接验收的行为标准

这些是可执行合同，不以“测试多、速度快”代替功能价值。输入均为合成数据，
没有外部用户采用、患者效果或完整标准认证的含义。

## 一条命令检查新增闭环

先按 README 构建 CLI 并安装测试依赖，再执行：

```sh
python scripts/roi_acceptance.py work/roi-acceptance
```

输出目录必须尚不存在。NiBabel 生成输入，编译后的 MoonBit CLI 处理，NumPy/NiBabel
独立验证结果；不是让同一套代码写入后再自证正确。处理已有文件只需 Node。

### 可手算输入

图像大小 `[4,3,2,3]`，原始值 `x+10*y+100*z+1000*t`，缩放为 `raw*2-7`。
静态标签只有 `x=1..2,y=1,z=0..1` 四个位置等于 2，其余为 0。qform 的尺度为
`[1.5,2,-3]`、原点 `[10,-20,30]`；sform 的前三行为
`[1.5,.25,0,100] / [0,2,.5,-50] / [0,0,3,20]`，单位 mm，形式代码分别为 1、2。
时间步长 500 ms，toffset=1500 ms。qform/sform 有意不同。

| 行为 | 输入/操作 | 必须观察到的结果 |
| --- | --- | --- |
| 缩放后分类标签选择 | `roi image labels 2` | 选中 4 个空间体素；包围盒起点 `[1,1,0]`、大小 `[2,1,2]` |
| 每帧统计 | 同上 | 时刻 `[1.5,2,2.5]` 秒；均值 `[116,2116,4116]`；第 0 帧范围 `[15,217]` |
| 几何测量 | 默认 PreferSForm | 中心 `[102.5,-47.75,21.5]` mm；体积 36 mm³，非简单 pixdim 相乘 |
| 直方图 | 第 0 帧，范围 `[15,217]`，2 箱 | `[2,2]`，上界 217 进入最后一箱；下溢/上溢为 0 |
| 双空间裁剪 | `roi-crop`，margin=0 | 形状 `[2,1,2,3]`；qform 原点 `[11.5,-18,30]`；sform 原点 `[101.75,-48,20]`；原始值/缩放/代码/单位保留 |
| 矩形而非掩码填零 | 只选全图对角端点 | 包围盒内未选体素照样保留；超出图像的 margin 被裁到边界 |
| 网格错位拒绝 | 标签 sform 原点偏移 5 mm | `roi` 返回 1，错误 `mask_grid_world_coordinates`；不输出可信区域统计 |
| 分类标签边界 | 分数/负数/超 Int 范围、NaN/Inf | Label 模式拒绝；NonZero 允许有限分数及负数、但也拒绝 NaN/Inf，不作权重解释 |
| 静态掩码约束 | 标签含 2 帧；或空间维度不同 | 分别为 `mask_must_be_static`、`mask_grid_spatial_shape`；不自动重采样 |
| 未知空间/单位 | 任一所选形式或长度单位未知 | 拒绝构造区域；不造默认可信几何 |
| 空区域 | 掩码全 0，选 label=2 | 计数/体积为 0；中心/边界/均值为 null；裁剪返回 `empty_region`，无新文件 |
| 非有限强度 | 区域中一个 NaN、+Inf、-Inf 和 112 | 有限计数 1、均值 112，三种非有限计数各 1；直方图不丢样本 |
| 单位及字节序 | 米/mm/微米的同物理网格，大小端 | 选择结果相同，几何量在声明容差内一致 |
| 时间语义 | sec/msec/usec；Hz；单帧 dim[4]=1 | 前三者转换到秒；后两者不生成时间值；显式时间轴非有限 toffset 拒绝 |
| 扩展保护 | 带不透明扩展的源数据 | 默认 `opaque_extensions`；仅显式 drop 后导出，并含 `opaque_extensions_dropped` 警告 |
| 参数与文件保护 | 非法 frame/bin/range/margin；输出已存在 | 退出 1、不创建错误结果；已有输出内容不变 |
| 下游 CSV 集成 | 清单含一个匹配案例和一个偏移案例 | 匹配例三行均值如上且裁剪独立可读；偏移例被拒绝；批程序返回 2；重复输出目录拒绝 |

统计误差用绝对容差 `1e-9`（固定小样例）；中心 `1e-4 mm`、体积 `1e-4 mm³`。
以上是具体测试容差，不宣称对任意规模/极值输入保证同一误差上界。

## 既有能力仍须通过

`scripts/oracle.py` 使用独立生成的大小端、8 种数据类型、3D/4D、斜切 qform、
负 qfac、带剪切 sform 输入。裁剪与全部 48 种带符号轴排列的输出，要求原始样本
完全相同，缩放值、形状、单位、两套形式代码正确；每套形式按自己的索引变换更新。
矩阵比较 atol=0.0005、rtol=0.000005，角点 atol=0.002（原文件空间单位），
不能将 qform 和 sform 互相替代。这个集合计 276 案例，不代表完整 NIfTI 认证。

`tests/cli.mjs` 验收真实退出码、损坏输入、gzip 解压上限、禁止覆盖、失败时不遗留临时文件。
MoonBit 测试覆盖 3,000 次固定种子头部变异与后端一致性；不是覆盖率/模糊安全性证明。

## 源码量与证据口径

运行 `node scripts/source-audit.mjs --check`。只计核心根包时：物理 1,436 行；
排除空白/注释为 1,235 行；进一步排除纯分隔符行为 1,005 行。不计测试、CLI、
示例、生成接口、Python/JS 测试或 FFI 宿主代码。后者也是估计，不是官方审核公式。

对照 `node scripts/source-audit.mjs --ref v0.1.0`：旧核心对应 1,087 / 942 / 769 行。
因此旧材料的“非测试物理行 1,380”不能证明核心有效 MoonBit 行数达标。
本次增量是标签选择、几何区域、逐帧统计/直方图及导出的可运行闭环；统计公共逻辑已复用，
没有以注释、空行、测试或复制代码凑数。报告逐文件列出，最终认定仍属组委会。

## 证据不足的部分

有自建下游集成，不等于真实外部采用；有跨后端测试，不等于已有完整浏览器查看器。
尚未测量峰值内存、最大输入下的全覆盖兼容性，也不承诺比成熟库更快。
这些限制见 [定位对照](POSITIONING.zh-CN.md) 与 [性能记录](PERFORMANCE.md)。
