# 分样输入与结果契约

主命令 `python3 scripts/aliquots.py reconcile --input FILE` 接收 `aqueous-aliquot/v1`。完整 JSON 见 `examples/aliquots/acidified-split.json`。所有输入字段必须提供，数值为整数；未知字段拒绝，不能用额外字段偷偷加入未支持的离子或物理假设。文件适配器还拒绝重复 JSON 键。

| 对象 | 必需字段 |
|---|---|
| study | `schema`, `source`, `steps`, `assays` |
| source | `id`, `volume_ul`, `calcium_nmol_l`, `dic_nmol_l`, `ta_neq_l`, `sodium_nmol_l`, `chloride_nmol_l` |
| 每个 Bounds | `lower`, `upper`，包含端点，lower ≤ upper；相等表示固定值。 |
| step | `id`, `parent`, `kind`, `volume_ul`, `amount_nmol` |
| assay | `id`, `node`, `analyte`, `bounds` |

节点 ID 必须非空、唯一、父节点先于子节点；检测 ID 在检测中唯一。analyte 只接受 `Ca`、`DIC`、`TA`、`Na`、`Cl`。pH 不能直接当作一个可加分析量。操作 kind 及正负号见模型文档；体积类操作 amount 固定为 0，CO₂/方解石 volume 固定为 0，HCl/NaOH 的 amount 非负。

source.volume_ul 为 1..10⁹；所有中间体积也须在该范围。其他体积输入为 0..10⁹，具体操作另有严格正值要求。分析浓度/剂量范围的绝对值不超过 10⁹，Ca/DIC/Na/Cl 和 HCl/NaOH 非负；TA、CO₂/方解石净转移允许负值。最多 16 步、32 个检测范围、10 个不确定源样/过程量。体积分配不能重复使用同一瓶液体。

`challenge` 接收 `{"study": STUDY, "zero_steps": ["acid", "base"]}`。至少指定一个已存在的 co2/calcite/hcl/naoh 步骤，不能重复 ID 或指定取样/体积操作。它检验这些步骤的净转移能否**同时**为零。

`replay` 接收 `{"study": STUDY, "parameters": [...]}`。每个不确定源样/步骤恰好一项：`{"parameter": "step:acid", "value": {"numerator":"10000","denominator":"1"}}`。源样参数为 source:Ca/DIC/TA/Na/Cl；固定量不传参数。分母为正数字串，分子为整数串，每串最多 700 字符。输入分数可约分，输出保证约分、正分母；没有二进制浮点近似。非法、缺失、重复、越界或不能解释检测的选择被拒绝。

所有 JSON 入口返回 `{"ok":true,"result":...}` 或 `{"ok":false,"error":"..."}`。合法模型无解返回 ok=true、status=Conflict；ok=true 不代表样本一致。AliquotAssessment 包含 status、history（仅 Compatible 时存在）、conflict、parameter_count、constraint_count、diagnostics。每个冲突项给 constraint ID 与精确 weight；行定义由原始输入决定，独立验证器会重建，不能仅相信导出的矩阵。

共同历史包含按输入顺序的 parameters 和全部 samples（ID、体积、五项精确浓度）。缺失 Option 字段在 JSON 中省略。ClaimAssessment 包含 base 和可选 assumed_zero；两者的模型和参数 ID 保持一致。状态含义和文件适配器退出码见 README。不能用 `ResourceLimit` 充当通过。

保留的平衡包使用另一个 mol/L 数值契约，见 `equilibrium-schema.md`。两套输入不互相静默转换，主任务不从 pH/SI 计算逆向处理历史。
