OpenProgram / Provider design / implementation handoff

请求录制与严格离线回放入口

设计审计基线:990bfe36e625579443e1cb01a1b3266c8dbd0e87;生命周期实现复核提交:99252205957a2850e1203a888a0fa0b403b0c3f7。本页定义产品入口、provider runtime 生命周期、registry 包装、文件安全、严格离线和验证合同,并分别记录设计与实现证据。

确定方案。 保留现有 RecordingProviderReplayProvider 和 registry transform;新增显式、线程安全、进程级的 provider runtime 生命周期。导入 openprogram.providers 不注册厂商 provider、不读取 record_replay 配置、不打开录制文件、不安装 transform。第一次实际获取 provider 前完成注册与激活;openprogram recordings 只执行管理操作,不进入 runtime 生命周期。录制默认关闭,脱敏不可关闭;回放不读取凭证、不委托真实 provider、不执行 provider fallback。

01 · 目标、非目标与安全边界

目标

  • 从产品 CLI/config 启用完整 provider 请求录制。
  • 以同一文件离线复现多次、跨 API 的 provider 调用。
  • 文件写入并发安全、默认 owner-only、始终脱敏。
  • 提供可脚本化的 list/show/delete/prune。
  • 错误定位到文件、行、调用、事件或字段。
  • 把 provider 注册、runtime 激活和 recordings 管理分成可独立调用和验证的边界。

非目标

  • 不录制 tool、MCP、浏览器或任意 HTTP 流量。
  • 不定义、持久化或恢复 workflow、Agent DAG、Session 和工具副作用;回放只替换 LLM provider 请求/事件边界。
  • 不把 JSONL 定义为长期稳定的公共交换格式。
  • 不做跨模型或跨 prompt 的模糊匹配。
  • 不在 Web UI 增加入口;CLI/config 先覆盖 worker。
  • 不自动上传、共享或提交录制文件。
  • 不重写 provider 插件协议,不改变录制格式,不引入运行中热切换。

安全边界

  • “严格离线”只承诺 LLM provider 层没有网络访问。
  • 回放中的 tool 仍按自身权限运行;需要全进程断网时继续使用 sandbox/network policy。
  • 脱敏只处理凭证;prompt、回复和工具结果可能包含私有业务内容。
  • show 默认只显示元数据,内容必须显式请求。

02 · 基线中的当前实现与缺口

能力当前证据状态缺口
录制、回放与脱敏recording.pyreplay.py已实现严格 v1 JSONL、并发写入、固定脱敏、离线 mismatch 与 legacy reader 已有测试证据。
Registry transformapi_registry.py::configure_provider_transform()已实现覆盖已有和后续注册 provider;同一进程只允许一个 transform。
CLI/config 与文件管理openprogram recordings已实现status/record/replay/off/list/show/delete/prune 与 next-start 配置已落地。
管理命令恢复e8c9f645已实现recordings 管理入口不初始化 provider runtime;损坏 replay 文件不再依赖环境变量哨兵恢复。
Provider runtime 生命周期initialization.pyregister.py已实现显式 NEW/INITIALIZING/READY/FAILED 状态、并发首次初始化、失败脱敏、auth/API 原子发布与启动期 fail-fast 已验证。
简版 Markdownrecord-replay.md/.zh.md已同步记录当前 CLI、严格离线事实与生命周期实现证据。
实现后的生命周期边界:providers/__init__.py 不注册 built-ins、不读取配置、不打开录制文件、不安装 transform。公共 provider 获取和 Web/worker 启动显式调用 initialize_provider_runtime();recordings 管理直接操作配置与文件,不依赖环境变量协议。

03 · 现有 reference corpus 的对应设计

本仓当前参考快照只有 references/pi-ai。本节只描述该快照,不声称覆盖其他版本或整个开源生态。

对象实际机制录制/回放能力OpenProgram 取舍
references/pi-ai/src/stream.ts stream()/streamSimple()model.api 解析一个 provider,并直接调用 provider 方法。 快照内未提供。快照没有 recording/replay 模块、配置入口或录制文件管理;stream.ts 也没有 wrapper hook。 采用单一 registry 分派点;修改为由 registry 统一变换 provider,而不是改每个 vendor 模块。
references/pi-ai/src/types.ts 共享 ModelContext、stream options 和事件类型。 快照内未提供。没有 recording manifest 或 replay mismatch 类型。 继续序列化现有 Pydantic 对应类型;不创建第二套请求/事件领域模型。

拒绝的做法:逐 vendor 增加 recorder、通过 HTTP 代理捕获原始流量、用测试 mock 替代产品入口。这些做法分别增加重复实现、暴露未脱敏 wire 数据,或不能覆盖真实 worker 配置。

04 · 总体设计

模块导入 import openprogram.providers 只定义和导出 API;不注册厂商 provider,不读取配置,不访问录制文件,不安装 transform 正常运行 实际 provider 获取 get_api_provider(api) initialize_provider_runtime() NEW → INITIALIZING → READY / FAILED 注册 built-ins → 配置快照 → 原子安装 transform 最终 registry provider 原 provider / RecordingProvider / ReplayProvider 管理运行 openprogram recordings ... 直接调用 inspection / config 管理函数 不初始化 provider runtime 损坏配置或 replay 文件仍可诊断和关闭 next_start 管理修改不重置当前进程生命周期

保留

现有 JSONL、严格 parser、共享 sink/replay、registry transform、next-start 配置、管理函数和直接构造 RecordingProvider/ReplayProvider 的库 API。

分离

register_builtins() 只负责 provider/auth adapter 注册;initialize_provider_runtime() 负责一次性配置快照和 transform;管理命令不调用二者。

新增

统一生命周期状态、并发首次初始化、失败缓存、公共 provider 获取前强制初始化,以及 Web/worker 的启动期 fail-fast 调用。

拒绝

不使用环境变量控制 import;不为每个 vendor 增加初始化分支;不热重载配置;不改变录制格式;不把 recordings 管理依赖于 provider runtime。

05 · CLI 与配置合同

CLI 语法

openprogram recordings status [--json]
openprogram recordings record [--name NAME]
openprogram recordings replay <ID-or-PATH>
openprogram recordings off
openprogram recordings list [--json]
openprogram recordings show <ID-or-PATH> [--json] [--content]
openprogram recordings delete <ID> [--yes]
openprogram recordings prune --older-than-days N [--dry-run] [--yes]
命令确定行为退出码
status显示 effective mode、已选文件、配置来源、文件可用性、是否需要重启。不得读取录制内容。有效为 0;配置无效为 1。
record创建并校验 managed ID,以 0600 预创建只含 header 的文件,再单次事务写入 mode=record 和 selector;任何一步失败都删除本次新建的空录制并保持旧配置。不立即重启 worker。省略名称时生成 UTC 时间加随机后缀。文件与配置均成功为 0。
replay先严格解析指定文件,成功后再以一次配置事务写入 mode=replay 和 selector。文件无效时为 1,且配置不变。
off只把 mode 写为 off,保留最近 selector 便于后续显式复用。成功为 0。
list/show/delete/prune见 §10。管理命令不改变 mode。参数错误 2;运行错误 1。

配置 schema

{
  "record_replay": {
    "mode": "off",          // off | record | replay
    "file": ""              // managed ID or explicit filesystem path
  }
}
SettingSpec类型与默认值apply校验
record_replay.modeenum,默认 offnext_start只允许 off/record/replay。
record_replay.filetext,默认空next_startoff 可为空;record 为空时启动失败,CLI record 会预先生成;replay 必须是可严格读取的文件。

openprogram config set 仍可逐项修改;推荐的 recordings record/replay/offsetup.update_config() 一次写两个字段,避免中间状态。配置仅在 initialize_provider_runtime() 从 NEW 转入 INITIALIZING 时读取,READY 或 FAILED 后不再重读。修改后必须重启相关进程;命令输出必须明确打印这一点。管理命令本身只检查和更新配置,不触发 runtime 初始化。

路径规则:不含路径分隔符的 selector 解析为当前 profile 的 managed ID;包含分隔符或绝对路径时解析为显式路径。record 模式只允许 managed root 内路径,避免长期 worker 覆盖项目文件;replay/show 可读取显式文件。delete/prune 永远只作用于 managed root。

06 · Registry 包装与生命周期

导入合同

import openprogram.providers 只完成 Python API 定义、模型 API 导出以及既有静态模型模块加载。它不得调用 register_builtins()、不得读取 record_replay 配置、不得打开录制文件、不得安装 transform,也不得通过导入 auth adapter 改变凭证注册表。这个合同用隔离子进程测试验证,不以代码注释代替;本任务不承诺清除模型目录模块已有的其他只读初始化。

职责与接口

接口职责禁止行为
register_builtins()线程安全、幂等地解析内建 provider,使用一次 registry batch 原子发布 API provider,并显式加载已有 auth adapter。不读取 record/replay 配置,不访问录制文件,不安装 transform;内部 import 错误不得按“可选 SDK 缺失”静默忽略。
initialize_provider_runtime()串行执行 built-in 注册、配置快照、record/replay 预构造和 transform 发布。不解析 credential,不创建 vendor client,不发起网络请求。
get_api_provider(api)作为公共 provider 获取边界,先确保 runtime READY,再从 registry 返回最终 provider。FAILED 时不得返回原 provider,也不得绕过录制/回放配置。
create_runtime(...)先确保 provider runtime READY;off/record 保持现有 runtime 路由,replay 根据显式或非凭证默认 provider/model 构造基础 Runtime。replay 下不得执行 CLI/AuthStore detection,也不得实例化会读取 credential 的 subscription runtime。
recordings management读取/校验文件和更新 next-start 配置。不调用 initialize_provider_runtime(),不依赖 provider registry。

api_registry 保留 register_api_provider(api, provider)get_api_provider(api) 和一次性 transform。transform 必须同时处理初始化前已有的注册项和初始化后新增的注册项。

configure_provider_transform(transform)
  - 同一进程只允许设置一次;重复同值是 no-op,冲突值抛 RuntimeError
  - 保存 original provider,不对已经 transformed 的 provider 再包装
  - 原子地替换当前 registry 的全部值
  - 后续 register_api_provider(api, original) 自动存入 transform(api, original)

record:
  shared_sink = RecordingSink(path)
  transform(api, original) -> RecordingProvider(original, shared_sink)

replay:
  shared_replay = ReplayProvider(path)
  transform(api, original) -> shared_replay
  original 只用于确认该 api 已注册,不保存到 ReplayProvider,不允许委托

所有 record wrapper 共享一个 sink,所有 replay API 共享一个 ReplayProvider,因此 call_index 表示进程内所有 provider API 调用的总顺序,而不是每个 API 各自从 0 开始。direct library 用法继续支持独立构造器;产品激活不得要求调用方逐个枚举 API。

进程级状态

NEW
  └─ initialize_provider_runtime() ─→ INITIALIZING
       ├─ 成功 ─→ READY
       └─ 任意未完成退出 ─→ FAILED(保存不可变 failure descriptor)

Built-in 注册的可见性

register_builtins() 在自身锁内先导入和验证现有内建模块,在局部变量中形成 API provider batch,再由 API registry 一次发布;失败前不改变 API registry,只有完整成功后才设置 registered。Anthropic/Codex/Gemini auth adapter 继续调用各自现有幂等注册函数,但从 package import 移到这个显式阶段。若 auth adapter 失败,provider runtime 进入 FAILED,公共 get_api_provider() 因生命周期门禁不能观察或使用已发布 provider;进程重启后重建。项目当前把 Anthropic/OpenAI/Google SDK 声明为核心依赖,因此不再用包住整个 provider import 的 except ImportError 静默跳过内部缺陷。

1
确定 profile
沿用现有 CLI/worker profile 解析;生命周期不重新解释 profile。
2
公共入口请求 provider
get_api_provider(api) 先调用 initialize_provider_runtime();Web/worker 可在启动线程前显式调用以便提前报告失败。
3
注册 built-ins
register_builtins() 自身线程安全;完成后才标记注册成功,异常不得永久固化部分状态。
4
读取一次配置并预构造
off 不安装 transform;record 创建共享 sink;replay 完整解析文件并创建共享 replay provider。
5
原子发布并处理调用
registry 返回最终 provider;仅 requires_credentials != False 时解析 credential。

显式启动与统一保证

Web 和 worker 在创建恢复线程、dispatcher 或 provider warm-up 线程前显式调用 initialize_provider_runtime(),使无效 replay 在单线程启动阶段失败。公共 get_api_provider() 仍执行幂等检查,覆盖 Python library 调用和未来入口。auth 的延迟 adapter 注册只调用 register_builtins();它不能因为 replay 文件损坏而失败。

Runtime 构造与 credential 边界

严格离线必须覆盖 create_runtime(),不能只覆盖 stream.py。当前 factory 和 subscription runtime 构造器会在 get_api_provider() 前读取 API key/OAuth。目标实现不重构这些 live runtime:off/record 继续走现有 factory;replay 增加一个 credential-free 分支,只接受调用方显式给出的 provider/model,或 AGENTIC_PROVIDER/AGENTIC_MODEL、配置文件 default_provider/default_model 中已有的非凭证选择。没有这些选择时立即返回 replay 配置错误,不继续执行 CLI 探测或 AuthStore 探测。

replay factory 不实例化 subscription runtime,而是按 PROVIDERS 的 provider→model namespace 纯元数据构造基础 Runtime。plain provider 已有该字段;三个 subscription entry 新增 model_namespaceclaude-code→anthropicopenai-codex→openai-codexgemini-cli→gemini-subscription。API-routed provider 继续使用其模型目录 namespace。这个分支不读取录制文件来推断原始 factory provider:现有 v1 request 只保存 wire-level Model,无法区分 claude-codeanthropic 等共享 wire 的入口,也不能把多 API 文件的首个 model 当成所有后续 runtime 的 identity。请求进入 ReplayProvider 后由现有完整 request comparison 校验最终 Model、context、tools 和 options;不匹配按既有 ReplayMismatch 报告。

范围隔离:当前主工作区存在并行 auth 账号管理重构。本任务不修改三个 subscription runtime 或 credential API,只在 replay factory 分支绕过它们;实现基于独立 worktree,合入时需复核 registry 的最终调用位置。

与 OpenProgram 执行模型的关系

record/replay 是 provider 测试与故障复现能力,不是 workflow 能力。Agent loop、Runtime retry、工具执行、Session 持久化和 DAG 状态仍按正常代码执行;仅 registry 返回的 provider 从 live/record provider 变为 ReplayProvider。因此 replay 可以验证这些上层组件在同一模型事件序列下的行为,但不会恢复历史 Session、重放工具副作用、回滚文件或定义执行步骤。

07 · 录制文件、严格解析与并发写入

格式策略

新 writer 继续写 format_version=1 和现有四类行。call_end.outcome 是可选诊断字段:缺失等同 complete,因此旧 v1 文件和旧 reader 保持兼容;它不改变事件模型或 request 匹配。header 可增加可选元数据;v1 reader 必须忽略未知 header 字段。任何 request/event 的必需字段或语义发生变化时才升级版本。

{"type":"header","format_version":1,
 "recording_id":"20260811T134502Z-f7a29c1d",
 "created_at":"2026-08-11T13:45:02Z","redaction_version":1}
{"type":"request","call_index":0,"model":{...},"context":{...},"options":{...}}
{"type":"event","call_index":0,"event_index":0,"event":{...}}
{"type":"call_end","call_index":0,"event_count":1,"outcome":"complete"}

recording_idcreated_atredaction_version 只用于管理和诊断,不参与请求比较。旧 v1 文件没有这些字段时仍可回放,list/show 从 stat 与内容推导缺失信息。

严格读取不变量

文件级

  • 第一条非空行必须是唯一 header。
  • 每行必须是 JSON object;错误包含行号。
  • format_version 必须是支持的整数。
  • 只接受 header/request/event/call_end。

调用级

  • request 的 call_index 唯一且从 0 连续。
  • event 必须引用已有、未结束的 call。
  • event_index 从 0 连续。
  • 每个 request 必须有且只有一个 call_end。

结束级

  • event_count 等于实际 event 数。
  • outcome 只允许 complete/error/cancelled/abandoned;缺失按 complete 兼容。
  • call_end 后不再接受该 call 的 event。
  • 未知 event type 在 provider 构造时失败,不延迟到中途。
  • 空文件、截断末行和孤立行一律拒绝。

请求级

  • 比较前对 incoming 使用同版脱敏。
  • model/context/options 三段依序比较。
  • 差异保留 call、field、recorded、incoming。
  • 超过录制调用数时拒绝,不回退 live provider。

并发写入合同

  1. RecordingSink 持有进程内 threading.RLock;每条 JSONL 的序列化和 append 在同一临界区完成。
  2. 每次 append 以 os.open(O_WRONLY | O_CREAT | O_APPEND, 0o600) 打开数据文件,并用 openprogram._compat.flock(fd, LOCK_EX) 提供跨进程互斥。
  3. 首次创建 header 与分配下一个 call_index 都在文件锁内完成。最小实现可在 begin_call 时扫描现有 request 的最大索引;这是诊断文件路径,先接受 O(n) 分配,只有测得大文件成为瓶颈时才增加 sidecar counter。
  4. 一条完整 UTF-8 JSON 加换行使用循环 os.write 写完;不得依赖一次 write 写全。
  5. request 和每个 event append 后不强制 fsync;call_end 写入后 fsync。这样崩溃最多留下一个可被严格 parser 识别的未完成 call。
  6. 跨线程/跨进程 event 可以交错,但 call_indexevent_index 把每个调用完整分组。回放顺序按 request 的 call_index。
已知上限:并发请求的开始顺序本身取决于调度。录制保证文件不损坏,不声称让并发调度确定化。需要确定性回放的测试应串行发起 provider 调用;如果未来必须复现并发调度,再引入显式 request correlation,不提前增加该机制。

08 · 0700/0600、路径约束与脱敏

文件系统

脱敏合同

内容风险:凭证脱敏不等于内容匿名化。用户 prompt、模型回复、工具返回、文件片段和个人信息会保留。文档、CLI help 与 show --content 前的提示都必须说明这一点。

09 · 严格离线回放合同

回放的离线保证由结构约束实现,而不是依赖调用方自觉避免网络。

  1. replay transform 对所有已注册和后注册 API 返回同一个 ReplayProvider
  2. ReplayProvider 不保存 original provider,不 import vendor transport,不提供 fallback。
  3. stream.py 必须先取得 provider;当 provider 声明 requires_credentials = False 时,不调用 resolve_provider_key()
  4. 文件在 transform 安装前完整解析和验证;损坏文件阻止 worker 启动,不能启动到一半后才失败。
  5. 未知 API 仍由 registry 返回 missing-provider 错误;不得为了回放动态加载真实 provider。
  6. request mismatch、调用耗尽、未知事件、事件校验失败都直接抛异常;任何失败都不能切到真实 provider。

测试必须同时替换 socket.socket.connectsocket.create_connection 和共享 HTTP client 构造函数为失败函数,并断言正常回放、mismatch、损坏文件和调用耗尽四条路径都没有尝试网络。

10 · 录制文件管理

命令默认输出/行为安全规则
listID、created_at、格式版本、call/event 数、完整性、字节数、当前 active 标记;按创建时间倒序。--json 输出稳定字段。单个损坏文件显示 invalid 与简短错误,不阻止列出其他文件;不输出请求内容。
show默认解析并显示 header、统计、每个 call 的 model provider/api/id、event 数和 complete 状态。--content 才打印完整的已脱敏 JSONL。内容输出到 stdout 前打印 stderr 风险提示;JSON 模式不混入提示文本。
delete删除一个 managed ID。TTY 默认要求输入完整 ID 确认;非 TTY 没有 --yes 时退出 2。拒绝 active selector、外部路径、目录和 symlink;删除不可恢复。
prune按文件 stat mtime 删除早于 N 天的 managed 文件;--dry-run 只列出。输出 matched/deleted/failed/bytes。N 必须为正整数;跳过 active、invalid symlink 和正在持有排他文件锁的文件;没有 --yes 时 TTY 确认,非 TTY 退出 2。

list/show 使用同一 inspect_recording(path) 纯函数,replay 激活也使用同一严格 parser。不能为管理命令另写一套宽松解析器。prune 的年龄来源使用 stat mtime;header created_at 只显示,不作为删除依据,避免手工编辑 header 改变保留行为。

11 · 失败语义

阶段条件异常/CLI 行为是否允许继续
Built-in 注册vendor 模块导入失败、auth adapter 注册失败、批发布中止runtime 进入 FAILED,保存不可变 failure descriptor(stage="builtins");每个调用方得到字段一致的新 ProviderRuntimeInitializationError;等待线程全部唤醒;不得返回 original provider。管理命令可继续
Record/replay 激活mode 未知、file 空、record 外部路径、replay 文件丢失、损坏或版本不符结构化错误日志;registry transform 原子替换为 fail-closed _BlockedRecordReplayProvider;runtime READY 且 snapshot.mode == "blocked"。所有 provider 调用抛同一诊断,指示运行 openprogram recordings status/off;不解析凭据、不访问网络。仅诊断
控制退出初始化线程收到 KeyboardInterrupt/SystemExit状态进入 FAILED 并唤醒等待线程;initializer 抛原控制异常,其他调用方收到 initialization_interrupted;只允许进程重启重试。管理命令可继续
创建同名文件、symlink、目录不可写、无法收紧权限RecordingFileError;不覆盖已有文件。
写入锁失败、磁盘满、序列化失败终止当前 provider 调用并保留已写内容;不能只记录日志后继续 live 调用。
真实 provider开始/中途失败、调用取消或消费方提前关闭生成器finally 写入带 outcome 的 call_end 并关闭源生成器,随后保留原异常/取消语义。整份文件仍可解析;replay 先校验请求,只在消费到该调用时报告对应中断,调用计数随后允许调用方重试并消费下一条录制。按调用
解析JSON、版本、顺序、计数或事件类型无效RecordingFileError(path, line, call_index, reason)
匹配incoming 与 recorded 不同现有 ReplayMismatch 字段保留;CLI/Web 错误链显示 call 与 field。
调用耗尽incoming call 超过已录 callReplayMismatch(field_path="call_index")
回放少调用进程/测试结束但 recorded calls 未消费完保留 assert_consumed();one-shot/program/test 在正常结束时调用并失败。长期 worker 的 status 显示 consumed/total,不在进程运行中提前判错。按入口
管理删除部分文件删除失败逐文件报告;prune 继续其余文件并最终退出 1。

12 · 兼容与迁移规则

13 · 最小实现文件

文件最小改动复用理由
openprogram/providers/initialization.py新增 runtime 状态、Condition、单次 built-in 注册、配置快照、transform 激活和失败传播。生命周期协调独立于 recording 格式与 registry 存储。
openprogram/providers/register.pyregister_builtins() 线程安全,并只在完整执行后标记成功。保留现有 vendor 注册清单,不复制到生命周期模块。
openprogram/providers/api_registry.pyget_api_provider() 在读取 registry 前确保 runtime READY;保留现有 transform 原子发布。所有实际 API provider 获取的共同边界。
openprogram/providers/registry.py三个 subscription entry 增加纯 model_namespace 元数据;create_runtime() 先取得 lifecycle snapshot,仅 replay 增加无凭据的基础 Runtime 分支。off/record 的 live factory、subscription runtime 与 credential 行为保持。
openprogram/providers/__init__.py删除 built-in 注册和 record/replay 激活调用,仅保留导出。消除包导入对运行状态和文件系统的影响。
openprogram/providers/recording.py保留格式与管理函数;激活函数由生命周期显式调用,不再参与 import。不移动已经稳定的录制/回放逻辑。
openprogram/auth/credential_provider.py把 provider side-effect import 改为显式 register_builtins()auth 只需要 adapter 注册,不应激活 replay。
openprogram/webui/server.py在线程启动前显式初始化 provider runtime。保留现有单线程 preload 意图,并把失败边界变为显式 API。
openprogram/worker/runner.pystart_web()、provider warm-up 和 channel 线程之前显式初始化。worker 对无效 replay 配置统一 fail-fast,不允许后台线程独立触发首次初始化。
openprogram/cli.py删除 recordings 环境变量哨兵,继续直接分派管理函数。管理恢复由模块边界保证。
tests/providers/test_provider_initialization.py新增 import purity、状态机、并发、失败原子性、auth/Web 边界测试。生命周期测试与格式、CLI 测试分开。
tests/providers/test_record_replay_cli.py补 status/off 对 missing、corrupt、unsupported-version 文件的真实子进程恢复矩阵。验证用户恢复入口,不用单元 mock 代替启动行为。

不修改 vendor provider、off/record 的 live factory、Web routes、Node CLI 或录制格式。runtime factory 只增加 replay 分支。若实现时发现调用绕过 get_api_provider() 或插件绕过 register_api_provider(),应改回统一边界,不能在 recorder 或生命周期模块增加 vendor 特例。

14 · 验收与测试矩阵

维度用例关键断言
Import 合同隔离子进程只执行 import openprogram.providers不读取 config、不打开 recording、不导入 vendor SDK/auth adapter、不修改 API registry。
首次初始化无配置 / mode off / record / replaybuilt-ins 和配置只处理一次;READY 后 provider identity、credential resolver 次数和现有行为保持。
并发初始化多个线程同时首次 get_api_provider()只有一个初始化执行者;其余线程得到同一 READY provider 或同一 FAILED 原因;无死等。
失败原子性built-in 注册、配置读取、ReplayProvider 构造、transform 发布分别注入异常不返回 original provider,不查询凭证,不构造网络 client;transform 不处于部分发布状态。
入口一致性Web 启动、worker、公共 stream、直接 library provider 获取、auth adapter 加载实际 provider 使用确保 READY;Web/worker 在线程前 fail-fast;auth 只注册 built-ins,不激活 replay。
Registry包装前注册、包装后注册、重复配置、两个 API已有/后续项均 transformed;冲突 transform 失败;record wrappers 共享 sink;replay 共享 call_count。
并发线程两个线程各录多次 call/event每行合法 JSON;call_index 唯一连续;每个 event_index 连续;call_end count 正确。
并发进程两个进程写同一 managed file单一 header、无半行、无重复 call_index、严格 parser 通过。
异常与取消provider 抛异常、任务取消、消费方 aclose()、同步启动失败原控制流保持;源生成器关闭;call_end outcome 和 event_count 正确;整份文件可解析;replay 在对应调用失败而不影响后续计数。
激活失败replay 文件丢失/损坏、未知 mode、非法 record selector应用初始化不抛出;runtime READY,snapshot.mode == "blocked";provider 调用 fail-closed;不解析凭据、不访问网络;管理命令可恢复。
回放计数同一请求先 mismatch 后修正重试mismatch 不增加 call_count;修正请求仍与同一 recorded call 比较;成功或已记录的中断才消费一次。
权限新目录/文件/锁文件、既有宽权限目录、managed 历史 0644 文件、显式外部 0644 文件POSIX 0700/0600;record 与 managed replay 可收紧;外部 replay 只验证并拒绝宽权限,不修改 mode。
路径../、绝对 record path、symlink、目录、外部 deleterecord/delete/prune 拒绝逃逸;replay/show 外部文件只读可用。
脱敏大小写字段、嵌套值、复合 token/key/secret/password 后缀、常见 auth scheme、URL userinfo/query、普通文本凭证字节不在文件;token_count 等非凭证文本保持;record/replay 使用同版规则。
严格格式空文件、坏 JSON、双 header、未知行、孤立 event、索引间隔、重复 end、错误 count、未知 event构造 ReplayProvider 时报告 path/line/call/reason。
严格离线正常、mismatch、耗尽、损坏四条 replay 路径socket/http client sentinel 均未调用;credential resolver 未调用;无 live fallback。
Runtime factory显式 provider/model、非凭证默认配置、无默认配置、plain/subscription providerreplay 使用 entry 的 model_namespace,不导入/构造 runtime_class,不执行 CLI/AuthStore detection,不调用 credential resolver;最终 wire-level Model 仍由 ReplayProvider 严格比较。
CLI 配置record/replay/off/status、无效 selector、配置写并发一次事务更新;失败不改配置;输出 restart 提示;profile 隔离;不初始化 runtime。
恢复命令replay 文件 missing、corrupt、unsupported version 时执行 status/off真实子进程可启动;status 报稳定诊断,off 成功写配置;不需要环境变量哨兵。
管理mixed valid/invalid list、metadata show、content show、active delete、dry-run prune、partial failure稳定 JSON schema;默认无内容;active 不删;结果计数与退出码正确。
旧文件当前测试生成的 header-only v1 metadata正常文件继续回放;结构损坏的旧文件按新严格规则拒绝。
现有 loop多轮 tool loop record → replay事件类型、tool execution 和 final message 与当前测试一致。

实现后最小验证命令

pytest -q tests/providers/test_provider_initialization.py tests/providers/test_record_replay.py tests/providers/test_record_replay_registry.py tests/providers/test_record_replay_cli.py
  pytest -q tests/providers/test_scripted_provider.py tests/providers/test_registry_from_config.py tests/unit/test_usage_stream_chokepoint.py tests/unit/test_auth_methods.py tests/unit/test_web_config_schema.py tests/unit/test_config_write_safety.py
python -m openprogram recordings --help
python -m openprogram config get record_replay.mode
python -m tools.docs_site.build
python -m tools.docs_site.checklinks
git diff --check

15 · 实现与验证记录

状态
范围
证据(设计基线 990bfe36;实现复核 99252205)
已实现
共享 RecordingSink 以线程锁、跨进程 flock、完整 os.writecall_end fsync 写入单一 v1 JSONL;扩展固定脱敏并执行 0700/0600 与 symlink/普通文件检查。
recording.py::RecordingSinkpaths.py::get_recordings_dir;spawn 三进程并发测试严格解析 15 个连续 call。
已实现
一次性 registry transform 覆盖已有和后续 provider;record 模式共享 sink,replay 模式共享一个预验证 provider。
api_registry.py::configure_provider_transformrecording.py::activate_record_replay_from_configtest_record_replay_registry.py
已实现
严格 parser 在构造阶段验证 header、版本、行类型、call/event 索引、call_end/count 和事件模型;replay 无原 provider/fallback,两个 stream 入口先选择 provider 并跳过凭证解析。
replay.py::read_recording_fileReplayProvider.requires_credentialsstream.py;结构失败、credential sentinel、多轮 tool loop 测试。
已实现
record_replay.mode/file 为 next_start settings;CLI 提供 status/record/replay/off/list/show/delete/prune,配置失败回滚新文件,破坏性命令包含 active、symlink、外部路径、锁与非 TTY 确认保护。
config_schema.pycli.pyrecording.py 和 CLI/管理测试。
已替代临时修复
recordings 管理命令不导入或初始化 provider runtime;损坏或丢失 replay 文件时 status/off 可直接恢复配置,环境变量哨兵已删除。
e8c9f645test_off_recovers_from_a_replay_file_that_became_invalid 与无导入副作用子进程测试。
已实现
显式 provider runtime 生命周期、无运行副作用的 package import、线程安全首次初始化、built-in 注册失败的稳定 FAILED 传播、record/replay 激活失败改走 fail-closed blocked provider 且 runtime READY(snapshot.mode="blocked"),auth/recordings/runtime 分离,以及 replay 模式只读模型解析。
4d82d485..99252205;规格复审 PASS,质量复审 PASS;提交态 tests/unit + tests/providers 记录于本次交付说明。
可靠性修订已实现
异常、取消、提前关闭和同步启动失败写唯一带 outcome 的 call_end 并关闭 source;重复 terminal 在首个 terminal 后停止;source close 失败保留。配置激活失败时原子替换已有 registry transform 为 fail-closed provider;mismatch 不消费调用,匹配的中断调用消费一次并报告。
c26df53aa4f27b3bfe7275466595d444;公共入口 RED/GREEN、规格复审 PASS、质量复审 PASS。
兼容边界
保留 v1 构造器、属性、异常、旧 header 和 mismatch 行为;新增严格检查会拒绝旧 reader 曾忽略的损坏行。严格离线仍只覆盖 LLM provider 层;跨进程调度顺序不保证复现。
test_record_replay.py 继续覆盖库级录制、两轮 tool loop、版本拒绝和字段差异。
明确不做
Web UI、全进程 HTTP 录制、tool/MCP 录制、模糊 request 匹配、自动网络 fallback、自动上传、Windows 专项实现或验证。
超出本阶段边界。
验证完成
相关 pytest、完整 unit/providers、changed-file Ruff、docs build、link checker、CLI smoke 与 diff whitespace。
仓库根级 pytest 另被既有缺失模块 openprogram.functions.agentics.test_framework 阻断于 integration collection;本变更未修改该入口。

设计来源:当前 OpenProgram provider 实现、CLI/config 入口、测试,以及仓内 references/pi-ai 快照。更新实现状态时必须附代码符号、测试或 commit;不得把“设计已写”标为“功能已实现”。