43 KiB
Pi 一等支持:盲审规范契约
状态:规范性(normative)
用途:本文件是 Pi 一等支持累积盲审的唯一产品与行为契约。审查只使用本文件列明的 authority、artifacts 与验证命令。
优先级:本文件若与
pi-support-contracts-zh.md冲突,以后者为准;Pi 原生格式与加载行为以本节固定的官方源码提交为准。
0. 固定来源与术语
0.1 源码固定点
Pi 实现 authority 与 2026-07-31 UTC Phase 0 远端观察值必须分开记录:
| 来源 | 提交 | 规范用途 |
|---|---|---|
earendil-works/pi |
ab366ebe94cacd419d986be454f12b1b9913aaca |
唯一 Pi implementation pin;schema、composer、transport 与源码注释 authority |
earendil-works/pi remote HEAD |
977ec833bbb86e245057e9162dbc1443c7b6e707 |
Phase 0 观察值;不是第二个 implementation pin |
CallmeLins/pi-switch |
5cfa2e8d3f6657b0508ee66ea63acdedfb53388d |
产品层 gateway/failover 参考,不定义 Pi 原生契约 |
Ginkgoooo/pi-cc-switch-provider |
740c06692b15ad891e6d3ac3ee24c053d2626a9a |
互操作边界参考,不定义主架构 |
Wing900/Pi-switch |
e2cc7f7837136277d317b742b650f402048ca80d |
Provider/Model 桌面交互参考,不定义数据契约 |
实现与测试中的 Pi schema、文件加载顺序和行为注释只能标明 implementation pin。 社区项目和远端观察值不得覆盖该 authority。
0.2 术语
logical_app_type:产品归属、UI 与筛选;Pi 的值为pi。wire_api_family:请求实际使用的协议 family。input_token_semantics:wire family 对输入与缓存 token 的计数语义。managed aggregate:SQLite 中由 CC Switch 管理的 Provider 主行及全部持久化子行。native entry:当前 Pimodels.json中的一个原生 provider key。projection claim:CC Switch 对models.json中一个精确 key 的设备本地所有权证据。desired_enabled:用户希望 CC Switch 为 Pi 部署某 Skill。owned_deployment:设备本地 ledger 证明目标由 CC Switch 部署。effectively_discovered:Pi 按当前 discovery 规则实际发现该 Skill。gateway token:设备安装级 loopback bearer 凭据。server_generation:当前进程 listener 实例代次。catalog_epoch:catalog mutation 与请求/writeback fence。
上述概念均为独立状态;不得通过 app_type、文件存在、内容相同或路径相同互相推导。
1. 功能范围
1.1 必须交付
- Pi 应用注册、显示/隐藏和切换;
- Provider/Model CRUD、复制、排序、预设、拉取模型、默认 Provider/Model;
- Pi 原生全局 Skills;
- 全局
AGENTS.md; - 全局
SYSTEM.md与APPEND_SYSTEM.md; - 全局 prompt templates;
- Pi sessions 的发现、树形 active branch 展示、恢复和显式删除;
- loopback gateway、同 family 请求级 failover、circuit breaker 与 SSE;
- usage 明细、聚合、费用、Pi 筛选;
- portable backup、同步、restore 与运行态 reconcile;
- en、zh、zh-TW、ja 四语 UI。
1.2 明确排除
- 跨 API family 转换;
- User-Agent 伪装;
- Pi
auth.json或 OAuth 生命周期管理; - MCP;
- themes、keybindings;
- project-local resources、packages 与 trust;
- extensions/package 的 inventory、安装、启停、升级或删除;
- agent 内自动模型切换。
extensions 仅允许保留本文件第 16 节的设计契约;不得出现 Rust、SQL、TS、UI、IPC、capability flag 或空占位实现。
2. 数据所有权
| 数据或资源 | 权威来源 | 允许操作 | 禁止操作 |
|---|---|---|---|
| Pi managed Provider aggregate | SQLite | aggregate CRUD、默认与 route state | 把它视作 Pi 全部 provider universe |
pi_provider_projections |
当前设备 SQLite | 记录精确 provider key claim | 以前缀、内容或命名猜所有权 |
models.json |
Pi、用户与第三方共享 | 原子 patch 已 claim 的精确 key | 整体覆盖或删除 unowned key |
settings.json(Pi) |
Pi 与用户共享 | 原子 patch 明确的默认字段 | 重写未知字段 |
auth.json |
Pi | 只判断凭据能力是否可用 | 读取、复制、导出、覆盖凭据 |
skill_deployments |
当前设备 SQLite | 证明 CC Switch 的 Pi deployment | portable/sync 搬运或由内容猜记录 |
| Pi Skill 目录 | Pi、用户与 CC Switch 共享 | 仅操作 identity/digest 匹配的 owned target | 覆盖或删除 unowned target |
AGENTS.md |
用户与 CC Switch 共享 | revision-safe library 投影 | 以 DB enabled 状态覆盖外部修改 |
SYSTEM.md / APPEND_SYSTEM.md |
当前设备用户共享文件 | revision-safe 直接 CRUD | 建 DB enabled mirror 或进 portable sync |
| prompt templates | 当前设备用户共享目录 | 用户指定的单文件 CRUD | 递归扫描或删除未知文件 |
| Pi sessions | Pi | 只读解析、用户显式恢复/删除 | 修改 transcript |
| gateway token | 设备 settings 文件 | 本地生成、持久化、显式旋转 | 进入 DB authority、URL、日志、export/sync |
| runtime snapshot | 当前成功发布的 generation | immutable request lease | 请求中回读可变 DB/global Provider |
所有 ~/.pi/agent 路径必须经一个统一的 get_pi_agent_dir() 解析并尊重 PI_CODING_AGENT_DIR。Session 另按第 9 节解析 session root。
3. Managed Provider 与 capability
3.1 API family
本范围只代理下列四个 Pi 原生 family:
anthropic-messagesopenai-completionsopenai-responsesgoogle-generative-ai
其他合法 Pi native family 可显示为 unsupported/direct-only 诊断,但不得被误路由。
3.2 两级模型
控制面必须表达:
PiManagedProviderConfig
provider baseUrl?: string
provider api?: PiManagedApiId
apiKey?: unresolved expression
headers
models[]
modelOverrides
compat
PiManagedModel
id
name?
model baseUrl?: string
model api?: PiManagedApiId
contextWindow?: PiNumber
maxTokens?: PiNumber
remaining native shaping fields
唯一继承规则:
effective api family = model.api ?? provider.api
effective endpoint = model.baseUrl ?? provider.baseUrl
每个 managed model 必须解析出 family 与 endpoint。Provider 默认字段可以为空,只要每个 model 自身完整。一个 Provider 可以包含多个不同 family 的 model;路由单位是 (provider_id, model_id, effective_api_family)。
effective_pi_model() 是投影、诊断、runtime、routing 与 failover 的共同解析入口。不得在调用点复制继承规则。
PiNumber 保持 Pi/JavaScript JSON number 语义;整数与小数都可无损经过
raw→managed→projection round-trip,不得以 u64 DTO 收窄。Managed 额外的正值
要求属于 managed-layer narrowing,不改变 raw validity。
Managed API ID 是 opaque non-empty string,必须无损 round-trip。四 family enum 只能存在于 gateway 层;新的 Pi-valid API ID 本身不构成 managed rejection。
3.3 capability 独立性
每个 native entry 必须独立表达分层结果:
- raw native validity:
valid | invalid | unknown; - managed assessment:
manageable | unsupported; - composition status:
composed | failed | unknown; - management status:
importable | managed | unsupported; - gateway status:
proxyable | direct_only | unknown。
importable = raw validity == valid AND managed assessment == manageable
proxyable = raw validity == valid
AND composition status == composed
AND API family、credential 与 headers 均受 gateway 支持
后层不得把 raw invalid/unknown 提升为 importable/proxyable。valid 不蕴含
manageable;manageable 不蕴含 proxyable;direct-only managed entry 不得进入
failover 候选。
Reason 必须是 {layer, code, json_pointer?}。raw schema failure、managed
narrowing、composition failure 与 gateway limitation 使用不同 layer 和稳定 code。
4. Native catalog diagnostics 与 import
4.1 只读检查
检查必须每次读取当前 models.json,不得写文件、数据库、claim、默认值或 runtime。每个原生 key 都必须产生诊断,包括:
- built-in overlay;
- modelOverrides-only;
- 完整 custom catalog;
- extension overlay;
- malformed/unknown shape。
每项至少返回 provider key、可选显示名、确定性 fingerprint、entry kind、四层判定、 management/gateway capability 和结构化 reason。
Layer 1 只运行 vendored pinned TypeBox schema 的 JSON evaluator:
- 直接消费 raw JSON,不先反序列化为 managed 类型;
- 支持 provenance allowlist 中的 required/optional、absent/null、object/record/array、
union/literal/enum、string/boolean/Number、additionalProperties,以及完整递归
compat; - 遇到未支持 operator、custom validator/transform、pin/hash 漂移或适用 schema 不确定时返回 unknown;
- 不解释 built-in/extension registry、OAuth、env、
!command、文件、网络、 credential、composer fallback 或 gateway。
Layer 2 独立评估 managed narrowing:model id 唯一非空、override key 闭合、present URL 为非空绝对 HTTP(S)。API ID 以 opaque string 保留。Pi Number 保持 number 语义,小数不得因整数 DTO 而变成 raw invalid。
Layer 3 只组合显式 custom catalog、显式 models 与 matching overrides。默认值与
precedence 来自实际执行 pinned Pi 的 vendored composer oracle。Composer 直接消费
raw-valid value,不先转换为 managed DTO;未知 provider/model/override shaping
字段、thinking map 与 compat 必须以 JSON value 无损保留。它是 credential-blind
纯函数:OAuth/env/!command/文件/网络表达式只作为 deferred material 保留,不能
执行或令 composition 变为 unknown。只有必须依赖但无法执行的
built-in/extension catalog/default-model 语义才 fail-closed 为 unknown,并返回
精确 catalog-required reason。
Layer 4 才把 composed API ID 缩窄到四个 gateway family,并在 capability 阶段构造
完整 CandidateHeaderPlan。非法/protected/hop-by-hop header 使候选不可代理;
request 阶段必须在任何网络 I/O 前逐候选物化 deferred auth、tenant、自定义与
protocol header,失败只淘汰当前候选,不得复用前一候选的 URL、Host、auth 或
header。Gateway 不支持已知 opaque API ID 时为 direct-only,不得把 composition
降为 unknown。
固定状态矩阵:
- 未知 API 的显式 custom catalog:raw valid + composed + importable +
direct-only(
api_family_not_gateway_supported); - built-in/extension 因缺 catalog/default-model context:raw valid + unknown composition + unknown gateway。
未知或不支持的 entry 必须可见,不得只记录日志。
4.2 显式 import
只有 importable entry 可导入。Import 必须在 Pi switch boundary 内:
- 重读精确 key;
- 重算并比较 caller 提供的 fingerprint;
- 原子插入完整 aggregate 与精确 claim;
- 不写
models.json; - 不改默认 Provider/Model;
- 不解析 env、
!command或 OAuth 凭据; - 不自动加入 failover queue。
fingerprint 变化、claim collision、unsupported shape 或 DB failure必须零副作用。成功重试不得生成重复 Provider 或 claim。
5. Provider aggregate 与数据库 ledger
5.1 Aggregate hydration
Provider DAO 必须提供完整 aggregate 的单项与全量读取。旧的 Provider 单行读取只能 从 aggregate API 转换。Aggregate 只用于 read、rollback snapshot 与 restore,不是 通用 mutation DTO。
- 单项与全量读取对同一 Provider 必须逐字段一致;
- 删除
save_provider()、save_provider_row_on_tx()、全部upsert_provider*、通用 aggregate save 与同签名兼容壳;结构扫描必须证明生产 与 integration test 真实语法中禁止符号为零; Provider、ProviderAggregate与 hydration 后的ProviderMeta.custom_endpoints都不是主行 write DTO;不得提供从 read DTO 到 write DTO 的隐式From/Into;- write surface 只暴露
create_provider(NewProviderAggregate)、update_provider(&ProviderKey,&ProviderRowUpdate)、rename_db_only_additive_provider(RenameProvider)与add/remove/touch_provider_endpoint六类类型化操作; ProviderRowUpdate不得含id、endpoint 集合、is_current、in_failover_queue、sort_index;update 恰好命中一行,零行返回 NotFound, 永不转为 create;- create 在同一事务 strict INSERT 主行与全部 initial endpoints;重复 Provider、 endpoint 或任一约束失败时主行、endpoint、current/failover 状态均零副作用;
- existing-provider 表单与 update IPC 不携带 endpoints;后端收到非空
custom_endpoints必须显式拒绝,create IPC 在 service boundary 拆为initial_endpoints; CustomEndpoint.added_at在 Rust、IPC 与 TS 全链路 nullable;不得使用 epoch 0、COALESCE或 unwrap default 代替 NULL;- add/remove/touch endpoint 使用精确行 mutation;touch 影响零行返回 NotFound;
- rename 只允许 OpenCode/OpenClaw 的 DB-only、非 live、非 OMO additive
Provider;单事务 strict INSERT 新主行、复制全部 endpoint 的
url/added_at/last_used(含 NULL)、删除旧主行;目标冲突或不允许的来源均整体 失败。provider_health可丢弃,usage/history 保留旧 provider id; - live import、seed、universal sync 等 reconcile 必须先读取,再显式 strict create 或 strict update;并发 create 冲突不得退化为 overwrite;
- exact endpoint replacement 只允许 switch-lock 串行的 compensation coordinator
调用
pub(super)/sealedrestore_provider_aggregate_on_tx(); - delete/restore snapshot 必须包含 Provider 主行、endpoints、projection claims 与 Pi route state;
provider_health可重建,不属于 rollback snapshot。
针对 providers / provider_endpoints 的 production DML 只允许存在于 provider
row/state DAO、endpoint DAO、schema migration 与 canonical data copier。结构扫描
必须区分 DML/DDL/SELECT/普通字符串、忽略 #[cfg(test)],并以禁止符号、越权 DML
的负向 fixture 证明规则会失败。
认证测试必须经过真实 ProviderService:
- create 多个 initial endpoints 后完整 hydration 集合完全一致,duplicate create 零副作用;
- stale update 前后的公开 endpoint add/remove/touch 全部存活,非空 endpoint update payload 显式拒绝;
- rename 成功保留 endpoint 与 NULL 时间戳;target conflict、live、OMO、非 additive rename 均零副作用。
5.2 Projection ledger
projection claim 是设备本地状态:
- 精确绑定 provider key;
- 不进入 portable export/sync;
- restore 时保留 live device 的 ledger;
- stale claim 清理失败必须显式失败;
- claim collision 不得覆盖原生 entry。
5.3 Skill ledger
表的规范形态:
CREATE TABLE skill_deployments (
app_type TEXT NOT NULL CHECK (app_type = 'pi'),
skill_id TEXT NOT NULL,
destination TEXT NOT NULL,
destination_key TEXT NOT NULL,
method TEXT NOT NULL CHECK (method IN ('symlink', 'copy')),
source_identity TEXT NOT NULL,
deployed_digest TEXT,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
PRIMARY KEY (app_type, skill_id, destination_key),
UNIQUE (app_type, destination_key)
);
destination是部署时词法规范化的绝对路径;destination_key按目标卷大小写语义生成,不使用 SQLiteNOCASE;Auto必须落为实际symlink或copy;- 不加 cascade FK;
- portable/sync skip 且 restore 保留 live device rows;
- 同一 skill 可在 root relocation 期间短暂拥有新旧两条记录。
6. Catalog mutation 状态机
Provider mutation 集合:
CreateProvider(provider, initial_endpoints)
UpdateProvider(provider)
AddEndpoint(provider_id, endpoint)
RemoveEndpoint(provider_id, url)
DeleteProvider(provider_id)
ImportNative(provider_key, expected_fingerprint)
SetDefault(provider_id, model_id)
Provider form 的 update submit 不得携带 endpoint 集合;endpoint 控件只调用专用
mutation。last_used 当前不影响 routing,使用精确单行 touch 且不进入 catalog
epoch;若将来参与 routing,必须升级进入 switch boundary。
所有 Pi Provider CRUD、endpoint CRUD、default、import、route/failover queue、takeover start/stop 与 database restore 必须共享一个 switch boundary,锁序固定为:
Pi switch lock
-> listener lock
-> begin odd catalog epoch / close Pi admission
-> load complete PiCatalogSnapshot
-> short DB transaction
-> atomic exact-key projection
-> build runtime from the same hydrated catalog
-> publish even epoch
-> activate listener generation
不得跨文件 I/O 持 SQLite connection mutex。Projection helper 不得自行开始 epoch。
纯排序只取得 Pi switch lock、执行单个 DB 事务并原子发布下一份 even-epoch runtime;不得取得 listener lock、进入 odd epoch或修改 models.json。
6.1 成功
DB、精确 projection、runtime 与 listener 必须指向同一 catalog generation。旧 request lease 可以完成响应,但 epoch 不匹配时不得写 usage/health。
6.2 失败
| 失败点 | 必须结果 |
|---|---|
| DB mutation 前 | DB/文件不变,旧 runtime 以新 even epoch republish |
| DB 后、projection/runtime 失败,完整恢复成功 | aggregate、claims、route state 与精确文件集合恢复;返回原错误;authoritative_state=previous_restored |
| DB restore 事务失败 | restore 事务自身回滚;mutated DB 为 authority;按实际 DB reconcile;authoritative_state=mutated_database_authoritative |
| DB 已恢复、文件恢复失败 | 旧 DB 为 authority;Pi admission 保持关闭;authoritative_state=projection_pending |
任何分支都不得返回假成功,不得将旧 runtime 发布到 mutated DB 之上。
6.3 故障注入
必须覆盖:
- 带多个 endpoint 的 Provider 删除后 projection replace 失败;
- rollback-reject trigger 与 projection failure 同时发生;
- projection 阻塞时并发 edit、default、restore 与请求;
- restore staging 与 live device ledger 的 commit-boundary race;
- stale epoch usage/health writeback。
7. Skill 三态与安全部署
API 必须独立返回:
desired_enabled
owned_deployment
effectively_discovered
ownership
discovery
issue
UI toggle 只绑定 desired_enabled。原生发现但未 owned 的 Skill 显示“原生 Skill 活跃”,toggle 不得点亮。
7.1 Enable
在共享 deployment mutex 内:
- 解析 source identity 与目标;
- 对同名目录、同内容 copy、指向 SSOT 的 symlink 一律视为 unowned collision;
- 使用同目录临时路径部署并 atomic rename;
- 在一个 DB 事务中写 ledger 与
desired_enabled=true。
collision 必须拒绝且不改目标、ledger 或 desired。
7.2 Disable
- 先持久化
desired_enabled=false; - 只有 ledger 存在且当前 identity/digest 匹配时删除目标;
- 成功删除后删 ledger。
owned copy 漂移或 symlink retarget 后必须拒删、保留 ledger并显示冲突。
7.3 Crash 与 relocation
- 部署完成但 ledger 写入前崩溃:目标为 unowned orphan;不得自动删除或收养;
- agent root 变化:旧记录显示
stale_destination; - reconcile 先部署并登记新目标,再只在旧 identity/digest 匹配时清理旧目标;
- toggle、sync-all、uninstall、Skill 更新和 storage migration 共用同一 mutex 与判断。
Discovery 必须遵循 frontmatter name、必填 description、root file/递归规则、ignore 文件、位置优先级与 effective-name first-wins shadow。
8. 原生指令文件与模板
8.1 三种文件
GlobalContext -> <agent-root>/AGENTS.md
SystemOverride -> <agent-root>/SYSTEM.md
SystemAppend -> <agent-root>/APPEND_SYSTEM.md
只有 SystemOverride 与 SystemAppend 暴露 direct IPC replace/delete。GlobalContext 只能由现有 Prompt library service 投影。
官方固定提交的拼装契约:
SYSTEM.md存在时替换 Pi 默认 system prompt;APPEND_SYSTEM.md的内容追加在所选 default/SYSTEM prompt 之后;- context files 再作为 project context 追加;
- 已运行进程只在其资源 reload/rebuild 边界观察变化,UI 不得承诺即时影响现有会话。
8.2 文件状态与并发
- 文件存在即 active,不存在即 inactive;
- 不提供 Switch,不建 DB enabled 字段,不使用空内容、改名或
.disabled表达停用; - whitespace-only replace 必须拒绝;
- replace/delete 必须带
exists + sha256expected revision; - 外部修改后旧 revision 必须 conflict;
- 三文件操作共用一个进程内 mutex;
- 只接受 UTF-8 regular file,最大 1 MiB;
- symlink、目录、设备文件 fail closed;
- 写入使用同目录临时文件、flush/fsync 与 atomic rename,并保留既有权限;
- agent root 变化时不迁移或清理旧 root;
- 隐藏 Pi、关闭 gateway、切 Provider 或恢复数据库均不得删除这些文件。
8.3 Library 与 templates
- Prompt library 只投影
AGENTS.md; - live
AGENTS.mdrevision 偏离最后投影内容时,disable/switch/clear 必须 conflict并保留文件; SYSTEM.md/APPEND_SYSTEM.md不进prompts表、Profile、portable backup 或自动同步;- templates 只位于
<agent-root>/prompts/*.md,非递归; - slug 必须是 Pi 可调用的单 token 安全文件名,拒绝空白、路径遍历、separator、控制字符、保留名及与 agent-root 指令文件混淆的名字;
- template 名为
SYSTEM.md时仍是模板目录资源,UI 必须显示完整路径以区分。
9. Sessions
- Pi JSONL 按 entry
id/parentId形成树; - UI 展示 header 指定/current leaf 的 active branch,不混入 abandoned branch;
- session id、cycle、entry 数、文件大小、UTF-8、symlink 与路径均有 fail-closed guard;
- 恢复使用
pi --session <path-or-id>,并使用完整进程树的 timeout/cancel; - 删除只在用户显式确认后删除精确 session regular file;
- root 优先级必须明确处理
PI_CODING_AGENT_SESSION_DIR与settings.json.sessionDir; - relative
sessionDir无法从全局 UI 确定启动 cwd 时返回“不可枚举/project-relative”状态,不得回退扫描默认 root。
10. Device settings 与 gateway token
10.1 Settings 文件
CC Switch device settings 必须使用同目录临时文件:
- 创建/校验 regular file;
- Unix 权限收紧为
0600; - 写入完整内容;
- flush/fsync;
- atomic rename;
- 必要时同步父目录。
不得使用 .truncate(true) 直接写 authority 文件。
10.2 Token 生命周期
- gateway token 存于 device settings 文件;
- 首次生成后跨进程重启稳定;
- 只有显式“重置网关凭据”操作旋转;
- reset UI 必须提示运行中的 Pi 会话需要重启;
GatewayToken的 Debug/Display 脱敏;- bearer 比较采用常量时间;
- frontend settings DTO 清空 token;
- save merge 无条件保留 existing token;
- TS settings 类型不得包含 token。
10.3 Legacy 与 export
- SQLite
settings.pi_gateway_token仅可在 live DB 启动迁移时读取; - device 文件已有 token 时 device 胜出,并删除 DB key;
- staged import/portable restore 直接删除远端 key,不得 adopt;
export_sql_string()与export_sql_string_for_sync()分别断言产物不含 token key和实际 value;- token migration 在文件 durable write 后、DB delete 前崩溃时必须幂等。
11. Listener、runtime 与 takeover
Bind/reconcile 顺序:
bind exact loopback host/port
-> 持久化首次动态端口
-> 读取稳定 token
-> close Pi admission
-> compare exact owned projection
-> mismatch 时以同 token atomic reconcile
-> publish immutable runtime(origin + token + catalog)
-> activate listener generation
- 重启必须恢复同 host/port/token;
- 已占用的固定旧端口必须显式失败,不得暗换端口;
- bind、settings durable write 或 projection reconcile 失败时 token 不旋转、Pi admission 不打开;
- listener 可以继续服务其他 app;
- desired takeover 保留并显示 degraded/pending;
- 停止 takeover 先关闭 admission,再恢复 direct projection;
- database restore、proxy start/stop、端口更新与 catalog mutation 使用同一锁序。
每个请求取得 immutable runtime lease,包含 origin、token、catalog、server generation 与 even catalog epoch。旧 generation 不得 admission;旧 epoch 不得 writeback。
12. Usage semantics
12.1 类型与 authority
InputTokenSemantics
1 TotalIncludesCacheBuckets
2 FreshExcludesCache
StoredInputTokenSemantics
Live(1|2)
3 LegacyTotalIncludesCacheRead
4 UnknownLegacy
- 新请求由 wire adapter/parser 在 request context 创建时提供 live semantics;
- 落库 authority 是
proxy_request_logs.input_token_semantics; - logical
app_type不参与语义选择; - request-id collision equality 与 dedup hash 包含 semantics;
- spawn 后移动到 logger 的值来自 admission lease,不受后续 catalog 变化影响;
- live writer 缺 semantics 必须拒绝落库。
12.2 Fresh input
- Anthropic family:
input_tokens已是 fresh,使用FreshExcludesCache; - OpenAI Completions、OpenAI Responses、Google family:input total 包含 cached bucket,fresh 为 total 减 cached,使用
TotalIncludesCacheBuckets。
费用、明细、dedup、backfill、rollup 与前端 fresh 展示必须消费同一 stored semantics。
12.3 Migration
schema migration 是唯一允许从旧 app_type 推断的路径:
- 旧值
0且 app 为 codex/gemini/grokbuild ->3; - 其他已发布 app ->
2; - 意外旧 Pi row ->
4,不得猜 family或自动回填费用; - rollup 旧
0->2; - migration 后所有相关表不得残留
0; - typed DAO、runtime decoder 与 import 拒绝
0; - 所有 session import raw SQL 显式写 semantics。
前端 KNOWN_APP_TYPES 包含 pi,但该列表只用于筛选。
13. Routing、candidate materialization 与 failover
13.1 Eligibility
eligible(primary, candidate, route) :=
candidate exposes route.model_id
AND effective_api_family(primary) == route.api_family
AND effective_api_family(candidate) == route.api_family
AND canonical_request_wire_profile(primary)
== canonical_request_wire_profile(candidate)
AND canonical_protocol_header_profile(primary)
== canonical_protocol_header_profile(candidate)
不进入 equality:
- scheme、host、port、base path;
- provider/model
baseUrl; - API key 与认证 header;
- tenant 与普通自定义 headers;
- display、pricing、排序、默认值。
进入 wire equality:
- 已知 request shaping:
compat、reasoning、thinkingLevelMap、input、contextWindow、maxTokens等; - 除明确 transport/auth/display denylist 之外的未知字段;
- 已知 protocol header 的最终有效值。
JSON object key 顺序不敏感;array 顺序敏感;数字与字符串不宽松等价。
13.2 Header 分类
| 类别 | 行为 |
|---|---|
| Gateway/HTTP owned:Host、framing、hop-by-hop、proxy trace、gateway bearer | 剥离或重建,不进 equality |
Protocol:anthropic-version、anthropic-beta、openai-beta、openai-version |
最终有效值进 equality |
Candidate auth:Authorization、x-api-key、x-goog-api-key |
每次 attempt 延迟解析 |
| Candidate tenant:organization/project headers | 每次 attempt 解析 |
| Provider custom headers | 每次 attempt 解析 |
| Incoming request-local | 统一过滤后复制到每个候选,不进 equality |
未知配置 header 视为 candidate-local。已知 protocol header 使用不可预测 !command 时,该候选 failover-ineligible。protected 与 hop-by-hop header 不得由配置覆盖。
13.3 Candidate materialization
- 每个候选先经
effective_pi_model(); - 复用对应 family adapter 的 URL builder;
- 最终
Host从该候选最终 URL 重建; - auth、tenant、custom header 均来自当前候选,不得从 primary 串值;
- 同一序列化请求 body 字节等价地 clone 给每个候选;
- 不得做候选级 body/model 改写;
- command-valued secret 需要 process-group/job-object tree kill、整体 timeout、有界 stdout/stderr,且日志不得包含 resolved secret。
13.4 Retry 与 SSE fence
允许切换:网络错误、timeout、401、403、404、408、409、429、非结构性其他 4xx 与 5xx。
禁止切换:400、405、406、413、414、415、422、501。
SSE 只有在首个语义事件提交给 Pi 前可切换;comment/keep-alive 不算语义提交。语义事件后失败不得 replay。
Failover 只影响当前请求,不改全局 current Provider、默认值、live config 或 UI current state。
13.5 必须存在的矩阵测试
pi_route_identity_matrixpi_cross_origin_materialization_matrixpi_wire_profile_canonical_matrixpi_protocol_header_profile_matrixpi_four_family_candidate_materializationpi_failover_error_policy_matrixpi_sse_commit_boundary_matrixpi_snapshot_epoch_race
14. Shared-file 与 restore 安全
- JSONC/CST patch 保留 comments、unknown fields 与 unowned entries;
- 只修改 manifest-owned exact keys;
- 文件写入均为同目录 temp + durable atomic replace;
- path traversal、symlink、目录、设备文件和超限内容 fail closed;
- 删除前后重新执行
symlink_metadata/identity check; - portable export/sync 不包含 projection ledger、skill ledger、gateway token、SYSTEM/APPEND;
- restore 完成后在 Pi switch boundary reconcile DB、projection、runtime 与 listener;
- 任何 reconcile 失败都必须返回明确 authority/admission 状态。
14.1 Canonical schema authority
src-tauri/src/database/schema.rs 的 current-schema factory、current DDL 与
migration chain 是完整 canonical schema authority。CanonicalTableSpec /
SchemaInvariantSpec 机械描述 Provider/endpoint/device-local ledger 的承重
invariant;tests/fixtures/pi/canonical-schema-manifest-v1.json 是由这些代码 spec
产生并由测试锁定的审查快照。其余 current user table 由下述穷举
RestoreTableSpec 锁定恢复策略与固定列。Fixture 不是运行时 authority,也不能
认证 imported schema;外部 schema 无论通过多少结构比较都不得获得 publish 资格。
类型屏障固定为:
struct UntrustedScratch(Connection);
struct CanonicalStage(Connection); // 私有字段,仅 schema.rs current factory 构造
Publish 只消费 CanonicalStage;不得接收裸 Connection,也不得存在
UntrustedScratch 到 publish 参数类型的转换。Canonical stage 是全新磁盘临时库,
由 schema.rs 从空库建立当前全部 tables/indexes/collations、PK conflict policy、
CHECK、FK action、canonical trigger/view、PRAGMA 与 user_version,不得复制 source
sqlite_schema 中的任何对象。Restore 生产代码不得包含
CREATE TABLE/INDEX/TRIGGER/VIEW 字符串。
RestoreTableSpec 必须对每张 current user table 恰好指定一种策略:
PortableIncoming:从 input 固定列逐行 decode 与复制;PreserveLive:设备本地 rows 只从 live DB 复制;RebuildRuntime:运行态不从 input 恢复;SeedCanonical:由 current schema/seeding authority 重建。
结构测试必须证明 canonical user-table 集合与 policy 集合完全相等;新增表没有策略
必须失败。每个 spec 固定列名、复制顺序、NULL/storage type、row validator 与父子
拓扑;不得读取 source PRAGMA table_info 决定搬运列。
14.2 SQL 与 binary 共用 publish 管线
两入口统一执行:
untrusted input
→ UntrustedScratch
→ scratch 中 current migrations
→ RestoreTableSpec 固定列逐行 decode
→ schema.rs 从空库创建 CanonicalStage
→ live device-local copy
→ canonical validation
→ SQLite Backup API publish
- Data transfer 在
foreign_keys=ON的单个 canonical-stage transaction 中使用 plain INSERT;禁止SELECT *、INSERT OR IGNORE、INSERT OR REPLACE、REPLACE、目标端ON CONFLICT DO UPDATE与复制sqlite_sequence; - duplicate canonical key、弱 source UNIQUE 产生的重复行、FK orphan、storage class 或 decoder 错误必须整体 abort;
- Provider 行写 stage 前使用生产 hydration 的同一 decoder 验证
settings_config、meta、数字与 NULL;未知 JSON 字段必须保留,不得通过 重序列化丢失; pi_provider_projections、skill_deployments只从 live DB 固定列复制,永不从 source;复制与 publish 持既定 live/switch boundary,失败保持 live 逐行不变;- AUTOINCREMENT 表可复制显式 ID,但不复制
sqlite_sequence,并验证下一 INSERT 的 ID 大于现有最大值; - WAL/SHM 永不复制、替换或重命名;binary source 只经 SQLite Backup API 克隆到 private scratch,publish 同样只用 Backup API。
Scratch 必须在不可信执行前关闭 trigger、关闭 trusted_schema、开启 defensive、
设 attached DB limit 为 0,并设置 SQL/value/page-count limits、有限 VM-step
budget 与取消检查。不注册应用函数、不启用 load-extension;authorizer 至少拒绝
ATTACH/DETACH、vtable、load-extension、writable-schema、非白名单 PRAGMA 与
unknown action。外部 batch 后删除 source trigger/view/显式 index,再执行可信
migration。未来 user_version 立即拒绝;0..=SCHEMA_VERSION 全部走现有 migration
chain,缺固定列或 row decode 失败即 abort。
SQL 文件入口使用 symlink metadata、regular file、nofollow open、opened/current
identity 与有限读取;binary 使用 READ_ONLY | NOFOLLOW | PRIVATE_CACHE 与同样
检查。边界为 MAX_SQL_IMPORT_BYTES=256MiB、
MAX_BINARY_RESTORE_BYTES=2GiB、MAX_SCRATCH_BYTES=2GiB。
Publish 前必须执行 integrity_check、foreign_key_check、全表 decoder sweep 与
行为断言,至少证明 providers PK collation 为 BINARY、精确 duplicate 为 ABORT、
duplicate 不会 REPLACE/级联删 endpoint。
SQL import_sql_string* 与 binary restore_from_backup 两个公开入口必须参数化覆盖:
- NOCASE/REPLACE PK、弱 endpoint UNIQUE/FK、trigger/view/index injection 后只发布 canonical schema;
- 不可解码 Provider JSON、错误 storage class、duplicate canonical key、FK orphan abort 且 live/device-local rows 不变;
- NULL
added_at等 nullable 值与显式 AUTOINCREMENT ID 无损往返; - symlink、目录、FIFO、N+1、VM/page budget、ATTACH/VACUUM INTO 在 publish 前 失败;
- 每个
user_version=0..SCHEMA_VERSION有 migration sentinel,current、上一版与 最老支持版同时覆盖 SQL 与 binary。
14.3 Native oracle provenance
盲审与 CI 消费以下 vendored artifacts,不运行 Node、不联网:
tests/fixtures/pi/native-oracle/provider-schema.snapshot.jsontests/fixtures/pi/native-oracle/raw-oracle-v1.jsontests/fixtures/pi/native-oracle/composer-oracle-v1.jsontests/fixtures/pi/native-oracle/transport-oracle-v1.jsontests/fixtures/pi/native-oracle/field-coverage-v1.jsontests/fixtures/pi/native-oracle/provenance-v1.json
provenance 必须记录 Pi repo/commit、TypeBox version、schema/composer 源文件路径与 SHA-256、实际调用的 composer/transport entry functions、全部 artifact SHA-256、 generator 与 evaluator operator allowlist。Composer/transport expected output 只能 来自 implementation pin 的实际执行,不能由本仓库 mirror 生成。
field-coverage-v1.json 必须枚举 schema 的每个 canonical field path。每个字段都
必须同时引用:
- 输入确实含该字段且 TypeBox 1.3.7 实际执行成功的 raw case;
- 输入确实含该字段、在其所属 provider/model/override 层实际执行成功且有 expected output 的 pinned Pi composer case。
缺字段、重复/陈旧字段、虚构 case、只在错误层出现或无法由 Pi 实际执行时 bundle 认证失败;无法执行的语义在 Rust 中 fail-closed 为 unknown。任一 hash/pin 不一致 时 raw evaluator 返回 unknown。更新 pin 必须显式重生成并审查。
15. UI、IPC、fixtures 与验收
15.1 UI/IPC
- Pi Provider 表单支持 provider/model 两级
api与baseUrl; - native catalog 使用独立只读面板,不使用 managed ProviderCard 操作;
- 只有 importable entry 显示导入操作;
- direct-only 在 managed 卡片上显式显示且无 failover 操作;
- 多模型 Provider 不显示必然失败的无 model-id 通用 Enable;
- Skill UI 独立展示 desired、owned、discovered/shadow;
- Prompt 顶层名称为
Instruction Files | Prompt Templates,不得将 AGENTS.md 称为 system prompt; - SYSTEM 页面有替换默认 prompt 的危险警告;APPEND 页面有追加说明;
- Sessions 显示不可枚举的 relative root 状态;
- 所有异步入口有 loading、error、empty 与 disabled 状态;
- en、zh、zh-TW、ja key parity,且不得依赖 fallback。
15.2 控制面 fixture
tests/fixtures/pi/control-contract-v1.json 必须稳定覆盖:
- 四 API family;
- provider-level 与 model-level api/baseUrl 继承;
- provider 默认为空、models 自包含;
- built-in overlay、modelOverrides-only、custom、extension overlay、malformed;
- raw validity / managed assessment / composition / management / gateway 分层分类与
{layer,code,json_pointer?}reasons; - aggregate 主行 + 多 endpoint;
- projection claim;
- Skill desired/owned/discovered 的组合及 stale/collision/drift;
- prompt file kinds、路径与 revision;
- session tree、active branch 与 relative-root diagnostic;
- 四语 UI/IPC 枚举与字段名。
fixture 只含无凭据的确定性示例。Commit 10 后其语义与字段冻结。
检查点 A 还必须锁定:
- canonical schema manifest 与
schema.rscode spec 等价; - raw oracle 的 valid/invalid 结果与 vendored TypeBox 1.3.7 期望逐项一致;
- composer oracle 的默认值、provider/model/override headers/compat precedence、 unmatched override、空 URL、built-in/extension unknown 结果逐项一致;
- schema 每个 canonical field 均有实际成功的 TypeBox 与所属层 pinned Pi composer 执行证据;transport resolver 的 deferred value/header 结果逐项来自 pinned Pi 实际执行;
- raw unknown/invalid inspection 对文件、DB、claim、projection、default 与 gateway 均零副作用。
15.3 故障 fixture
至少覆盖:
- cache-heavy 四 family:
1000 input / 800 cached; - native import fingerprint TOCTOU;
- exact claim collision;
- aggregate delete + projection failure;
- DB rollback failure;
- Skill 三种 unowned collision;
- deploy 后 ledger 前 crash;
- owned copy drift 与 symlink retarget;
- external prompt revision conflict 与 symlink;
- relative session root;
- stable token restart、migration crash 与 unwritable projection;
- concurrent mutation/restore/request;
- cross-origin URL/header/auth materialization;
- SSE pre/post semantic-commit failure。
15.4 验证命令
cd src-tauri
cargo fmt --all -- --check
cargo clippy --lib -- -D warnings
cargo test --lib
cd ..
pnpm typecheck
pnpm test:unit -- --run
pnpm format:check
pnpm build:renderer
git diff --check
renderer build 后必须删除 dist/ 与 dist-web/。仓库不得提交生成物。
16. Extensions 设计边界(仅设计)
未来只有在固定官方源码确认下列原生能力后,才可另行实现:
- 不执行 extension 代码的 machine-readable inventory;
- 原生持久 enable/disable 位或官方 passthrough;
- scope 明确、结果可判定的官方 install/remove CLI;
- 一个固定 owned key 的窄集成。
任何未来实现都必须以 Pi 原生配置/CLI 为 authority,不建 DB enabled mirror;不得自建 registry、依赖解析、semver、lockfile、下载器、trust 流程或 project-local 管理;不得执行 extension 取 metadata;不得复制凭据;不得自动 claim provider overlay或 Skill;不得把 extension MCP 表示为 Pi 原生 MCP。
本次实现不得包含以上能力的代码或 UI。
17. Commit 与盲审边界
实现必须保持 16 个独立 green commits:
docs(pi): freeze support contracts and extension designdomain(pi): add managed model and capability contractsdb(pi): add aggregate hydration and device-local ledgersconfig(pi): add read-only native catalog inspectionprovider(pi): add catalog mutation coordinatorskill(pi): add ownership-safe deployment reconciliationui(pi): expose provider native and skill control planeprompt(pi): support native instruction files and templatessession(pi): add tree-aware session managementtest(pi): close and freeze control-plane contractssettings(pi): harden local settings and persist gateway credentialgateway(pi): add listener admission and immutable runtimeusage(pi): make input token semantics explicitrouting(pi): materialize four native API familiesfailover(pi): add cross-host failover and SSE commit fencetest(pi): close recovery concurrency and end-to-end boundaries
累积检查点:
- A:base..commit 4;
- B:base..commit 10;
- C:base..commit 16。
B 必须零 validated blocker/high 才可进入数据面。B 通过后记录 CONTROL_FREEZE_SHA=commit 10,commit 11–16 每次必须通过:
git diff --exit-code "$CONTROL_FREEZE_SHA"..HEAD -- \
src-tauri/src/pi_config/model.rs \
src/components/providers/forms/PiProviderForm.tsx \
tests/fixtures/pi/control-contract-v1.json
schema.rs 由 schema snapshot/contract tests 守护 managed aggregate 与 ledger 形态。数据面若需要改变冻结文件、Provider IPC/form 字段、managed/native/direct-only 分类或 aggregate/ledger schema,必须停止并使 B 失效,不得在数据面提交顺手修改。
每个检查点的 reviewer 必须检查完整累积 range,并按 correctness、regression、data integrity、concurrency、security、compatibility、UI state、i18n 与 tests 维度报告。每条 finding 必须包含 severity、confidence、精确文件与行、具体故障场景、推理与建议方向;无 finding 时明确写出。
检查点 A 的 reviewer 材料必须包含:精确 base/checkpoint SHA、中立仓库地图与 authority 表、本文件、canonical schema manifest、native provenance manifest、 schema snapshot、带期望结果的 raw/composer/transport oracle fixtures、field coverage、provider-write/restore-policy/module-boundary 机器快照、完整验证命令与 通用审查维度。不得附带实现摘要、设计辩护、疑似文件/行号、既有审查输出或定向检查 提示。
审查总预算最多七轮。同一 invariant 连续两轮失败时停止局部修补并回到契约/状态模型;第七轮后仍有 blocker/high/data-integrity finding 时停止,不进行第八轮。