# ADR-0260:Generation-bound、Content-free 的 Plugin Package Prompt 执行 - 状态:Proposed - 日期:2026-08-02 - 关联:D-144、D-157、D-161、D-207、D-211、D-212、D-243 ## 背景 Plugin Package 已经能够把 Prompt 文件物化为 generation-bound 语义资源,并通过 automation publication 发布 exact active definition。但“定义已发布”不等于“可以安全 调用模型”。如果执行端临时读取 current head、直接渲染后调用 provider,会出现以下问题: - Package 在渲染、admission 和 provider I/O 之间发生升级、disable、quarantine 或 publisher revocation,导致一次请求混合两代事实; - provider 已产生费用,但 Run、StepRun、quota 或 audit 尚未提交; - COMMIT 已成功但响应丢失时,重试再次调用 provider; - 为了方便回放,把 template、参数或模型正文复制到数据库、日志、备份和 HA 副本; - 把 Prompt 伪装成 Task 或单步 Workflow,引入错误的执行 authority,或让路由设备承担 frontier、Attempt、timer、watcher 等不必要常驻成本; - 为协议、SQLite adapter、PostgreSQL adapter 分别新增 workspace package,继续放大 importer、构建、SBOM 和发布边界。 QingLong 同时面向小型路由设备和多节点集群,因此该能力必须满足两端同一语义:低配设备 按请求付费且禁用时零加载;集群在并发、响应丢失和数据库主从切换下仍可围栏和对账。 ## 决策 ### 1. Prompt 是显式 model execution Plugin Package Prompt 不是 Task、Workflow、Trigger 或 scheduler。每次调用创建: - 一个父 `Run`,从 admission 起为 `running`; - 一个 `StepRun.kind=model`,从 `ready` 经唯一 ModelInvocation 链进入终态; - 一个 content-free admission receipt; - 一个由 ModelInvocation Completion 或 Resolution 支撑的 finalization receipt。 不创建 RunAttempt、child Run、Workflow frontier、后台 cadence 或 per-Prompt worker。 ### 2. 执行计划不可变且不含内容 执行前从 exact `PluginPackageAutomationPublication` 生成最多 32 KiB 的 immutable plan。 plan 必须绑定: - Project、Package、installation、lock; - generation、generation digest、materialized revision digest; - publication digest、Prompt ID、Prompt definition digest; - request、invocation、Run、StepRun、trace、发起 Subject 与 Project Policy fence; - provider、model、max output tokens、temperature、deadline; - parameter digest、model request digest 和 input bytes。 plan、receipt、RunEvent 和数据库索引列不得包含 template、参数值、渲染后的 message 或模型 输出正文。 ### 3. 内容只做一次瞬态 exact rendering Prompt template 与参数只在调用栈内存中渲染: - required 参数缺失失败关闭; - optional 参数缺失表示省略,不等于显式空字符串; - 参数值不会再次递归解释其中的 placeholder; - 不接受未声明参数; - 单参数值与单 message 均受 64 KiB 上限; - 生成后的输入字节、token 和 deadline 继续受 Model Gateway 硬预算约束。 渲染结果仅交给本次 Gateway 调用,admission repository 永远看不到正文。 ### 4. Admission 必须先于 provider I/O 首次 admission 在单个存储事务中完成: 1. 先检查相同 request ID 的 durable receipt; 2. 对新请求复验 exact active publication; 3. 在同一事务内复验发起 Subject 的 current Project/RoleBinding fence; 4. 复验 current install/lock、lifecycle、quarantine、publisher start guard; 5. 复验 exact materialized revision 中恰好存在一个同 digest Prompt; 6. 原子写入 Run、admission event、model StepRun、StepRun mutation 和 admission receipt。 任何 guard、identity、digest、外键或计数冲突都整体回滚。exact replay 必须先于 current guard,使已提交 winner 在 Package 后续 disable、withdraw 或 upgrade 后仍能收敛。 ### 5. ModelInvocation 是唯一 provider fence Prompt executor 不直接持久化 provider 状态,也不建立第二套 invocation 状态机。所有 provider I/O 必须经过 D-157 的 ModelInvocation: - `ready→running`、Run/Event/Mutation 和 ModelInvocationStart 原子提交; - quota reservation、price quote、usage 和 completion 沿用既有 Gateway policy; - `running→succeeded|failed|timed_out|lost` 由唯一 StepRun mutation chain 决定; - durable start 已存在但 completion 缺失时禁止 provider replay; - completion 为 `outcome_unknown` 且没有人工 Resolution 时保持不可判定; - Resolution 为 retry 时父 Prompt Run 保持运行,cancel/fail 才能终态化。 ### 6. 父 Run 从终态证据收敛 finalization repository 在一个事务中读取 exact admission、Run/StepRun 与 ModelInvocation Completion/Resolution: - Completion `succeeded|failed|timed_out` 直接决定父 Run 状态; - `outcome_unknown` 只接受绑定同一 completion digest 的终态 Resolution; - Run version 与 event sequence 必须保持相等并 CAS 增加一次; - final RunEvent 只保存 evidence digest、final StepRun digest、plan digest 和低敏状态; - finalization receipt 与 Run/Event/StepRun/evidence 必须可双向复验。 ### 7. Replay 不承诺返回模型正文 首次 live 调用可以把 `GenerateResult` 返回当前 caller。exact replay 返回 admission 与 finalization receipts,但 `result` 为 `null`,绝不再次调用 provider。 如果产品需要跨请求读取输出,必须后续引入显式、受保留期与加密策略约束的 Artifact sink, 并只在 ModelInvocation completion 中保存 bounded output ref/hash/bytes。不得把正文补进 Prompt admission/finalization 表。 ### 8. 双方言与权限边界 SQLite: - AI feature 未 active 时不加载 Prompt executor、不建新进程或连接; - 与 Local ModelInvocation 共享同一 operation authority; - admission/finalization 使用短 `BEGIN IMMEDIATE`; - 两张 Prompt 表属于可选 `ModelInvocation*` schema allowlist,不污染主 schema readiness; - 停用 AI 时既有 exact replay 可读,但新 mutation 受 feature fence。 PostgreSQL: - 使用独立 `ql3_ai` migration stream 的 9007/9008; - `model_invocation_prompt_admissions` 和 `model_invocation_prompt_finalizations` 为 append-only; - `ql3_runtime` 只有 `SELECT, INSERT`,没有 `UPDATE, DELETE`; - AI schema 提供 Prompt 专属 `SECURITY DEFINER` `plugin_package_prompt_admission_snapshot`,不借用名称错误的 Workflow 入口; - snapshot 复用核心 `plugin_package_automation_start_allowed`,并锁定 publication 与 materialized revision;它同时按 Subject type/ID、Project version 与最新 RoleBinding version 复核 API admission 的 immutable policy fence; - mutation 使用带 5 秒 statement、2 秒 lock timeout 的短 SERIALIZABLE transaction, 对 serialization/deadlock 最多重试三次。 ### 9. Package 与部署边界 实现全部留在既有 `@qinglong/ai` package 的显式 subpath: - `plugin-package-prompt-execution`; - `plugin-package-prompt-executor`; - `local-plugin-package-prompt-admission-storage`; - `postgres-plugin-package-prompt-admission-storage`; - `postgres-plugin-package-prompt-application`。 这些是同一可选 AI capability 的协议和 adapter,不满足独立部署、进程、权限域或重依赖 隔离价值,因此不得新增 workspace package。 Edge/Standalone 通过 Local application 在 AI active 后动态装配。Cluster 通过显式、默认 关闭的 PostgreSQL application composition 装配;disabled 分支不得打开 Pool、执行 readiness 或加载 provider,active 分支必须按“只读 migration/ACL readiness → bounded recovery → provider credential load”顺序启动。该 composition 不增加进程、listener、端口或 workspace package。 Cluster Control 只在显式注入 `promptExecution` capability 时增加 `POST /api/v3/projects/{projectId}/packages/{packageName}/prompts/{promptId}/executions`; 默认 allowlist 仍只有 Run read/cancel。该 route 复用现有 bearer authentication、认证前 rate shield、Project Policy、durable security audit、TLS/body/response/in-flight/request timeout,并使用独立 `model.invoke` permission。客户端只能提交 publication digest,不能 提交 publication JSON;服务端从受限 snapshot 解析 exact publication,随后 admission 事务再次复核 publication 与 policy fence。route 不新增进程、listener、端口、依赖或包。 ## 已实现证据 - immutable Prompt plan、非递归 rendering、digest/size/identity normalizer; - SQLite admission/finalization repository 与 Local AI lazy composition; - PostgreSQL 9007/9008、Prompt 专属 snapshot 与 SERIALIZABLE repository; - Prompt executor 的 first execution、safe resume、completion repair 与 exact replay; - SQLite 真库覆盖原子 admission、publication withdrawal 后 replay、target drift rollback、 parent Run finalization、provider exactly-once 与内容排除; - 原生 Linux arm64 Node 24.18.0 的 `router-stress-ci`(128 MiB、0 swap、0.5 CPU)与 `edge-release-ci`(256 MiB、0 swap、1 CPU)已直接运行 Local AI active product vertical: 正式 install/materialize/publication → Prompt execute → `succeeded@v5` → exact replay, provider 恰好一次、零 RunAttempt、durable SQLite 不含私有输入/输出。两档 Prompt process peak RSS 分别为 `92282880`/`90951680` bytes,数据库 logical/allocated growth 均为 `0`, cgroup `memory.peak` 分别为 `128229376`/`129253376` bytes,零 max/OOM; - 同一两档门已纳入 ModelInvocation start/completion 的 7 点/profile、14 场景 `SIGKILL → reopen → exact replay` 矩阵;Prompt admission/finalization 外层事务再覆盖 10 点/profile、20 场景,其中 16 个 COMMIT 前 crash 全回滚、4 个 COMMIT 后 crash durable, exact replay/content-free/integrity/foreign key 全绿并报告 `promptAdmissionFinalizationCrashProven=true`。所有 CI 证据仍固定 `physicalPowerLossProven=false`,不把进程崩溃或 tmpfs 文件增长冒充闪存写放大或真实断电; - crash fixture 通过 `@qinglong/ai` 的 test-only workspace devDependency 使用正式 `@qinglong/local-sqlite` baseline migration 与 Package repositories;AI production dependencies 仍只有 `@qinglong/runtime-core`,没有新增 importer、生产依赖或部署闭包; - AI suite 共 115 项:112 pass、3 个无 URL 条件 skip、0 fail;新增覆盖 Prompt 外层事务 20 点 crash matrix、Cluster disabled 零加载、exact migration/ACL readiness、先 recovery 后 provider、幂等资源释放与 readiness 失败不开 provider; - runtime-core 430/430 通过,`model.invoke` 仅 owner/admin/operator 可直接使用,viewer 拒绝,agent 保持 `require_approval`;Cluster Control 167 项为 165 pass、2 个外部服务 条件 skip、0 fail,覆盖 strict body、subject/fence 传递、request abort、低敏错误、live result 与 content-free replay receipt,以及未注入时 route 不存在; - 独立 PostgreSQL 18 真库使用 migration、package-executor、runtime 三个非特权账号,完成 Cluster application readiness、Package install/materialize/publish、Prompt execute、 ModelInvocation completion、父 Run finalization 与 replay;provider 调用一次,Run 收敛到 `succeeded@v5`,durable JSON 不含私有参数和模型输出; - PostgreSQL 18.4 arm64 physical-streaming HA 门在最新 9008 checksum 上 `gates.passed=true`:9001—9008 history/ACL、runtime 只读 migration history、Prompt admission/finalization、provider exactly-once、content-free durable facts 在晋升前复制并在 timeline 1→2 后完全一致;旧主先 fencing,再经 `pg_rewind` 以只读同步 standby 重加入, 新 product service 从数据库解析 publication,RoleBinding 撤销后旧 policy fence 被拒绝且 provider 调用数保持一次;`gates.passed=true`,临时容器/网络/卷零残留,用户现有 evidence control-plane 未被触碰; - 通用三节点 K3s/CloudNativePG 1.30/PostgreSQL 18.4 HA 门继续全绿,覆盖 TLS 1.3、 identity/certificate rotation、failover/outage recovery、CNI/RBAC 与 durable facts。 ## 接受门与非目标 本 ADR 保持 Proposed,直到以下门完成: 1. [x] 在 PostgreSQL physical streaming timeline promotion、旧主 fencing、rewind/rejoin 后,独立证明 9007/9008 migration、ACL、admission/finalization receipt 完全一致; 2. [x] Cluster application 显式装配 Prompt executor,且不存在 repository 可用即等于 route 上线的隐式行为; 3. [x] 产品 API transport 完成受认证 Principal、Project Policy、`model.invoke`、rate/body/ timeout budget、request abort 和低敏错误映射;agent 自动化仍必须先通过 approval 产品链; 4. [x] API 入口明确区分 live result 与 content-free receipt replay;UI/MCP 可复用该 API, 但不作为本 ADR 接受前提; 5. [ ] durable output 已由 ADR-0261 单独冻结 envelope、transaction、authorization、retention 与 GC 边界;双方言原子 adapter、产品读取和 maintenance authority 完成前保持不可达; 6. [x] 在固定 128/256 MiB 原生 Linux CI envelope 中补充 active application、RSS、SQLite logical/allocated growth、exact replay、内容排除、ModelInvocation 与 Prompt admission/ finalization 外层事务 SIGKILL/reopen 矩阵;disabled 零数据库/provider 加载继续由组合测试 证明; 7. [ ] 在固定物理低配路由设备上采集最终 application artifact 的 active RSS、真实数据盘/ 闪存写放大与受控断电重启矩阵。CI 报告必须保持 `physicalPowerLossProven=false`,完成前不得形成最低支持声明。 以下不属于本 ADR: - Prompt 定时执行或 Workflow 内嵌 Prompt; - Agent、多模型编排、Tool calling 或向量检索; - Secret 自动插值; - provider 自动重试; - Prompt/输出正文默认采集; - 新 workspace package、daemon、watcher、listener 或 per-Prompt timer。 ## 被拒绝方案 1. **把 Prompt 包装成单步 Workflow**:引入不需要的 frontier、Attempt、cancellation 与 recovery ownership,放大路由设备成本。 2. **把 Prompt 当普通 Task**:绕过 Model Gateway policy、quota、price、usage 与唯一 model StepRun fence。 3. **provider I/O 后补 Run**:数据库故障时产生无 durable owner 的外部费用和结果。 4. **exact replay 自动再调 provider**:COMMIT response loss 或 caller retry 会重复计费。 5. **持久化 template、参数和输出**:扩大 Secret、个人数据、日志、备份和 HA 副本泄漏面。 6. **复用 Workflow snapshot 函数**:实现可行但 authority 名称错误,会把 Prompt 生命周期 隐式耦合到 Workflow admission;因此改为 AI schema 内 Prompt 专属入口。 7. **为每个 adapter 拆 package**:没有独立部署或依赖价值,违反 D-207 并增加供应链成本。