diff --git a/docs/1.0-stabilization-draft.md b/docs/1.0-stabilization-draft.md new file mode 100644 index 0000000..2a2e536 --- /dev/null +++ b/docs/1.0-stabilization-draft.md @@ -0,0 +1,460 @@ +# light-ocr 1.0 稳定化草案 + +- 状态:Draft +- 提案日期:2026-07-27 +- 目标周期:3–5 周 +- 目标版本:首个候选 `1.0.0`;GA 为首个通过全部 Gate 的 `1.0.x` +- 依据:[Roadmap N5](roadmap.md#10-n5--10-稳定化)、[D115](decisions.md#d115--keep-external-adoption-non-blocking-for-10) + +## 1. 摘要 + +`light-ocr` 已经完成 1.0 所需的主要产品能力:一个 npm 安装入口、默认 +Small 模型、图片与 PDF OCR、CLI 与 Node.js API、六个平台 native 包、 +离线运行、CPU 最终候选,以及通过资格验证的 Apple Core ML 和 +Linux/Windows WebGPU 加速。 + +1.0 不再增加 Layout、表格、公式、新模型或新 provider。稳定化阶段只做 +四件事: + +1. 冻结 `@arcships/light-ocr` 的 Node.js、CLI、错误和 JSON schema; +2. 删除不应进入 1.x 的迁移字段和契约歧义; +3. 用锁定 corpus、消费者工程和六平台发布产物完成可重放验收; +4. 明确平台、生命周期、安全、弃用和包保留政策。 + +外部用户数量、第三方集成数量、下载量和固定观察窗口不是发布门禁。 +外部反馈仍可发现 blocker,但没有反馈不能阻塞 1.0。 + +## 2. 1.0 产品边界 + +### 2.1 稳定入口 + +以下入口进入 `1.0 stable surface`: + +- npm:`@arcships/light-ocr` +- CLI:`light-ocr` +- Node.js: + - `createEngine()` + - `createDocumentEngine()` + - `recognizeDocument()` + - `hasPdfSupport()` + - `getVersion()` + - `modelProfile` + - `OcrError` +- Engine: + - `recognize()` + - `recognizeEncoded()` + - `detect()` + - `info` + - `close()` +- CLI 命令: + - 隐式图片/PDF 路径 + - `recognize` + - `detect` + - `document` + - `info` + - `doctor` +- JSON: + - `schemaVersion: 1` + - `pageSpace` 坐标 + - 图片/PDF page envelope + - JSONL 每页流式记录 +- 默认执行: + - macOS arm64:Apple → CPU + - macOS x64:CPU + - Linux x64 glibc:WebGPU → CPU + - Linux arm64 glibc:CPU + - Windows x64:WebGPU → CPU + - Windows arm64:CPU + +其中 `modelProfile` 只描述主包内置的 Small 模型;Tiny、Medium 的同名信息 +不因此进入 stable surface。`getVersion()` 只返回 facade +`@arcships/light-ocr` 的版本,不代表 Core、runtime、模型或 schema 版本。 + +上述 provider 箭头只表示 `provider: "auto"` 在 **engine 创建期**依次尝试 +候选;某个 backend 一旦创建成功,推理期失败绝不切换到另一个 backend。 + +### 2.2 明确不进入 1.0 的范围 + +- Tiny、Medium 继续保持 Preview,不阻塞 1.0; +- Layout、阅读顺序、表格、公式和字符级坐标不进入 stable surface; +- Bun、浏览器、WASM、musl、Android 和 iOS 不承诺支持; +- CUDA、TensorRT、OpenVINO、DirectML、QNN 等新 provider 不进入本轮; +- 不提供云端 OCR、远程 URL、运行时模型下载或安装期二次下载; +- 不提供 C ABI、跨编译器 ABI 或系统级 C++ SDK 安装承诺; +- 不新增默认遥测。 + +### 2.3 包版本边界 + +- `@arcships/light-ocr` 与六个平台 native 包进入 `1.0.0`; +- 六个平台 native 包是 facade 的安装实现制品,不是面向用户直接消费的 + stable API; +- runtime、model、Tiny、Medium 和 Document 兼容包继续独立版本化; +- facade 精确锁定通过验收的 runtime、native 和 Small model; +- `@arcships/light-ocr-document` 保持兼容转发,不重新成为 PDF 的主入口; +- 1.0 不因内部包仍使用 `0.x` 版本而降低 facade 的稳定承诺。 + +## 3. 1.0 前的契约清理 + +这些清理必须在冻结 1.0 候选 tarball 前完成;候选冻结后任何代码变化都要 +废弃候选并重新验收。 + +### 3.1 删除迁移期 execution 字段 + +建议在 `0.6.0` 完成: + +- 从公共输入类型删除 `SessionFallback` 和 + `ExecutionOptions.sessionFallback`; +- 删除已经稳定失败的 `sessionFallback: "cpu"`; +- 从 `ExecutionInfo` 删除请求级 `sessionFallback`; +- 保留每个 session 的布尔诊断字段仅当它仍表达真实 backend 内部事实; +- 删除已标记 deprecated 的 `EngineInfo.executionProvider`; +- 统一使用 `execution.sessions` 和 `selectionTrace`。 + +理由:D112 已经把跨 backend fallback 收敛为 `provider: "auto"` 的创建期 +状态机。把无效输入和旧聚合字段带入 1.x,只会迫使后续 major 才能删除。 + +迁移文档必须给出旧字段到新字段的精确映射。 + +### 3.2 冻结错误与取消语义 + +- 冻结 `OcrError.code` 到触发条件的映射;1.x minor 只能新增 code,消费者 + 必须能处理未知 code; +- `message` 不作为机器契约,`code`、`detail` 和 `creationTrace` 才是; +- PDF 页间失败保留已完成 JSONL 记录,CLI 返回非零退出码; +- 所有资源上限错误固定为 `resource_limit_exceeded`,不泄漏底层异常文本。 + +取消与关闭冻结为下表;`AbortSignal.reason` 存在时原样作为 rejection reason, +否则使用 `AbortError`: + +| 状态 | 调用方结果 | native 工作与容量 | CLI | +| --- | --- | --- | --- | +| 接纳前已取消 | 立即 reject | 不接纳、不占容量 | exit 72,无页面输出 | +| 已排队后取消 | 立即 reject | 从队列移除并释放容量 | exit 72,保留此前 JSONL | +| 已运行后取消 | 立即 reject | cooperative discard;不承诺中断本次推理,容量在 native 完成后恢复 | exit 72,保留此前 JSONL | +| PDF 页边界取消 | 下一次边界检查 reject | 已关闭 page/document 资源 | exit 72,保留此前 JSONL | +| `close()` 与已接纳任务竞态 | 已接纳调用按原结果 settle | `close()` drain 后完成,重复调用幂等 | CLI `finally` 等待关闭 | + +1.0 不承诺强制抢占正在执行的 native 推理。 + +### 3.3 冻结文档与坐标 schema + +- 图片与 PDF 的输出统一使用 `schemaVersion: 1`;该值是兼容性主版本,不是 + 冻结字段集合; +- 所有 box 使用左上原点、像素单位的 `pageSpace`; +- PDF 必须返回可审计的 DPI、rotation、media/crop box 和 scale; +- 契约 inventory 必须包含 PDF 坐标换算的规范公式和至少一个带 rotation 与 + crop box 的数值示例,验证输出始终为渲染后左上原点像素坐标; +- Node Document API 与 CLI envelope 的字段命名、可选性和页码基准必须一致; +- `--schema-version 1` 请求精确的兼容性主版本,不支持时稳定失败; +- v1 消费者必须忽略未知字段;现有字段删除、重命名、类型变化或语义变化 + 需要 schema v2; +- 1.x 只允许兼容新增字段;删除、重命名或改变坐标含义需要 2.0。 + +### 3.4 文档一致性清理 + +冻结 1.0 候选前必须消除以下已知冲突: + +- `bindings/node/README.md` 仍把 PDF 列为不支持; +- CLI 设计文档仍保留已经落地前的 Draft/开放决策措辞; +- implementation status 中部分包版本和 candidate 状态落后于 0.5.5; +- README、package README、类型声明和 `--help` 必须来自同一能力矩阵; +- 所有示例必须从正式 npm 包运行,不引用工作区内部路径。 + +## 4. 支持政策草案 + +### 4.1 Node.js 与宿主 + +- Node.js 22、24:1.x 稳定支持; +- 宿主状态统一为 `stable`、`compatible`、`preview`、`unsupported`: + `stable` 受 1.x 门禁约束,`compatible` 只有记录过的 smoke 证据, + `preview` 可试用但可能变化,`unsupported` 不接受兼容承诺; +- Electron:1.0 标记 `compatible`;当前 Windows x64 smoke 只是兼容证据, + 完整正式安装、ASAR、worker、退出和六平台矩阵不阻塞 1.0; +- Bun:明确不支持,直到 Node-API lifecycle 全矩阵通过; +- ESM 和 CommonJS:保持同一导出与错误 identity; +- 删除 Node 版本或模块系统属于破坏性变化,不在 1.x 静默发生。 + +### 4.2 OS 与架构 + +1.0 保留当前六个平台 npm identity: + +- macOS arm64 / x64 +- Linux glibc arm64 / x64 +- Windows arm64 / x64 + +候选前从实际 native descriptor、deployment target 和 runner 记录已验证的 +OS、glibc、driver 与 CPU 架构 baseline。只有加入明确边界测试的值才称为 +“最低版本”。未列出的 facade 安装组合允许 npm 跳过 optional native 包, +随后由 `createEngine()` 稳定返回 `unsupported_platform`;用户显式安装某个 +不匹配的 native 包时,npm 自身可能因 `os`/`cpu` 限制直接拒绝。不进入本地 +源码编译 fallback。 + +### 4.3 C++ 边界 + +- npm facade 1.0 不用 SemVer 管理 C++ source API;Core 继续遵循自身的 + source compatibility policy,且不承诺二进制 ABI; +- npm `1.0.0` 不等于 C++ SDK `1.0`; +- headers、ownership、errors 和 lifecycle 继续文档化; +- C ABI、安装布局、符号版本和其他包管理器另立决策,不阻塞 npm 1.0。 + +### 4.4 安全与版本维护 + +- 安全修复优先进入最新 1.x; +- `0.6.x` 从实际 GA 的 `CANDIDATE_VERSION` UTC 发布时间起保留 90 天 + critical security overlap,覆盖 facade、runtime 和随 facade 发布的 + native 包; +- critical 指可利用的代码执行/权限突破、数据破坏、密钥或隐私数据泄露; +- 依赖、PDFium、模型和 provider runtime 更新必须重新通过对应 Gate; +- npm 包不撤回已发布正常版本;发现问题时发布修复版并标记受影响版本; +- security advisory、release notes、migration guide 和 SBOM 必须互相链接; +- 不可信 PDF 的进程隔离建议进入安全文档,但默认 API 保持进程内本地执行。 + +GA 时把仍受 90 天 overlap 保护的精确 `0.6.x` facade、runtime 和 native +版本集合写入 release record。只对该集合 backport 上述 critical 问题; +不能安全 backport 时必须发布 advisory,并把升级到 1.x 作为修复路径。 + +## 5. 内部验收矩阵 + +### 5.1 三套消费者场景 + +所有场景从完整本地 tarball 集或 registry candidate 安装,使用 +`--ignore-scripts`,不引用仓库源码: + +1. **CLI 图片场景** + - path 与 stdin; + - JSON、JSONL、text; + - ROI、detect、info、doctor; + - stdout/stderr、exit code 和 schema。 +2. **Node.js Engine 场景** + - raw 与 encoded image; + - CJS 与 ESM; + - queue、AbortSignal、close、错误 identity; + - CPU 与当前平台 Auto。 +3. **Document 场景** + - PDF 与多图片; + - Node async generator 与 CLI JSONL; + - page range、DPI、取消、页级错误和确定性清理; + - 安装及运行阶段无二次下载。 + +### 5.2 PDF corpus + +保持 Roadmap 的内部证据标准: + +- 至少 30 份、100 页、5 类文档; +- 类型至少包含: + - 原生文本 PDF; + - 扫描/图片型 PDF; + - rotation、crop box 或非标准 page size; + - 多页高密度文档; + - malformed、encrypted、超限或含非渲染资源的拒绝样本; +- 正常样本锁定 page count、尺寸、transform、OCR 期望或容差; +- 拒绝样本锁定错误 code、发生阶段和资源清理结果; +- corpus 必须可合法提交或可确定性生成。 +- 候选前锁定 `manifest.json`:文件 SHA-256 或生成 seed、五类逐类最小数量、 + 页数、预期值、比较器版本和数值容差;修改 corpus 或比较器即使全部通过, + 也必须产生新的 evidence run。 + +### 5.3 平台与运行时 + +每个平台必须通过: + +- native build 与 Core tests; +- Node.js 22、24 installed-package smoke; +- 从完整本地 tarball 集,在空 npm cache 且网络禁用的环境执行 + `npm install --offline --ignore-scripts`; +- 图片 OCR; +- PDF render + OCR; +- malformed input 与资源上限; +- native/runtime/PDFium artifact hash、license 和 SBOM; +- registry integrity(发布到 `next` 后由 G7 在联网环境验证); +- sterile cwd 与禁网运行。 + +Apple 与 WebGPU 只在各自已接受的平台执行 provider Gate;CPU 是所有平台 +的稳定显式 backend 和 Auto 最终候选。 + +### 5.4 发布否决条件 + +以下任一情况阻止候选发布到 `next` 或从 `next` 晋升 `latest`: + +- Sev-0/Sev-1 correctness、安全、数据损坏或无界资源问题; +- stable API/CLI/schema 存在两种合理解释; +- 任一支持平台无法从正式包完成图片和 PDF smoke; +- install/postinstall 或 runtime 出现二次下载; +- Auto 隐藏跨 backend runtime fallback; +- tarball、SBOM、license、provenance 或 registry integrity 不完整; +- 文档声明与正式包行为不一致。 + +严重度定义: + +- **Sev-0**:可利用的远程代码执行、权限突破、供应链制品被篡改或不可恢复 + 的广泛数据破坏; +- **Sev-1**:违反冻结 schema/API/error/coordinate invariant,锁定 corpus + 超出已批准 comparator 容差,进程崩溃/死锁/无界资源、正式包损坏,或任一 + 支持平台的默认图片/PDF 主路径不可用; +- Sev-0/Sev-1 不允许 waiver;较低严重度可以延期,但必须在 release record + 记录负责人、影响和跟踪项。 + +## 6. Gate 归属与证据 + +同一维护者可以承担多个角色,但每个 Gate 必须在 +`release/1.0/candidates//gates.json` 中记录 owner、必跑 +命令/workflow、输入 manifest hash、通过判据、run URL、结果和 UTC +签字时间。以下 ID 是唯一 Gate 枚举; +依赖、模型、provider runtime、driver baseline 或测试输入变化会使受影响 Gate +失效,必须重跑。 + +| ID / Gate | 责任角色 | 固定证据位置 | +| --- | --- | --- | +| G1 公共契约 | API owner | `contracts/public-api-1.0.json`、类型/API snapshot | +| G2 CLI 消费者 | CLI owner | `tests/consumers/cli/`、候选 `gates.json` | +| G3 Node 消费者 | Runtime owner | `tests/consumers/node/`、候选 `gates.json` | +| G4 Document/PDF | Document owner | `tests/consumers/document/`、`corpus/document-1.0/manifest.json` | +| G5 六平台/provider 制品 | Release owner | 候选 `candidate-manifest.json`、SBOM/build attestation | +| G6 支持与迁移 | Maintainer | `docs/support-policy.md`、`docs/migration-to-1.0.md` | +| G7 正式 registry | Release owner | 候选 `gates.json`、registry integrity/provenance/run URL | + +`release/1.0/candidates//candidate-manifest.json` 是该候选的唯一身份 +清单,至少固定: + +- 候选版本,以及 facade、runtime、Small model、PDF compat 和六个 native + 包的完整包名与精确版本; +- 每个 tarball 的文件名、SHA-256、npm integrity、大小和依赖解析结果; +- 对应 Git commit、model bundle ID、Core/PDFium/provider runtime 版本; +- 每个平台的 SBOM、license 和可在发布前生成的 build attestation hash; +- `contracts/public-api-1.0.json` 和 corpus manifest 的 hash。 + +G5 只有在清单内全部制品存在、相互依赖精确匹配且六平台测试通过时才为 +pass;npm `publish --provenance` 产生的 registry provenance 不属于 G5, +由 G7 验证。G7 只有在正式 registry 上的版本、integrity、依赖图和 +provenance 与该清单完全匹配时才为 pass。不得以“CI 大体通过”替代字段级 +判定。`gates.json` 单向引用 candidate manifest hash,避免循环身份。 + +每个已发布到 `next` 的候选目录提交后不可改写;失败候选也原样保留。 +`release/1.0/candidate-index.json` 追加版本、状态、目录与 superseded-by, +最终 release record 保存实际 GA 候选两份 JSON 的 hash 和所有 run URL。 + +多包补丁规则: + +- `CANDIDATE_VERSION` 始终是 facade 版本;候选失败后 facade 必须递增补丁; +- 内容完全未变且已发布无误的 runtime、model、PDF compat 或 native 包可以 + 复用原精确版本和 integrity; +- 任何内容、依赖 metadata、SBOM/provenance 或 registry 制品需要变化的包, + 都按自身版本线递增补丁且禁止覆盖; +- facade 和所有发生变化的 native 包在首个 1.0 候选进入 1.0;内部独立包 + 不因 facade 补丁自动升到 1.x; +- 新 manifest 必须重新解析完整依赖图;复用制品不等于复用受影响 Gate。 + +已有 provider 资格证据作为基线链接到最终 release record: + +- Apple qualification:[`docs/releases/npm-0.3.0.md`](releases/npm-0.3.0.md) + 及 [Apple acceleration](apple-device-acceleration.md); +- WebGPU qualification:[Linux acceleration](linux-device-acceleration.md) + 与 [Windows acceleration](windows-device-acceleration.md); +- 最新已接受运行:Core + [run 30250720658](https://github.com/arcships/light-ocr/actions/runs/30250720658)、 + WebGPU + [run 30250722663](https://github.com/arcships/light-ocr/actions/runs/30250722663)。 + +## 7. 版本与时间线 + +周期从本草案被接受后的第一个工作日开始。3 周是所有入口条件已满足且无 +blocker 的乐观值,5 周是计划值;超过 5 周不降低 Gate。 + +草案阶段只允许准备 manifest、测试和文档;第 8 节决策在 +`docs/decisions.md` 形成可引用的接受记录后,才能删除公共字段或冻结候选。 + +### 第 1 周:契约冻结候选 + +- 入口:草案接受,1.0 scope 不再新增; +- 接受本草案及仍未决的 1.0 决策; +- 完成 execution 迁移字段清理; +- 冻结 Node API、CLI、error code 和 schema inventory; +- 决定最低 OS、Electron 和 C++ 支持表述; +- 建立 migration guide 骨架。 + +退出:契约 manifest 可机器比较、所有删除项有迁移说明、支持表述无未决项。 + +交付:`0.6.0`,作为已批准的最后一个有意契约清理 minor。 + +### 第 2 周:消费者与 PDF 资格 + +- 入口:`0.6.0` 已发布且契约 manifest 冻结; +- 建立三套正式包消费者工程; +- 扩充 PDF corpus 到 30 份/100 页/5 类; +- 补 cancellation、malformed、resource、cleanup 和 JSONL tests; +- 清理所有文档与 `--help` 冲突。 + +退出:消费者工程、锁定 corpus 和文档一致性 Gate 全绿。 + +交付:正式稳定化补丁 `0.6.1`。 + +### 第 3 周:支持政策与发布演练 + +- 入口:`0.6.1` 已发布且消费者/PDF Gate 全绿; +- 完成六平台 Node 22/24 资格矩阵; +- 完成 Electron 支持级别结论; +- 固定安全、EOL、包保留和弃用政策; +- 从本地候选 tarball 完成空缓存禁网演练; +- 生成 1.0 release checklist、migration guide 和 release notes。 + +退出:G1–G6 全绿,`CANDIDATE_VERSION` 的完整 tarball 集及 hash 已冻结; +首个 `CANDIDATE_VERSION` 为 `1.0.0`。冻结后任何代码、依赖、模型或制品 +变化都必须废弃该候选并重新进入本阶段。 + +交付:将精确的 `CANDIDATE_VERSION` 发布到 npm `next`,不另发 +`1.0.0-rc.*` 制品。 + +### 第 4–5 周:registry 验证与 GA + +- 对 npm `CANDIDATE_VERSION@next` 只执行 registry 安装、integrity 和运行 + 验证,形成 G7; +- 发布后发现任何 blocker 时不晋升 `latest`;npm 版本不可覆盖,因此修复后 + 递增补丁号,将其设为新的 `CANDIDATE_VERSION`,重走 G1–G7;失败版本保留 + 在 release record 且不打 GA Git tag; +- 没有代码或制品变化时,只移动 dist-tag,把完全相同的 + `CANDIDATE_VERSION@next` 晋升为 `latest`; +- 创建与实际 GA 版本一致的 `v${CANDIDATE_VERSION}` GitHub Release。 + +退出/交付:npm GA 版本的 tarball 与 integrity 在 `next` 和 `latest` +完全相同,G1–G7 有签字且没有 Sev-0/Sev-1。正常路径是 `1.0.0`;若正式 +registry 验证淘汰该不可覆盖版本,则由首个通过 Gate 的后续 `1.0.x` 完成 +1.0 GA。 + +## 8. 仍需接受的决策 + +草案建议以下默认答案: + +| 决策 | 建议 | +| --- | --- | +| 是否删除 `sessionFallback` 公共输入 | 是,在 0.6.0 删除 | +| 是否删除 `EngineInfo.executionProvider` | 是,在 0.6.0 删除 | +| runtime 是否同步升到 1.0 | 否,独立版本化;facade 承担稳定承诺 | +| Tiny/Medium 是否 GA | 否,继续 `next` | +| Layout 是否阻塞 1.0 | 否 | +| Electron 是否立即称为 stable | 否,当前称 compatible;完整矩阵留待后续且不阻塞 1.0 | +| Bun 是否支持 | 否 | +| C++ 是否提供稳定 ABI/SDK | 否,只保留 source contract | +| Document 兼容包是否删除 | 否,1.x 保留转发;不作为推荐入口 | +| 是否要求外部用户/集成观察 | 否,D115 已取消 | + +任何修改上述默认答案的决定,都必须同时说明对 3–5 周目标的影响。 + +其中 Document 兼容包的 “1.x 保留” 指 facade 1.x 的支持生命周期,不表示 +兼容包自身必须使用 1.x 版本号。 + +## 9. 完成定义 + +满足以下条件时可以发布 1.0: + +- stable surface inventory 已冻结并有机器可验证的导出快照; +- 三套消费者场景和 PDF corpus 全绿; +- 六平台、Node 22/24、图片/PDF、禁网和 integrity 全绿; +- migration、deprecation、security、support 和 retention 文档完整; +- 实际 GA 的 `CANDIDATE_VERSION@next` 只经 dist-tag 晋升为 `latest`, + 版本、tarball、hash 与 integrity 完全相同; +- 没有发布否决级问题; +- Roadmap、decisions、README、package README、类型和 CLI help 对同一事实 + 给出一致答案。 + +1.0 的含义是默认 Small 图片/PDF OCR 产品契约稳定,不代表项目停止增加 +能力,也不把 Preview 模型或未来 Layout 自动变成稳定承诺。