goagent-kernel

module
v0.34.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 12, 2026 License: MIT

README

goagent-kernel · 多智能体编排通用内核

一句话:编排是代码,LLM 只做单环节——本仓只提供确定性编排的机制面, 不认识任何领域概念。

从上游私有宿主仓(代号见内部文档)拆出的内核层,宿主业务不公开。定位对标同族的 oagentkit(Agent/LLM 编排工具链矩阵)与 oekit(基础库):独立发版、版本化引入、 零 replace(go.mod 含 replace 即门禁红;本地并行开发走 make devlink 的 workspace,见下)。

本文档导航

  • README(本文)= 是什么、为什么这么设计、边界纪律
  • docs/USAGE.md = 怎么接、接错了会怎样 ← 宿主开发者从这儿开始
  • docs/AI-GUIDE.md = 怎么改、会被哪条门禁拦 ← 要动这个仓的 AI 代理从这儿开始
  • AGENTS.md = 在本仓干活的工作纪律
  • 文档站 = 介绍 / 设计初衷 / 怎么用 / 能力 / 参考(VuePress,源码在 site/)

全部文档一览(docs/ 下标着历史快照的那些记录当时的判断与踩坑, 不是当前事实;当前事实一律以本文 + 门禁 + 源码为准):

文档 回答什么 性质
README 是什么、为什么这么设计、边界纪律、下游升级须知 活文档
docs/USAGE.md 宿主开发者:怎么把 harness 接进自己产品 活文档
docs/AI-GUIDE.md 改本仓的 AI 代理:任务 → 改哪些文件 → 哪条门禁拦 → 怎么验收 活文档
AGENTS.md 在本仓干活的工作纪律(每条纪律的事故来源) 活文档
architecture-policy.yaml 门禁声明面:模块拓扑 / 规则 / 阈值 / 例外 活文档(机器判定)
CHANGELOG.md 版本历史与破坏性变更 活文档(按 tag 切段生成)
api-lifecycle.yaml API 生命周期清单:每个生产包的 Experimental/Stable/Deprecated 定级 + 理由(机器判定) 活文档(机器判定)
docs/api-compat.md API 兼容策略:分级判定规则(什么算 breaking)/ 破坏性变更流程 / 宿主升级须知 活文档
docs/error-semantics.md 全仓错误哨兵语义总表:哪个包哪个哨兵、触发条件、调用方处置(机器判定) 活文档(机器判定)
docs/adr/README.md 决策记录(ADR)索引:每条设计取舍的编号 / 一句话结论 / 状态 决策记录(ADR)|设计推导与取舍的真相源
docs/OAGENTKIT-OVERLAP.md 本仓与上游 oagentkit 的能力边界:哪些该做、哪些上游已有 活文档
docs/SANDBOX-DESIGN-20261021.md 执行沙箱的下一层:跨域执行与资源兜底(发现「内存无兜底」真缺陷 + 与上游 guardx 的分工) 活文档
docs/HARNESS-BENCHMARK-20261021.md 对标 Codex / DSH / Octop / Coze 五家:借鉴了什么、什么归宿主、什么本轮没做 活文档
docs/GAP-ANALYSIS-20261021.md 与 Codex / DSH / Octop / Coze 的逐项实测对齐:差距在哪个维度、哪些该补、哪些不该补 活文档
docs/GAP-ANALYSIS-20261106.md 同一对标的刷新(2026-11-06 重拉五家):Coze 已停更、DSH 219→400 叶子包、缺口从「可撤销性」移到「判定面的宽度」 活文档
docs/GAP-ANALYSIS-20261107.md 第三次刷新(2026-11-07 重拉五家):参照系这轮没长出新机制面;唯一开着的 P0 是出口强制件的两端都空(无代理 + 无 Enforcer 实现) 活文档
docs/CROSS-ENV-VERIFY-20261021.md 跨环境验证:平台相关的机制怎么在「CI 绿但其实跳过」的情况下真验(实测环境矩阵 + 交叉编译流程) 活文档
docs/CHECKPOINT.md 续跑契约的设计推导、三条不变量与跨进程闸 活文档
docs/ENGINEERING-HISTORY.md 「工程底座」表九轮校准的过程与账:为什么它漂过、后来怎么接进门禁 历史快照(从 README 下沉,2026-11-11)
docs/SPLIT_PLAN.md 从宿主仓拆分的分层依据(L1/L2/L3) 历史快照(2026-10-17 写于 10 个包时)
docs/MIGRATION_OAGENTKIT_20261018.md 门面迁移与 errors 链路改写的执行记录 历史快照(已于 2026-10-18 执行完毕)
docs/SELF-AUDIT-20261018.md 独立复核发现并修复的缺陷 历史快照(2026-10-18 收尾)

边界:本仓有什么、没有什么

✓ 有(机制,与领域无关)        ✗ 没有(资产,属于宿主/领域包)
─────────────────────────      ──────────────────────────────
protocol   统一报告协议          专家包 / 专家清单 / 技能
           宽容解析 + 结构化校验  诊断链 / 编排图(faultChain 等)
           白名单校验            运维词表(巡检域、K8s 词面…)
           Markdown 渲染         目标类型(主机 / 集群 / Pod)
contract   编排运行契约(唯一边界) 宿主状态的落库方式与并发约束
statemachine 环节状态机          审批风险分级(restart/delete 四档)
eventbus   进程内有损广播        拓扑内容(谁允许派发谁)
policy     操作审计门            品牌文案(报告标题、引导 agent 名)
spill      输出预算溢出          事件持久化与表结构
budget     输入侧上下文预算      摘要生成的具体实现(走 Summarizer 接口)
offload    内联大对象外置        IM 通道 / 通知模板
toolfail   工具失败分类契约      HTTP 路由 / 鉴权
tooldisclose 渐进式工具披露 + 调用门禁    工具实现(MCP server)
guardset   守卫装配与拒绝合成      具体守卫实现(披露门/出站门/审计门)
pii        个人信息脱敏          工具目录内容(本包只管披露与门禁)
netguard   出站 SSRF 守卫        诊断链/巡检图(编排图属宿主)
dispatch   派发守卫(权限即拓扑) 摘要策略(何时压实属宿主)
ptc        程序化工具编排沙箱    环节语义(收口/幽灵结果的具体判据属宿主)
watchdog   工具调用配对监视      工具描述文案
checkpoint 编排续跑契约          快照的存储介质(SQLite / PG / 文件)
commitgate 环节终态独占提交闸    并行分支的冲突裁决(该归宿主还是机制)
delta      增量节流 + 标签分流   模型供应商路由
sideeffect 副作用至多一次契约   幂等键的存储实现(Redis / PG / etcd)
sidetest  副作用闸契约测试套件   存储实现的并发正确性(原子性 / 跨进程)
retryplan 重试处置决策          幂等键的具体存储(内核只给路径,不给实现)

判定口诀:「这个包需要知道自己在给谁干活吗?」需要 → 属于宿主。

包清单

包 生产行数 外部依赖 职责
protocol 2341 oekit/pkg、jsonschema Report 契约 + 五级容错解析 + 白名单校验 + Markdown 渲染
dispatch 36 oagentkit/agent allow 矩阵物化 + 深度上限 + 自派发拒绝 + 环检测(拓扑经 EdgeSource 由宿主提供)
eventbus 377 oagentkit/runtime 进程内有损广播(缓冲 chan);持久化在发布点由宿主负责
policy 89 cloudwego/eino、oagentkit/guardx、oagentkit/toolgate 操作审计门:阈值只降不升、解析失败即拒。两个接入口:WithAuditGate(包单个工具)与 Stage(接成判定环的一环——工具调用、程序内调用、子进程侧调用全走同一条 invoke 链,审批规则只装配一次;门面原先缺后者,宿主想接审批进判定链只能 import 上游 toolgate,「调用面逐字兼容」的承诺就断在那里)
spill 263 oekit/pkg 输出预算溢出:提示契约 / 取回 / 按龄清理
pii 712 oekit/pkg 个人信息价值面脱敏(手机号 / 身份证四重校验);运行时策略(开关/面/强度)整份单指针原子交换 + 变更订阅 Subscribe,代次从此不只可查、还可响应
netguard 1212 oagentkit/guardx、oekit/pkg 出站 URL 守卫(SSRF):名字层(解析前按名拒)+ 解析后逐 IP 过 egress + 重定向逐跳重验。扩展名单整份原子交换 + 代次可观测(两次独立取会读到撕裂策略)。代次只有一个真相(策略指针内的 gen,全局值是它的投影;交换走 CAS 循环,故并发下代次唯一、单调、不回卷)。DialFunc 把同一套判定落到建连那一刻(名字层 + egress 预检 + 建连后核对真实 RemoteAddr)——闭合「校验 → 拨号」的 rebinding 窗口;判据仍是 egress,不引入第二套 IP 策略。放行面具名且两层共用同一类型(Allow,零值即严);NewClient/NewTransport 是接线件——宿主一行把守卫挂上,且 client 可注入(测试连本机假服务器时只在测试里放宽回环)非纯 stdlib;DNS 解析与拨号间的 rebinding 窗口见包头「已知局限」与 dial.go;与上游 oekit/pkg/urlguard 的分工(IP 策略单源 egress 不换、域名后缀层本包为超集)同样见包头
statemachine 79 无 环节状态机:条件迁移的纯函数件
contract 124 无 编排运行契约:编排引擎 ↔ 宿主状态的唯一边界(纯接口面,零实现)
checkpoint 890 oekit/pkg 编排续跑契约:进度快照存取边界 + 「续跑而非重放」的恢复判定(空洞不采信 / 终态不自动重开 / 序号单调闸)+ 跨进程 CAS 存取契约(StoreCAS.SaveIfNewer;v0.33.0 起序号为 0 的快照一律拒收——它不是一次真实提交,而放它落库等于给了它唯一能覆盖既有快照的写)与提交序列固化(Committer:抢序号→落库→仅失败时回滚,宿主无法漏掉)+ 依赖声明(StepDef.DependsOn / Isolated):并行 fan-out 的合法乱序完成按各自前驱采信,不再被「连续前缀」整批作废;零值 = 旧口径逐字不变
commitgate 328 oekit/pkg 环节终态独占提交闸:同一 (run, 环节) 的终态只允许被提交一次(statemachine 是纯谓词,SeqGate 只排快照序号,两者都排不出「两条分支都赢了」)。终态词表由宿主注入。放闸靠凭证:Acquire 返回唯一令牌、ReleaseLease 凭令牌精确释放——靠 (状态,分支,序号) 认人的旧 Release 分辨不出两次同名同序的占用,迟到的释放会放掉别人的闸
budget 750 oekit/pkg 上下文预算:分段计量 + 窗口钳位 + 压实计划骨架(三重空转守卫;摘要生成与原文落盘各留宿主接口)
offload 587 oekit/pkg 内联大对象外置:sha 去重 + 两阶段(首次标记、后续轮才替换占位)+ fail-soft(与 spill 是姊妹机制)
tooldisclose 959 oagentkit/guardx、oekit/pkg 渐进式工具披露(确定性打分检索换完整 schema)+ 执行门禁(未加载可重试 / 不在目录禁止绕过 两类严格二分,拒绝即激活,重试不绕路)+ 接成 invoke 链的一环(Stage,链上 Reason 直接回面向模型的指引)
toolfail 89 oagentkit/guardx 工具失败的错误分类契约(门面,核心在 oagentkit/guardx/toolfail):类别 → 可重试性/退避/需人工/重发安全/模型指引为数据表而非散落 if;不可归类一律不可重试。重发安全与重试价值是两个正交维度——超时值得重试但服务端可能已执行,原样重发即重复施加副作用;Guidance() 是模型侧的唯一消费方。判定口径必须跨宿主一致,故核心归上游安全层,本仓只保调用面稳定
toolname 96 无 工具名 → 程序内可用的 JS 标识符:一条单一真相。IsJSIdent(能不能直接用)+ Normalize(不能的话用哪个)。生产侧(tooldisclose 派生调用形态)与消费侧(ptc 挂载绑定并校验形态里的函数名)共用它——两侧必须逐字一致,否则就是「这里派得出、那里认不出」的夹缝。规整不保证唯一(get-logs 与 get_logs 同名),撞不撞由调用方判
sideeffect 955 oekit/pkg 副作用「至多一次」的跨进程契约:幂等键(同一逻辑动作重试不重复施加)+ 租约(同一资源独占,带 TTL)+ 栅栏令牌(过期持有者的迟到写入被下游拒绝)+ 下游拒收契约(FencedSink:比较与写入须同一次原子操作,否则两个持有者会同时读到同一个旧值、双双通过检查,更旧的那次写入反而后落覆盖新的)。上游三个判据共用一次原子操作(Store.Acquire)——拆成三份存储就会出现「租约过了但幂等没过」的夹缝,允许同一动作被施加两次。执行失败不记账(没做成的不能记成做成了),但释放闸(否则失败残留的闸会把后续重试全挡成「别人占着」)。v0.32.0 起施加失败且无法判定远端是否已生效 ⇒ 结果未知(OutcomeUncertain):该 Key 禁止重试,必须先对账(Reconcile)——「响应丢了」与「远端没做」在调用方看来完全一样,而前者重试就是重复施加;执行器同时拿到栅栏令牌(lease.Fence)好把陈旧写拒在下游。施加前有资格检查(ExecuteWith 两段式:准备 → 检查 → 施加)——闸已易主时根本不施加,因为 Complete 被拒只说明没记上账,副作用早已生效,事后补救不了。承诺的是至多一次而非 exactly-once——后者要求下游也接受幂等键。v0.33.0 起补上「执行意图」:Acquire 原子地登记「本次尝试还在」(Key 级 IN_FLIGHT),于是进程死在施加成功之后、记账之前这一形态在存储侧留有痕迹——租约过期且未裁决时,同一 Key 转入需对账态(OutcomeUncertain)而不是重新授予执行权(声明了 RetrySafetySafe 的除外:下游按幂等键去重,重做安全)。配套把两条出口拆开:Abandon(确定没生效 ⇒ 撤销意图 + 放闸)与 Release(只放闸,不动意图)——「可能已生效」那条路若顺手撤销意图,重复施加的入口就重新打开
sidetest 1911 oekit/concurrency、oekit/pkg sideeffect.Store 的共享契约测试套件:宿主写完存储实现后跑一遍,违反哪条契约就红在哪一条——免得「各写一遍必然各判一套,漂移到没人发现为止」。29 条子测试覆盖「只做一次 / 已完成优先于占用 / 未过期时争用 / 过期后接管 / 令牌单调 / 过期持有者不得记账 / 过期持有者不得放闸 / 释放不等于完成 / 续期延长且不换令牌 / 闸已易主后续期被拒 / 闸已释放后续期被拒(只比令牌会漏掉的那条)」,外加「结果未知」一族五条(未知态禁止再执行 / 裁决未生效允许重试 / 裁决已生效记结果 / 裁决先到者为准 / 过期栅栏不得转未知)与「执行意图」一族六条(未裁决不得重新授予执行权 / 过期未裁决转入需对账态 / 声明可安全重试则过期后可重授 / 显式放弃后重新可执行 / 只放闸不撤销意图 / 放弃只认本次尝试的令牌)、「持有者身份」一族三条(身份由存储铸造 / 偷到令牌的组件冒充持有者被拒 / 跨资源凭据被拒)、「标记失败」一族一条(标记写失败后不得授予执行权,套件内置故障注入装饰器)、「重试安全三档」一族两条(Unsafe 裁决未生效仍阻塞 / 显式决策才放行)与「对账占用」一族两条(占用挡住第二个对账者 / 占用过期可重入且不放行)。范式沿用上游 cachetest / singleflighttest:硬失败不静默跳过(显式接入套件=声明要满足契约,跳过等于把「没测过」伪装成「通过了」)。时钟可注入(ClockedStore),否则过期场景恒不触发,套件会变成一组空断言。另有下游栅栏一族六条在 RunSinkContract(v0.33.0 起,issue #84:新令牌接受 / 等于也接受 / 陈旧拒收且零写入 / 新先旧后保最大 / 并发陈旧被拒 / 写入体失败不部分写)——判据收编自 sideeffect 包内对内存替身的用例(只有一份),内存替身与 PostgreSQL 参考实现(examples/pgstore)跑同一段断言;并发场景走 async.GoSafe(本仓禁止裸 goroutine)
cmdpolicy 527 oagentkit/tools、oekit/pkg 命令级判据:把判定面从「工具调用」向下延伸到工具内部实际执行的那条命令。此前边界停在工具名上——一次 exec 审批通过后,它内部跑的是 git status 还是 rm -rf / 没有任何机制面看得见,宿主只能靠正则黑名单兜,而黑名单在「写法与语义不一致」时必然失效。本包用前缀 token 规则(先分词再比 token,不随引号/转义/空白漂移)+ 三态裁决(allow/prompt/forbidden,与 toolgate 词表同义不另立口径)+ 规则自带样例(每条规则可带 Match/NotMatch,New 在加载期用真实判定路径跑一遍,不一致即报错——这是「从没见过它触发的规则等于没有规则」的机器化:规则会随命令名漂移,而漂移不会报错,只会静静地不再命中)。冲突口径确定性:前缀更长者胜 → 同长度取更严 → 同档取名字字典序最小。SplitCommands 单独存在是因为前缀判据的典型绕过:git status; rm -rf / 的前缀是 git,对整串只判一次就会让一条 git 的 allow 规则放行后半段。分词面已收编上游 oekit/pkg/shellx(2026-11-11):口径归上游一处,本仓不再维护第二份分词器;顺带修掉手写版三处 POSIX 错误(双引号内反斜杠被丢弃、分隔符按 token 相邻误合并、CR 未归空白),且 SplitCommands 的段文本改用上游 QuoteArgv 拼装,段可往返(段再分词得回原 token——否则含空格的备选前缀永不命中,是安全方向的漏判)。事实面已收编上游 oagentkit/tools/bashguard(真 shell AST):前缀判据的三类典型绕过(多段 git status; rm -rf /、命令替换 echo $(rm -rf /)、写重定向 git status > /etc/passwd)由「全调用面取最严 + 事实面下限(写重定向/动态展开 ⇒ 不低于 prompt)」闭合,本仓不手写 shell 解析器
egress 504 oekit/pkg 出口策略:三档(ModeUnrestricted < ModeProxy < ModeDenied,单调可比、零值即最松)+ 声明自检(Validate)+ 兑现判据(Env / Verify)+ 后端能力问询(SatisfiedBy)+ 兑现强度(VerifyVerdict / SatisfiedVerdict:环境变量兑现出来的永远是建议型;v0.33.0 起自报与实测分开——后端只是声明能力(Enforcer)⇒ 强度是 declared,不是 enforced;只有后端能实测(EgressVerifier)并给出证据时才判 enforced——「代理档已兑现」不等于「出站已被强制隔离」,而后端自报恰恰是最容易被读成事实的那种声明)。本包不含代理本体(那是上游 guardx 与宿主的活),只回答此前没人回答的三件事:这次执行要求哪一档出口 / 宿主给的环境是否真的兑现了那一档 / 某个沙箱后端能不能兑现它。两条底线都是「沉默与伪造都落在安全的那一侧」:后端不声明就按最弱处理(不是「没说不支持」=满足,否则从没实现过出口控制的后端会被选中,声明与实际之间的落差没有信号);禁网档拒绝伪造(ModeDenied 的 Env/Verify 直接报错,不做「尽力而为」——假装禁网比明确拒绝更危险,报告会显示「已兑现」而网络其实通着)
gatecheck 504 oagentkit/guardx、oagentkit/toolgate、oekit/pkg 审批层与沙箱层的联合判据:Codex 的两层安全模型是「沙箱强制 + 审批策略,两层都同意才放行」。上游 guardx/sandbox(档位×预算)与 toolgate/policy(审批裁决)各自完整但互不知情——「两层是否都同意」这个判定没有单点,落在各宿主手里必然漂移。本包只做合成(不重新实现任何一层,那会造成与上游并存的第二份真相)。判据顺序有意为之:审批已否 ⇒ 直接否(档位再高也不该执行);需人审 ⇒ 优先于档位不足(人该在场,而不是让宿主去找后端——否则这次调用既没执行也没人管,静默丢失);计划模式单列(只审不执行 ≠ 拒绝)。Managed 是受管后端(把真后端包起来):判据不通过就不委派(副作用不会发生)——判据与执行不可分,否则「判过的后端 ≠ 实际跑的后端」又有了信号落差
preset 230 oekit/pkg 命名权限预设:把「隔离档位 × 审批模式 × 出口策略」三件散参数收成一份具名、只读、加载期自检的组合(Preset{Name,Level,Mode,Egress,Unsafe,Justification})。散参数的病不是难写,是改一处漏两处——档位调到 OS 级而出口忘开代理、审批放到 auto 而沙箱仍是进程内,组合出来的权限谁都没声明过。自检两条最关键:三面全松必须显式 Unsafe 且写理由(「全都放开」最容易在调试时临时出现、然后留下来);语义相同的两个名字即报错——两个名字一份组合在配置里看起来是「两个选择」,真正的伤害在留痕:报告里出现两个名字,读者会以为执行过两种权限。Resolve 遇未登记名字报错而不降级(降级方向不可预测,可能更松,而「打错一个字就用上另一档权限」没有任何信号)。方向是 preset → gatecheck(高层组装件认识低层判据),gatecheck 不认识 preset
retryplan 192 oagentkit/guardx 重试处置决策:把「该不该重试」(toolfail 判)与「重试会不会重复施加」(sideeffect 判)合成一条可执行结论——三档路径(不重试 / 原样重发 / 走幂等键重放)。与 guardset 同族:组合逻辑归内核,判定归各自机制(各宿主各写一遍组合逻辑,必然各判一套)。关键一档是「可重试但原样重发不安全」——toolfail 只说「先确认上一次是否已生效」,没说什么怎么做到,而这正是幂等键的活;故该档判为 PathReplayed(走幂等键)。关键取舍:没有幂等键就不许「猜着重试」(降级为不重试,而非原样重发赌一次)
delta 253 oagentkit/runtime 增量节流:分片合并 + 权威整段优先 + 确定性收口扫序
pending 73 无 多轮挂起态载体:单一编码入口 + 宽容解码(非法值降级为无挂起)
reminder 30 oagentkit/guardx 系统注入提示的防伪包裹(门面):核心已下沉 guardx/injectband,本包只保调用路径稳定,宿主调用点一行不动
sessionref 212 oekit/pkg 跨会话引用快照:把另一个会话的结论以有界、带来源、带失效点的片段注入当前上下文。budget/spill/offload 三者回答的都是「同一份上下文里内容放不下怎么办」,而「引用别的会话的结论」是另一件事——那份内容不在当前上下文里,它的边界与保鲜度没有任何机制面管。不管的后果不是「引用得不好」,是把当时的结论当成现在的结论用,而这类错误在报告里读起来完全正常(那段话当时确实是真的)。三条判据都走结构:① 裁剪上限采集时给定且裁剪必须显式(Truncated + OriginalBytes + 注入面的 truncated=true,静默截断会让模型以为看到全文);② Source/SourceVersion/CapturedAt/ExpiresAt 随快照走;③ 渲染写 state=fresh|expired|stale。版本这一维不可省:TTL 是采集者猜的,源是否前进是事实(长 TTL + 高频源更新是最常见的失效形态,只靠时间会一路判有效);版本回退同样判 stale(同一个 id 已不是同一段历史);两者同时成立报 expired。渲染不拒绝、只标注——拒不渲染会让「引用过期」变成「引用消失」,宿主无从区分「本来就没引用」与「引用了但过期」。防伪不另写一套,走 reminder.Wrap
drain 320 oekit/logx、oekit/pkg 优雅排水:轮 ctx 取消脱钩 + 等在途收敛 + 预算耗尽强制收刀(经 Turns 接口)
ptc 3116 dop251/goja、oagentkit/guardx、oagentkit/runtime、oekit/concurrency、oekit/logx、oekit/pkg 程序化工具编排:模型写程序 → 沙箱 VM 执行 → 经 bindings 调宿主工具;三重预算(墙钟/调用数/字节数)替代 OS 沙盘。N 轮 ReAct 往返塌缩成 1 轮,原始数据不过模型面;绑定可带 Signature 让模型按位置传参(可选形参以 ? 标记、未传则不出现在 JSON 里;形态里的函数名必须是净化后的名字,规则见 toolname);程序内的 host.() 与普通工具调用走同一套 Stage 判定环(不做这一步,本包就成了整条链上唯一绕过所有门禁的路径);v0.32.0 起缺省即拒绝:装了工具绑定却没装判定环 ⇒ 拒绝执行(ErrorKind=unmanaged),裸执行必须显式声明逃逸口(生产代码里由门禁拦下);绑定名净化后撞名即装配期拒(否则后绑者顶替前绑者,副作用错位无信号)。新增子进程后端(ptc-subprocess):把执行搬出宿主进程,用 cgroup v2 的 memory.max 兑现 MaxMemory(限物理内存;RLIMIT_AS 限的是虚拟地址空间,会把 Go runtime 一起掐死——已实测否掉)——进程内形态兜不住内存(Go 运行时没有「只给一个 VM 限内存」的开关,goja 公开 API 也没有内存限制),故子进程形态如实声明自己兑现这一项(sandbox.Capable),建不出即拒绝执行(fail closed),兜不住的后端在登记簿里不会被选中
sandbox 116 oagentkit/guardx 执行沙箱的档位语义与后端协议(门面,核心在 oagentkit/guardx/sandbox):Level(进程内解释器 → 内核级 → 硬件虚拟化,单调可比,零值即最弱档——漏声明的后端只落到最弱一档,方向是安全的那一侧)+ Provider(后端自报提供哪一档并执行程序)+ Registry(登记簿;按要求档位选最弱满足者,无满足者即拒绝、不降级——降级会让「宿主声明的强度」与「实际执行的强度」之间没有信号,档位机制就成了装饰)。零内部依赖,ptc 反过来依赖它(沙箱机制不认识任何后端,后端认识沙箱)。已知局限:档位是后端自报的,内核不校验其真实性——宿主不信任某后端,唯一办法是不登记它。档位声明要签字:后端的档位登记在 policy 的 sandbox_providers 段(人 review 时看得见「它凭什么说自己有这一档」),门禁 harness-sandbox-provider-declared 核对登记与代码事实不漂移(走 go/ast 按方法指纹认出后端实现)
watchdog 127 无 被动配对不变量监视:环节收口仍有未执行声明 / 结果无对应声明;只告警不裁决(热路径裁决在守卫侧)
guardset 1027 oagentkit/guardx、oekit/pkg 守卫装配契约:一组相互独立的判定一并求值,多条拒绝合成一条(按 Rank 定序、不可重试优先的保守裁决、指引指向真正卡住的那一条)。接成 invoke 链上单独一环——链在第一个拒绝处就收口,后面的守卫永远没机会说话,模型一次只听得到一个理由,这正是本包补的那一面;链本身的顺序/留痕/panic 兜底沿用上游,不重复实现。装配面 Assembly 提供「撤销」的机械路径:撤销 = 换整份,不是改其中一条——改其中一条会让「当前有哪些守卫」出现两个真相;换份走 CAS + mutate(旧值)(并发撤销不丢更新,否则「撤销被撤销」)、代次住在被换的那份里(全局代次是投影)、链不用重接、旧份不被修改、在途判定跑在旧份上;「全部撤销」不是降级是失守(换上去的那份永远非空)
archcheck 5246 gopkg.in/yaml.v3 架构门禁执行体:architecture-policy.yaml 的可执行面(规则判定 / 门包判定 / 依赖方向判定 / policy 自校验 / 结构阈值)。可导入包——宿主仓复用同一套判据,不必各写一遍(各写一遍必然各判一套,漂移到没人发现为止)。判据与执行体分离:结构性判定一律走 go/ast(import 声明有六种以上书写形态,逐行正则必然漏)。依赖方向判三条:越界内部依赖 / 越界跨仓依赖(ext: 登记)/ 未登记包目录。执行沙箱后端的档位登记(sandbox_providers 段 ↔ 代码事实,走 go/ast 按方法指纹认出后端实现,核对档位漂移与未登记)。陈旧包根当天即红——root 指向空目录即「声明与事实已偏离」,而没有执行体时这句话没人读
trajectory 1535 无 轨迹投影器:事件流 → 一轮 turn 的步骤序列(增量投影:喂新事件继续更新既有行)。行含 kind / 起止时刻 / 实测耗时(起止相减现算)/ 工具调用配对状态(paired / missing_result / duplicate_result / unpaired_result / duplicate_call)/ 摘要。⭐ 结构上不设时长字段——时长唯一来源是起止相减,估算值无处安放(没有实测就是没有实测,不拿 payload 体积之类糊过去);配对缺口语义分两层:missing_result/unpaired_result 是当前状态(等到了会被清掉),duplicate_* 是发生过(永久留在清单里)。事件倒挂(结果先于调用)按「就地补起点」处理且此时判为无实测值。与 watchdog 分工:watchdog 判不变量(只告警),本包判这一轮的流水(给宿主渲染)。另有 OTel 映射契约(otelmap.go):每个属性写明来源字段 / 取值域(含 Go 类型)/ 基数上界 / 能否进 metric label,Span.ParentID 声明父子,trace 边界定在轮(Row.ID 的唯一性范围恰好就是轮内);无界看似维度的字段(session_id/agent_slug)列进禁区清单。v0.33.0 起覆盖 sideeffect / checkpoint 行(副作用阶段与快照序号,属性命名与既有类目同构;点事件落孤儿行——没有实测时长就不写时长)
taskboard 614 oekit/concurrency、oekit/pkg 多 agent 协调面的存储中立契约 + 一致性判据:dispatch 只回答「能不能派」,派出去之后「谁负责哪个子任务、依赖谁、怎么还回去」此前没有任何机制面——各宿主自己拿一张表兜着,于是「同一子任务被两个 agent 同时认领」在报告里读起来完全正常(两条分支都干完了活,各自都以为自己是负责人)。本包不给存储实现(表结构/事务边界/持久化是宿主资产,给一份参考实现宿主就会照着改它,最后两份真相并存——与 checkpoint.StoreCAS 同一分工)。三条口径:① 认领必须原子由存储保证,本包只判结果(ConcurrentClaim 并发跑一遍,成功者必须恰好 1 个;DuplicateTokenSubmit 让同一令牌的并发提交者正面相撞,成功者同样必须恰好 1 个);② 释放/完成凭令牌不凭内容(同名 agent 与「失败→释放→重试」都会让内容比对认错人)——v0.33.0 起令牌由存储铸造(Claim 改收「要多长的租约」的 ClaimRequest,令牌随返回的 Task 给出):调用方自带令牌时「只认 token」名存实亡,它能造出任意 token,输家的迟到释放照样放掉赢家的闸;③ failed 不是终态(一次失败不能把子任务永久钉死,否则补救路径静默失效)。v0.33.0 起带续期与接管代次:RenewingBoard.Renew 是资格检查不是心跳(校验令牌仍是最新代、租约未过期,才把到期时刻推后);Lease.Fence 与令牌同一次铸造、同一任务上严格递增——旧持有者迟到的提交凭「令牌不匹配 ⇒ ErrLeaseLost」必然被拒。Check 判快照不变量:id 唯一、依赖存在且无环、认领态与租约必须同时成立、依赖未完成不得认领、终态不带 owner、非认领态不残留租约/代次
skilldisclose 360 oekit/pkg 技能披露:确定性挑出该给模型看的那几个技能。与 tooldisclose 不是重复——那边的检索键是「工具调用需求」,而技能的触发信号有三种且强度不同:点名 > 资源命中 > 描述相关。混成一个分数会让「相关」压过「点名」,而那正是用户已经明确表达过的意图(故点名权重 1000,任何相关度信号之和都够不到)。判据三条:点名压过一切;同分按名字(不依赖目录遍历顺序与 map 顺序);预算裁剪显式(Truncated,静默截断会让宿主以为「就这几个相关」)。空结果必须写明原因——「没有检查」与「检查通过」在调用方看起来一样,而前者是「模型不知道有这个能力」
hookengine 296 oekit/pkg Hooks 的判定面(不是传输面):Codex 是「外部进程协议 + 规则文件」,DSH 是「动态插件包」,形态已经分叉——由内核选边等于把一种传输焊进契约,故只定三件跨形态恒定的事:匹配(事件 + 工具前缀)/ 裁决(allow / deny)/ 顺序与收口(按名字确定序,第一个拒绝即停)。传输与加载归宿主(Runner 是唯一接缝)。两条 fail-closed:阻塞 hook 执行失败 ⇒ deny(跑不起来不等于没问题,否则一个写错的检查会静默变成空检查);超时裁决必须显式声明(OnTimeout,缺省 deny)——没有缺省值时超时会被 ctx 取消掩盖成「这次没拒绝」。非阻塞 hook 不参与裁决但失败必须留痕:它一旦能影响裁决就只是「慢的阻塞 hook」,而宿主会以为它是旁路的
memorypipe 219 oekit/pkg 记忆注入的三段:注入 / 引用 / 打标(读写时机与摘要生成归宿主,与 budget 留 Summarizer 是同一条边界——内核不替宿主决定「这段对话值不值得记住」)。注入侧三件与策略无关、各宿主各写一遍又必然各判一套的事:① 来源是前提——没有来源的记忆与模型的猜测在文本上无法分辨,故不注入、显式丢弃并给出原因;② 逐条打标(id / source / at 三样缺一就少一条追溯路径:没 id 无法引用,没 source 无法判可信度,没 at 无法判新鲜度);③ 裁剪显式。scope 精确匹配、不做父级兜底:按 project 查却回退到 global 看起来友好,代价是作用域失去意义——只该在某个项目里生效的记忆会在任何会话里生效,而报告完全正常。同 ID 只留最新(两条正文不同的同 ID 记忆让读者只能猜)
delegate 176 oekit/pkg 异构委托的契约面:ACP 一类东西同时是协议 + 传输,线格式归上游;本包只定跨传输恒定的三条。① 能力必须子集(做不到就拒,不许「尽力而为」——尽力而为的结果无法判定「没做到」与「本来就没要求」);② 最小能力原则选执行方:多个都能接时取能力最少者,同数量按名字——按顺序取第一个是错的,顺序通常来自配置文件书写次序,一次无关重排就会换掉执行方;③ 回执状态三值(completed / failed / refused),且 completed 不得带 Failure、failed 与 refused 必须带原因——把「被拒绝执行」混进「执行失败」会让委托方去修一个不存在的 bug
worktree 232 oekit/pkg 工作区隔离的声明面与冲突判据(不碰真实文件系统——建目录/挂载/chroot 是宿主与执行后端的活)。并行工作区会不会互相踩此前没人判:两个 agent 各自「在自己的目录里工作」,可写区完全重叠时任何一边的写入都会让另一边的结论失效,而报告里只有两份「各自成功」。判三件:声明自检(同一工作区不得既只读又可写)、可写区重叠即冲突(只读重叠不算——多个人读同一份文件没有危害,写没有第二份真相)、无命中即不可读写(fail-closed:默认允许会让漏写的一行声明变成全域可写)。不支持通配(写明的局限):* 与 ** 的语义各实现不同,进契约就会出现「按 A 的理解写、按 B 的理解判」,需要更强表达力时宿主自己展开成多条精确声明
ticket 203 无 一次性短时授权票据的机制面:签发 / 单次消费(用过即焚)/ TTL / 用途绑定(载荷原样携带,语义归宿主)。⭐ 过期判定与消费判定同一处(两处各判一次必有一处漂);时钟可注入——过期语义要能被确定性测,不靠 sleep(靠 sleep 的话「等到了」会被当成「判对了」,判定写反了测试照样绿);票据空间有界且满了显式拒发(默默淘汰会让宿主以为票据还在、实际已失效 ⇒ 表现为「偶发地连不上」,极难查);随机源失败显式报错(不降级成可预测 id——id 一旦可预测,单次消费就没意义了)。进程内存储(重启即失效)对「只活几十秒的票据」是正确默认:落盘即多一份要擦的痕迹

「生产行数」口径:wc -l 统计 *.go 且不含 _test.go——包体量看的是要交付的实现, 不是它的测试。依赖列只列仓外 import;「无」表示该包生产代码只用 stdlib。

本表由 make check 末步的 docsync 机器校验(cmd/docsync),判定分两类: 依赖列逐项相等——加依赖是显式动作,必须同步;行数按 25% 相对容差判定 ——容差内的逐行涨落不拦,改一行代码不必顺手改文档,而跨量级的误述 (某包声称 ~86 行、实际 412 行那种)仍然拦下。行数取较大者为分母, 「少写」与「夸大」同等对待。职责列与依赖列非空也在判定内。

internal/ 下的包刻意不在本表:它们是内部件,不是对外承诺(docsync 的 反向收录判定只看一级目录,故 internal/subhub 不会要求一行)。但它们不受 「未登记包」豁免——architecture-policy.yaml 的 modules 段照样要给 {id, roots, requires, owner},否则依赖方向门禁判它「对本门禁隐形」。

特性

边界不是文档,是门禁

「内核不许长出领域知识」这句话写在散文里就会烂掉——本仓把它做成了 8 条正则规则 + 6 条 go/ast 结构性规则(门包不变 / 依赖方向 / 沙箱档位登记 / 执行路径 / 子进程出口 / 未受管逃逸),全部挂在 make check 上,CI 只执行这一条。 分界线很清楚:散文不是门禁——真有人往门面包里塞一份实现,规则不响,结果是与上游并存的第二份实现。

设计推导见 ADR-0001

工具调用只有一条路

渐进披露、披露门、多守卫合成、失败分类、程序内调用——全部接成同一条判定环,包括 ptc 里模型自己写的 host.<tool>()。 少接一段,那个位置就成了整条链上唯一绕过所有门禁的路径。

普通工具调用 ─┐
             ├─→ invoke.Stage 链 ─→ guardset(并求值·合成拒绝) ─→ tooldisclose(披露门)
ptc 程序内   ─┘                            └─→ policy(审计门) ─→ dispatch(派发拓扑)

guardset 补的是「链在第一个拒绝处就收口、模型一次只听得到一个理由」那一面:多条拒绝合成一条,取「全部可重试才算可重试」的保守侧,并点出该先处理哪一条。

崩了能接着跑,而且不会「两条分支都赢了」

checkpoint 把恢复判定做成纯函数:可信边界按依赖判定、终态不自动重开、序号单调闸。 commitgate 用独占提交闸保证终态只被提交一次(statemachine 是纯谓词、SeqGate 只排快照序号,都排不出「两条并发分支都以为自己赢了」)。 为什么:临场决定必然选最坏的那个(要么盲目全量重跑、要么盲目跳过);终态独占权必须单独一道闸,且 Commit / Release 成对。

上下文是有限预算,不是无限历史

三件套分工泾渭分明:budget 管输入侧分段计量与压实、offload 管二进制本体躺在消息里(两阶段:首次只标记不替换,否则模型会引用一个自己从没看过的东西)、spill 管输出侧截断与分页取回。 为什么:只给总数无法区分「工具面膨胀」与「历史膨胀」,而两者处置完全不同。

失败是数据表,不是散落的 if

toolfail 把「类别 → 可重试性 / 退避 / 需人工 / 重发安全 / 模型指引」做成数据表,不可归类一律不可重试(fail-closed)。 为什么:重发安全与重试价值是两个正交维度——超时值得重试,但服务端可能已执行,原样重发就是重复施加副作用。

N 轮 ReAct 往返塌缩成 1 轮

ptc 让模型写程序、进程内 goja VM 执行、程序内经 bindings 调宿主工具,只把归并后的小结果带回上下文:原始数据不过模型面。 为什么:三重预算(墙钟 / 调用数 / 字节数)替代 OS 沙盘,纯 Go 零 CGO,无 Node 分发面。

工程底座
指标 实测
生产代码 35,463 行 / 192 文件(不含 _test.go)
测试 220 个测试文件
覆盖率 门槛:全局 60% + 逐包下限 70%(两条判据判两件事,见下)。实测值不抄在这里,以 make cover-gate 的输出为准
结构阈值 max_file_lines: 400,max_methods_per_type: 25(按实质方法数判,见下),enforce: true
门禁 8 条规则(rules 段)+ 6 条 go/ast 结构规则(门包不变 / 依赖方向 / 沙箱档位登记 / 执行路径 / 子进程出口 / 未受管逃逸)+ 5 个门包(dispatch/policy/reminder/toolfail/sandbox——核心归上游、只保调用面稳定)+ 44 包依赖方向 + README 包表一致性 + USAGE 包数口径 + API 生命周期与错误登记口径(apilifecheck,#113/#104) + 竞态(-race)+ 漏洞扫描(govulncheck)+ 交叉构建矩阵
例外登记 0

上表分两类:前两行有机器判定,其余仍是散文(issue #31)。

✅ 已判:生产代码行数 / 文件数 / 测试文件数——三项由 docsync 判相等,不留容差。 包表那 25% 容差是给「逐行涨落」留的噪声滤波,而这张表的历史漂移量恰好落在容差里 (895 / 578 / 573 行相对 1.9 万行都在 3% 上下),给它容差等于让静默过期继续成立。 口径就是本段末尾那三条命令,门禁报错时也会原样给出来。

⚠️ 未判:覆盖率与其余四行。

  • 覆盖率不判是刻意的:它随「跑的是哪个用例集合」变——2026-11-06 同一批用例、 生产代码一行未改,量出 85.5 / 85.0 / 87.0 三个值。让散文去复述一个由门禁实时 给出的数字,只会制造两处不一致;故这一格只写门槛(门槛由 Makefile 判,不会漂), 实测值看 make cover-gate 的输出。
  • 结构阈值 / 门禁条数 / 例外登记数是描述,各自有执行体(policy + 各条门禁), 写错不会让门禁失效、只是描述不准——判定成本高于收益,故不判。

于是「改这张表」的规矩变成两半:前两行照门禁的报错改(它会告诉你实测值), 其余行改前请手算,别信上一版。 v0.6.4 那次修「工程底座」三处漂移(生产代码 11,491 行/66 文件 → 13,336 行/77 文件、 测试 84 → 99 个文件、覆盖率 80.4% → 86.6%)时,这三处已经过期了三个月—— 它们是 archcheck 从 cmd/ 下沉成独立包(2026-10-19)之前的旧值,此后没人更新。 这正是本仓「散文不是门禁」那条纪律的反面案例:机器只管得着被写成规则的那部分。 改这张表时请手动重算,别信上一版。

重算口径(照抄这三条,别手算)——「生产代码」= 全仓非 _test.go 的 .go, 排除隐藏目录与 vendor:

find . -name '*.go' -not -name '*_test.go' -not -path './.*' -not -path './vendor/*' | wc -l
find . -name '*.go' -not -name '*_test.go' -not -path './.*' -not -path './vendor/*' -print0 | xargs -0 cat | wc -l
find . -name '*_test.go' -not -path './.*' -not -path './vendor/*' | wc -l

覆盖率取 make cover 的最后一行(go tool cover -func=coverage.out | tail -1)。

2026-11-06 第八轮校准(生产代码一行没动):19,624 行 / 113 文件未变,测试文件 139 → 141(sandbox/forwarders_test.go、cmd/docsync/run_test.go),覆盖率 85.5% → 87.0%。这 +1.5pp 全部来自补测,不是新写的生产代码:sandbox 60.0% → 100%、cmd/docsync 23.6% → 88.8%(run() 端到端,用 t.Chdir 造夹具)。

中间有一次「只补了 sandbox」的测量是 85.0%——比上一版低 0.5pp,而生产代码 没变、只多了一个测试文件。这件事本身值得记下来:这张表的数字会随测量口径与批次 漂(同一批用例在不同轮次里给出 85.5 / 85.0 / 87.0 三个值)。

2026-11-06 第九轮(这一轮把表接进了门禁,issue #31):19,624 → 20,397 行 / 113 → 118 文件 / 141 → 146 个测试文件(+773 行)。前两行从此由 docsync 判相等,报错时会把实测值与三条重算命令一并给出,不需要再靠「记得回来手算」; 覆盖率那一格删掉了手抄的百分数,只留门槛(门槛由 Makefile 判)。 这一轮记下的教训是:「会漂的数」与「不会漂的数」要分开对待——覆盖率属于前者, 让它出现在散文里,就等于承认「这里迟早会说谎」;行数/文件数属于后者,那就判相等, 连容差都不该给。

同日补的第四条门禁口径:cover-gate 从「只判全局」变成「全局 + 逐包下限 70%」。 起因是全局 85.5% 同时掩盖着 cmd/docsync 23.6% 与 sandbox 60.0%——单个包的 塌方在大盘里完全看不见。sandbox 的 60% 已补到 100%(门面包的每条语句就是 一句转发,转发目标或参数顺序写错编译得过、语义却错,故对门面包而言语句覆盖率 恰好是恰当的度量);cmd/docsync 当时先登记豁免(判定逻辑仍在 package main、 没有可注入的接缝),同日即拆除——改用 t.Chdir 造夹具后 run() 端到端可测, 23.6% → 88.8%,PKG_COV_EXEMPT 现已为空。

2026-11-06 第九轮校准(差距台账落地轮):上一版记的 19,624 行/113 文件、141 个测试 文件、87.0%,这一轮按 docs/GAP-ANALYSIS-20261106.md 落了 P0-1/P0-2、P1-1..P1-5 与 五项 P2(cmdpolicy / egress / preset / sessionref / taskboard / skilldisclose / hookengine / memorypipe / delegate / worktree,外加 archcheck 的样例自检与子进程出口判据),实测 23,281 行 / 130 文件、156 个测试 文件、87.6%;门禁表里「依赖方向」那一格也从 34 包改为 42 包(archcheck 实测)。 这一轮值得记一笔的不是行数涨了 3,657——而是十条门禁里有两条是这一轮才有的 判定面(规则样例自检、子进程出口),它们此前都是「有声明、没人判」。

平台支持矩阵(2026-11-06 补齐)

此前本仓从未声明支持哪些平台,也没有任何东西验过——本机 darwin 全绿,而本仓是 「被宿主编进二进制」的库,真正的交付面是 linux。2026-11-06 实测才发现 Windows 根本编不过,而这个事实在仓里一个字都没有。

平台 go build ./... 依据
linux/amd64 ✅ 支持 make crossbuild 每次 make check 都验
linux/arm64 ✅ 支持 同上
darwin/arm64 ✅ 支持 同上(本机开发面)
windows/amd64 ✅ 支持(语义降级,见下) 2026-11-07 回归:上游 oagentkit/runtime procx 落地 Windows 进程树路径(Job Object 建组 + TerminateJobObject 整树硬杀,runtime v0.11.0 起,oagentkit/runtime#24)——make crossbuild 恢复 windows/amd64,CI 增加 windows job 真跑测试。语义降级:Windows 无进程组信号语义,TERM 优雅退出降级为整树硬终止;这是接口层写明的差异,不是对等实现

处置方式:显式声明不支持,而不是留着「看起来支持、其实编不过」。make crossbuild 只判 CROSS_TARGETS 里列出的平台——为一条永远编不过的目标留门禁不是严谨,是把红线 泡软:它会常年红,然后被人忽略,连带把别的红一起忽略掉。

要在 Windows 上使用,前提是上游 procx 先给出可移植的进程组实现;在那之前本仓 ptc 的子进程后端不适用于 Windows。这条限制不在本仓能力范围内,故不做兼容层。

三条「目标存在但没人跑」的门禁(2026-11-06 补齐)

2026-11-06 的仓库审计里有三条门禁的共同形态:目标/文档里有,但没有任何东西执行它——这和 lint 当年一模一样(见 Makefile 的 lint 注释)。 处置方式也一样:接进 make check,不挂到 ci.yml 上变回「推送后门禁」。逐条实测漏项如下。

门禁 此前状态 它漏掉的东西(实测)
race race 目标存在,但 check 依赖链与 ci.yml 都没有它 本仓的并发面(策略代次、提交闸、共享 map)从没被竞态检测过。补上时踩到一个硬事实:linux 上 -race requires cgo,而 GOENV := CGO_ENABLED=0 ⇒ race 目标不能跟着 GOENV,否则在 CI 上必红
vuln Makefile 里没有这个目标,govulncheck 从未跑过 首次跑出 6 条漏洞,全部在标准库(crypto/x509 / net/http 系列 / crypto/tls),修法是抬 go 指令到 1.27.3。载体正是「下游用本仓编出来的那个二进制」
crossbuild 没有任何交叉构建门禁 见上一节:Windows 编不过这件事从来没被记录过

顺带补上的第四条:覆盖率此前只判全局。全局 85.5% 同时掩盖着 cmd/docsync 23.6% 与 sandbox 60.0%——大盘涨 1% 就能抵消一个包从 70 掉到 20 的塌方,而报告里只有一行「85.5%,绿」。 故 cover-gate 现在跑两条判据(逐包判据的执行体是 cmd/pkgcov——原先是 scripts/pkgcov.awk,issue #36 收编为 Go 并带上自己的用例),且逐包下限的豁免失效即红(豁免包一旦自己达标,门禁会要求当天删掉那条豁免——本轮就是这样把它拆掉的:cmd/docsync 补测到 88.8% 后第一次跑门禁,报的正是「豁免失效,请从 PKG_COV_EXEMPT 移除」,而不是默默放行)。

头号红线:不得长出领域知识

拆分做完后最现实的风险不是拆不动,而是业务代码一年年渗回内核,等发现时内核已经退化成「换了个名字的 monolith」。 这条红线的规则清单、推导与事故来源已下沉成 ADR 集——README 只留结论,入口是 ADR 索引:

ADR 一句话结论
ADR-0001 领域知识只能住在宿主/领域包,本仓只留机制面——这条边界靠机器判,不靠自觉。
ADR-0002 机制面若要跨仓复用,就不能锁在 package main 里——判据与执行体分离,宿主只写声明面。
ADR-0003 本仓不是孤岛——家规只写在别处的文档里等于没有规矩,必须落成本仓自己执行的门禁。
ADR-0004 执行只允许经 sandbox 登记簿,档位声明必须有人签字——否则声明强度与实际执行强度之间没有信号。
ADR-0005 门包只许转发、不许长胖——门面一旦自己长出实现,就成了与上游并存的第二份真相。

通用化判据

内核每加一个能力,先过这一问:换个宿主,这段代码要不要改? 要 → 属于宿主,不该在内核。 落到三条可执行约定:判定语义归内核、判定依据归宿主;展示文案归宿主;测试夹具用合成数据。

设计推导见 ADR-0006

文档站(GitHub Pages · VuePress)

yi-nology.github.io/goagent-kernel

VuePress 文档站:介绍、设计初衷、怎么用(接入路线图 + 最小示例)、能力详解(九组)、 门禁体系、坑速查、包清单与机器事实。

站点源码在本仓 site/(docs/ 内容 + gen.mjs + .vuepress/config.js), 发布走 .github/workflows/pages.yml(build_type: workflow): push 到 main 且站点内容或其事实输入(README.md / go.mod / Makefile / architecture-policy.yaml / docs/USAGE.md)有变更时,CI 自动构建并部署。

本地构建:

make site     # = cd site && npm ci && npm run docs:build(开发预览:npm run docs:dev)

站点上的每个数字都不手抄。 生成器 site/gen.mjs 在每次构建前把 README(包表 / 工程底座)、go.mod(依赖版本)、architecture-policy.yaml(规则清单)、Makefile (覆盖率门槛)、docs/USAGE.md(坑速查表)抽成站点页——解析不到即构建失败, 不静默填默认值。生成页不入库(入库就会没人重新生成,等于把「自动」改回「手抄」)。 理由与本仓其它纪律同源:手抄一遍就等于多一份会静默失真的真相。

迁移记录(2026-11-11):站点原先住在 gh-pages 独立分支(build_type: legacy), 生成器与源码不在 main 里——恰好违反本仓「单一真相」的纪律。当日迁回 site/ 并升级为 VuePress 文档站;gh-pages 分支冻结为历史归档(迁走前的最后一版, 只作对照,不再接收产物)。

快速开始

要接入本仓:直接看 docs/USAGE.md,那是按「宿主接入顺序」 组织的完整指南——九组能力逐个讲清「职责 → 最小集成片段 → 关键符号 → 坑」, 末尾有一张按症状索引的坑速查表。

要改本仓:

make build          # 编译 archcheck
make test           # 全量单测
make check          # fmt + vet + lint + test + cover-gate + vuln + race + crossbuild + architecture-check + docsync + 仓库卫生
make architecture-check --report   # 看全量违规清单(默认前 10 条)

macOS:本机 go build/test 一律带 CGO_ENABLED=0(新 SDK 27.0 与 CLT linker 不兼容 + 许可未接受),Makefile 已内置。

make check 需要 golangci-lint(CI 钉 v2.14.0);未安装时该目标明确报错退出, 不静默跳过——没人跑的门禁等于没有门禁。

提交与合并

main 有分支保护:必须走 PR,且必须通过两条检查——

  • fmt + vet + lint + test + cover-gate(全局+逐包) + vuln + race + crossbuild + archcheck + repo 卫生(Linux 综合门禁)
  • windows (真跑测试)(Windows 上真执行,不是只编译)

两条都是 strict(需与 main 同步)。管理员保留 bypass 通道(应急直推), 但门禁本体全在 make check 里——本地能过,CI 就该过;CI 红了先看日志分清 「真违规」与「基建抖动」,不要绕过。

这条保护是 2026-11-11 开的(issue #77)。此前 main 完全无保护,直推即合, CI 通过与否与能不能合无关——于是「CI 是绿的」不再等于「能合」, 门禁的失败形态又变回了「变绿」。

本地多仓并行开发

本仓处在 oagentkit / oekit 矩阵里,改本仓时经常要同时改上游仓(如 guardx)。 这里有个必须知道的陷阱:

Go 的 replace 只在主模块生效,依赖模块的 replace 不级联。

在本仓 go.mod 里写 replace github.com/oagentkit/guardx => ./.guardx-work/guardx, 本仓构建全绿(自己就是主模块);但下游宿主把本仓当依赖时,这条 replace 被静默忽略, guardx 按 MVS 回落到已发布的旧版本,于是报 no required module provides package github.com/oagentkit/guardx/injectband—— 错指 guardx,真正原因(本仓依赖了未发布的新包)不出现,一行本地验证代码断掉所有下游。

正确姿势是 workspace(use 跨仓生效,且不碰任何 go.mod):

git clone https://github.com/oagentkit/guardx.git .guardx-work/guardx   # 未跟踪
make devlink     # 生成本地 go.work,把 guardx 工作副本接入
# ...改 guardx,本仓构建即时生效...
make devunlink   # 断开,构建回到 go.mod 声明的已发布版本

配套纪律:

  1. go.mod 零 replace。 go.mod 不得含 replace / go.work 不得入库 两条门禁在 make check 里(本地与 CI 同一条路径),本地 go.work 由 .gitignore 兜底。
  2. go.mod 里 require 的版本必须是已发布的 tag。 若确需依赖未发布的新包, 用 make devlink,不要动 go.mod。CI 在干净环境构建,未发布版本解析不到直接红—— 这是发版顺序纪律的第二道防线。
  3. workspace 期间别跑 go work sync。 它按 workspace 的构建列表回写各模块的 go.mod / go.sum:仓不全时用不到的依赖会被直接删掉(实测把 oagentkit 的四个依赖 整片剥掉)。要整合同步,先 make devunlink,再在单模块下 go mod tidy。
  4. make devlink 可重复执行。 已有 go.work 时走 go work use -add 追加而不是 重建,所以往 workspace 里加第二个仓不会抹掉别人已加的条目。
  5. 验下游而不是只验本仓。 本仓作为主模块绿,不代表作为依赖也绿(replace 的失效就发生在这个差别里)。

下游升级须知(破坏性变更)

本仓的公开 API 里有一部分类型来自上游矩阵(invoke 判定环原语)。上游把它换了包, 本仓跟随,下游的 import 路径就要跟着换——这类变更记在这里,不记在 tag 注解里 (tag 注解会沉到 git 历史里,README 不会)。

本仓版本 变更 下游要做什么
v0.33.0(已发版,tag v0.33.0) 两处 BREAKING + 两处修复 + 一处判据收紧:① sideeffect 引入执行意图——Acquire 原子登记「本次尝试还在」(Key 级 IN_FLIGHT),租约过期且未裁决 ⇒ 转入需对账态而不再重新授予执行权;Store 接口新增 Abandon,Release 收窄为「只放资源闸、不动意图」;② taskboard 认领令牌改由存储铸造(Claim 改收 ClaimRequest{TTL},ConcurrentClaim 末参改 TTL);③ 参考存储补持有者校验(释放后的租约不得再续期/记账);④ checkpoint.StoreCAS.SaveIfNewer 拒收 Seq == 0(它不是一次真实提交,放它落库等于给了它唯一能覆盖既有快照的写);⑤ egress 自报与实测分离(新增 StrengthDeclared 与 EgressVerifier,自报不再判 enforced)。依赖地板无变化 ① 实现 sideeffect.Store 的宿主要补一个方法并改一条语义(编译期即红):新增 Abandon(ctx, act, lease, reason)——撤销该 Key 的执行意图并释放资源持有;Release 改成只放资源闸、不动意图(旧语义会连意图一起清掉 ⇒ 「可能已生效」那条路的重复施加入口重新打开)。Acquire 的判定顺序改为「已完成 > 结果未知 > 执行中 > 资源被占 > 授予」:执行中且未过期 ⇒ contended;执行中且已过期 ⇒ uncertain(须先对账,RetrySafetySafe 除外)。
② 实现 taskboard.Board 的宿主要改 Claim 签名(编译期即红):收 ClaimRequest{TTL} 而非自带 Lease,令牌由存储铸造并随返回的 Task 给出——调用方自带令牌会让「释放/完成只认 token」名存实亡(它能造出任意 token)。ConcurrentClaim 最后一个参数改为 TTL。
②′(本轮补齐,#87)长任务要能续期:实现 taskboard.RenewingBoard(新增 Renew(ctx, id, token, ClaimRequest{TTL}) (Task, error)——资格检查不是心跳:令牌不是当前租约的或租约已过期 ⇒ ErrLeaseLost 且不改任何状态;通过才推后到期时刻,校验与延长须在同一原子操作里)。Lease 新增 Fence 字段(接管代次,与令牌同一次铸造,同一任务上严格递增;续期不递增)——下游资源可凭它拒收过期持有者的迟到写入。不实现 Renew 的存储编译不破(单独接口),但 boardtest 套件对缺失硬失败。
③ 契约套件升级:sidetest 22 条(须实现 ClockedStore,过期判定读注入时钟)、boardtest 16 条(须实现 ClockedBoard + Seed + RenewingBoard;并发认领规模档可配置,缺省跑 10 与 100 两档——4 并发撞不出「读-判-写」窗口)——宿主实现跑一遍即知是否合规。
④ checkpoint 宿主:SaveIfNewer 须拒收 Seq == 0(返回 ErrStaleSeq 且不写入)。
⑤ egress 宿主:后端只是声明能力(实现 Enforcer)时,兑现强度是 declared 而不是 enforced;要判 enforced,后端须实现 egress.EgressVerifier(VerifyEgress() (Mode, error))给出实测证据。
⑥ 无需 go mod tidy(本版无依赖地板变化);⑦ 文档:docs/adr/ 新增 ADR-0007《执行意图》,推导与取舍从 CHANGELOG 下沉到决策记录
v0.32.1(已发版,tag v0.32.1) 修复轮,无破坏性变更:PTC 未受管逃逸口的豁免登记路径此前是「照着指引做就红」——自校验的「规则存在」口径只看 policy rules 段里的规则 id,而 harness-ptc-unmanaged-escape 是恒开的、不在 rules 段里 ⇒ 宿主按 v0.32.0 的违规文案去登记豁免时,自校验反而报「指向不存在的规则」,逃逸违规也没被豁免。无 API 变更、无依赖地板变化 ① 无需 go mod tidy,调用面逐字不变。
② 若你的宿主用了 PTC 的 AllowUnmanaged 逃逸口(典型形态:PTC 只是本层工具的批量调用面,而每个绑定工具已由别处逐笔审计):v0.32.0 上你照着内核指引登记豁免会被红;升到 v0.32.1 后这条路径才走得通——在 exceptions 段登记 {rule: harness-ptc-unmanaged-escape, path, line, reason, until, removal}(path+line 精确指向那处字面量),豁免真的生效(违反计入 excused、不再阻断),until 到期即失效。
③ 三条收紧(可能影响既有登记):rule: harness-policy-self-check 的例外登记直接报出(自校验不可豁免);例外指向当前不在判的内置规则(该段被 enabled: false 关掉)报出;policy 规则 id 与内置规则重名也报出。
④ 既有 exceptions 登记若指向内置规则,在 v0.32.0 上它们是静默不生效的(当时自校验会红,但没跑门禁就没人知道)——升上来后它们开始真的生效,请逐条复核 until 与 removal 是否仍成立
v0.32.0(已发版,tag v0.32.0) 两处 BREAKING + 三处判据增强 + 参考运行时 + 文档体系重构:① sideeffect 引入「结果未知」一等状态——Store 接口新增两个方法(MarkUncertain / ResolveUncertain)、Effect.Apply 与 Runner.Execute 的执行体签名新增 lease Lease 参数、Action 新增 RetrySafety 字段(零值 = 未声明 = 按最弱处理)、Outcome 新增 OutcomeUncertain;② ptc 缺省即拒绝——装配了工具绑定却没装判定环 ⇒ 拒绝执行(ErrorKind=unmanaged),裸执行须显式声明 AllowUnmanaged;③ egress 判据返回兑现强度(新增 VerifyVerdict / SatisfiedVerdict,环境变量兑现恒为建议型);④ 新增 examples/reference-runtime(纯 stdlib 内存存储,零新依赖);⑤ 文档:README 收敛 + docs/adr/ 6 篇 ADR + USAGE 数字纳入 make docsync 机器判;⑥ main 开启分支保护(必须 PR + 必过检查,管理员可 bypass)。依赖地板无变化 ① 实现 sideeffect.Store 的宿主要补两个方法(编译期即红,接口强制新语义、不给可选兼容面):MarkUncertain(ctx, act, lease, reason) 把 Key 转入未知态、ResolveUncertain(ctx, act, res) 由宿主对账后裁决(已生效 → 记为完成;未生效 → 清除未知标记、允许重试)。裁决先到者为准(幂等)、不校验栅栏(依据是下游事实,不是持有权)。
② 执行体签名变了:Effect.Apply / Runner.Execute 的函数要加一个 sideeffect.Lease 参数——下游据此拒收过期持有者的迟到写。
③ 动作要声明重试安全性:不声明(零值)即按最弱处理——施加失败判「结果未知」并禁止重试,必须先对账;只有确认下游按幂等键去重,才显式写 RetrySafety: sideeffect.RetrySafetySafe。
④ ptc 调用方:装了工具绑定就必须装判定环(Stages),否则现在直接拒绝执行;纯计算程序(不装任何绑定)不受影响;确需裸执行的宿主显式声明 AllowUnmanaged(本仓生产代码里由新门禁 harness-ptc-unmanaged-escape 拦下)。
⑤ egress:原 Verify / SatisfiedBy 逐字不变,只是多了一对返回 Verdict 的入口——若你的宿主要区分「代理档已兑现」与「出站已被强制隔离」,改用新入口读 Strength(环境变量兑现恒为 advisory)。
⑥ 无需 go mod tidy(本版无依赖地板变化);⑦ 文档:docs/USAGE.md 的包数口径已改为机器判(包数 / policy 登记数 / 安装行版本三处),改文档数字后跑一次 make docsync 即知对错
v0.31.0(已发版,tag v0.31.0) BREAKING:项目整体更名 agent-harness → goagent-kernel——旧名在语境里与「模型外壳 agent harness」「评测 harness」「harness.io」三头撞车(实测搜索前排全是本仓明确不做的东西),名字把读者引向错误预期。GitHub 仓、Go module path、文档站 URL 全部换名;仓内 94 个文件的 import 与活文档已全部替换。历史快照刻意保留旧名(CHANGELOG 与 docs/ 标注「历史快照」的文档不改写历史);门禁规则 ID(harness-*)是内部标识符,不在本次更名范围。API 逐字不变——本次只是身份换名 ① 机械替换 import:github.com/yi-nology/agent-harness/… → github.com/yi-nology/goagent-kernel/…(全局替换即可,调用面零改动)。
② go mod tidy——module path 是编译期身份,不跑 tidy 会直接报找不到模块。
③ 旧 GitHub URL 自动 301(仓页面与 git 协议均然,旧 remote 不用急着改);文档站 URL 不重定向:yi-nology.github.io/agent-harness/ → yi-nology.github.io/goagent-kernel/。
④ go 指令地板 1.27.3 → 1.27.4(随 Go 2026-11-10 安全发布抬升,标准库 CVE-2026-97033…97054;govulncheck 当天报本仓命中 1 条):go mod tidy 后 GOTOOLCHAIN 自动对齐——地板抬升对下游是隐式 breaking,不跑 tidy 会报工具链不符
v0.29.0(已发版,tag v0.29.0) 判据面补强:cmdpolicy 事实面收编上游 bashguard:① DecideCommand 从「只比 argv 前缀」升级为全调用 AST 判定(事实面收编 oagentkit/tools/bashguard,mvdan.cc/sh 真 AST)——前缀判据的三类典型绕过在此闭合:多段(git status; rm -rf / 的 rm 段)、命令替换(echo $(rm -rf /) 里的嵌套 rm)、写重定向(git status > /etc/passwd 的 > 是语句级事实、不在 argv 里);② 新增事实面下限:含写重定向或动态展开($()/反引号/$VAR)⇒ 裁决不低于 prompt(这两类语义前缀规则结构上看不见,静默 allow 即「判据比声明宽」);③ 新增哨兵 ErrUnjudgeable(AST 超长/解析失败 ⇒ 判不了就不放行);Verdict 新增 Wrote/Dynamic 事实字段,Explain 追加信号说明。依赖:新增 github.com/oagentkit/tools v0.14.3(带入 mvdan.cc/sh v3.14.1);其余第一方地板与 v0.28.0 相同(该轮已抬到 guardx v0.24.1 / runtime v0.12.6 / toolgate v0.17.0 / pkg v0.42.0 等,以 go.mod 为准) ① go mod tidy——引入新依赖 oagentkit/tools(含 mvdan.cc/sh 传递依赖),不跑就报 updates to go.mod needed。
② DecideCommand 的判定结果会变严(这是修复不是回归):此前被绕过的三类命令现在会被拦/升 prompt——git status > x 由 allow 变 prompt、git status; rm -rf / 由 allow 变 forbidden(全调用面取最严)。若你的宿主依赖旧行为放行这类命令:要么按新口径补规则,要么改用 Decide(argv)(纯前缀、不做事实面判定,调用面一字未动)。
③ Verdict 新增 Wrote/Dynamic 两个字段:键名构造不受影响;无键名字面量构造会编译不过,改键名即可。
④ 命令解析失败/超长从「放行」变报错(ErrUnjudgeable)——判不了就不放行,与 ErrUnclosedQuote 同一处置口径
v0.28.0(已发版,tag v0.28.0) 两处上游能力收编 + 一处下游可见的行为修正 + 第四轮 issue 清零:① cmdpolicy 的分词面收编上游 oekit/pkg/shellx(本仓删掉手写分词器与 mergeSeparators,约 100 行)——收编顺带修掉三处 POSIX 错误:双引号内的反斜杠不再被一律丢弃("a\b" 由 ab 变回 a\b,bash 实测即此)、分隔符只在字符相邻时合并(a & & b 不再被误并成 a && b)、CR 归空白(CRLF 命令行的行尾 CR 不再粘进最后一个 token);② cmdpolicy.SplitCommands 的段文本改用上游 shellx.QuoteArgv 拼装,段变得可往返(段再分词逐项还原原 token);③ ptc 的子进程 stderr 落进 ExecutionAborted.Stderr 前过上游 guardx/logredact 脱敏(凭据形态打码:URL 内嵌凭据、Bearer 头、JWT、厂商 token 前缀、长 hex、键值对)——子进程常把连接串与 API key 打进 stderr(MCP server 启动回显配置是最高频形态),而这段文本原先原样进错误消息与诊断报告;④ 第四轮 issue 清零(#62-#67):egress 代理变量判据收严为「一个不缺、一个不歪」(#62)、绕过名单收口为 NO_PROXY/no_proxy 并集单判定(#63)、policy 门面补 Mutating(b) 转发(#64,toolgate v0.17.0:Op.Mutating 三态化,nil=未知 ⇒ 审计门 fail-safe 升人审)、ptc 子进程后端三处契约修复(#65-#67,判定环归因复用 stageOutcome 等) ① cmdpolicy 的分词结果在三处形态上会变(只影响含反斜杠 / 空格分隔的 & / CRLF 行尾的命令行):本仓旧实现给 "a\b" → ab,现在给 a\b——这是修复不是回归(bash 实测逐字一致),但若你的宿主按旧行为做过前缀匹配,需按新口径核对。② SplitCommands 返回的段文本形态变了(含空格的段现在带引号)——段仍是合法的 shell 命令行,可再分词、可回放,但不要再按字面比对段文本(改判段再分词后的 token)。③ ptc 的 ExecutionAborted.Stderr 现在是脱敏后的文本:崩溃原因(fatal error / panic 栈)原样保留,凭据被打码——若你的宿主把 stderr 全文写进自己的日志系统,脱敏后是安全方向(凭据不再二次扩散);若你的宿主依赖从 stderr 里读到某个明文 key(不当用法),请改从别的通道取。
④ go mod tidy:本轮无新增依赖(shellx 与 logredact 都在已声明的 oekit/pkg 与 oagentkit/guardx 模块内),故依赖地板不变。
⑤ 若你的宿主依赖「不标变更=非变更=放行」:toolgate v0.17.0 起 Op.Mutating 三态化(*bool),nil=未知 ⇒ 审计门升人审——gatecheck/tooldisclose/ptc 不再替宿主猜变更标记,需显式声明。
⑥ egress.Verify 收严:六个代理变量「一个不缺、一个不歪」才绿、Bypass 判定对 NO_PROXY/no_proxy 取并集——此前靠缺省通过的配置会开始报违约(修复假绿,#62/#63)
v0.27.1(已发版,tag v0.27.1) 修复轮,无破坏性变更:ptc 的拒绝分类载体化(issue #53-#59)——分类随抛出对象一起走,收口处据此把「守卫判定拒绝」与「守卫自己崩了」分成两类,不再一律映射成 transient;另修 protocol 的报告校验、drain 的预算归一、pending 的列值解析。依赖地板不变 无需 go mod tidy,调用面逐字不变。若你的宿主按「拒绝一律可重试」处理 ptc 的拒绝,请改按 toolfail 的分类分流(Kind 随拒绝一起给出)——本版起「权限拒绝被当成瞬时错」这条误判已修正
v0.27.0(已发版,tag v0.27.0) 第一方依赖全量抬到最新 + 一处下游可见的解析口径变化:① 依赖地板抬升——oekit/concurrency v0.28.4→v0.31.0、oekit/pkg v0.37.4→v0.40.0、oekit/logx v0.8.10→v0.8.12、oagentkit/agent v0.14.3→v0.14.5、guardx v0.23.2→v0.23.4、runtime v0.12.2→v0.12.4、toolgate v0.15.2→v0.15.3、mcpkit v0.12.1→v0.12.4;② protocol 的解析通道边界随之扩宽——oekit/pkg v0.38 起 jsonrepair.Repair 的输出契约明写「: 后缺值补 null」(pkg#268),故形如 {"agent_name":"X","confidence":} 的非法 JSON 从「拒收」变成「被修复通道救回」;救回点落在本仓 markRepaired 的既有设计内(修复产物必降置信 low,下游 verify/needs_review 承接),本仓代码零改动、只把测试的不变量从「失败即拒收」改写为「凡经修复通道放行必为 low」,并补了成对的反向判据(缺冒号、无 JSON 结构仍拒收) ① go mod tidy——地板抬升对下游是隐式 breaking,不跑就报 updates to go.mod needed,且报错不提真正原因。
② 若你的宿主直接调 protocol.ParseReport / ParseLLMReport:缺值形态的模型输出现在不再返回 ErrNotReport,而是返回一份 Confidence="low" 的报告。这不是回归——上游修复通道的扩宽是刻意行为,且本仓既有设计(markRepaired 降置信)本就是为这类形态准备的。若你把「解析失败」当拒收信号用,请改判 Confidence == protocol.ConfidenceLow。(结构不可修复的形态——缺冒号、无 JSON 结构——仍照常返回 ErrNotReport,本仓补了成对的反向用例钉住这条边界。)
③ 上游 oekit/concurrency v0.28.2 起 async.PanicHandler 签名改为 func(ctx context.Context, r any, stack []byte)——本仓未定义该 handler(故编译未破),但你的宿主若自行实现了它,需要改签名。这是本轮唯一一处真正传导到下游的 BREAKING,请务必核对。
v0.22.0(已发版,tag v0.22.0) 依赖地板抬升 + 三处零行为重构:guardx v0.21.0→v0.22.0、toolgate v0.13.0→v0.14.0、mcpkit v0.11.0→v0.12.0、logx v0.8.4→v0.8.6;本仓代码只做等价收口(archcheck 删掉两个同体异名的证据判定包装、protocol 的手写 nil 判空换 ptrx.Deref、taskboard 的循环二次拼接换 strings.Join),公开 API 零变更、零行为差异 只需 go mod tidy——地板抬升对下游是隐式 breaking:不跑就报 updates to go.mod needed,且报错不提真正原因。三处重构的调用面逐字不变。一处刻意不采用:mcpkit v0.12.0 新增的 DisclosureGate(模型面三态披露契约)本仓不接——它绑 mcpkit/mcp 的 Catalog,而那个包依赖 eino + mcp-go,引入等于拿一个三态判定换一整条 mcp SDK 依赖;且本仓 tooldisclose.Gate 的哨兵二分 + Retryable 自报 + Activated 副作用仍要留(toolfail 靠 Retryable 分流,不解析文案)
v0.21.0(已发版,tag v0.21.0;本行由 v0.22.0 轮补录,该版当时未补行) trajectory 的 OTel 映射长成契约表:新增 AttrSpecs / AttrSpecOf / ForbiddenLabels 与 TraceGranularity / Domain / Cardinality——每个属性有主、只有有界枚举才许当 label、父子关系落 Span.ParentID。无 API 删除、无依赖地板变化 无需 go mod tidy、无需改调用面。补录依据是 git log v0.20.1..v0.21.0(一个 feat + 两个 docs 提交);若你依赖 trajectory 的 label 面,请自行对照 AttrSpecs 自查——本次补录未逐条核对这版对 label 的收窄
v0.20.1(已发版,tag v0.20.1) egress.Spec.Allow 的语义修正(安全方向):Allow 是「允许经代理访问的目标」,此前被写进 NO_PROXY——那等于让允许名单里的目标绕过代理直连:代理的拒绝名单 / 端口白名单 / 只读档对它全部失效,而声明在报告里仍显示「已兑现」。现 Allow 不再产生 NO_PROXY;新增 Spec.Bypass []string 表达「不经代理直连」,Verify 增判绕过名单(语义等价比较:顺序与空白漂移不算违约,判字面就是假红)。另:egress 补齐端到端验收用例——真强制代理 + 真请求(GAP 台账 P0-1 遗留的那一条)。 ① 若你把 Spec.Allow 当「直连豁免」用(即依赖它产生的 NO_PROXY):改用 Spec.Bypass。名字与语义都变了,但这一版是把危险的默认关掉——旧行为让「允许」变成「豁免」。
② 若你把 Allow 当「允许目标」用(正确用法):变得更严格,不破坏调用面——Allow 现在只是交给兑现者的声明(代理按自己的名单判),env 里不再出现 NO_PROXY。
③ Verify 收紧了:Bypass 非空而 NO_PROXY 缺失或不一致即判违约。
④ 不需要 go mod tidy——本版无依赖地板变化。
v0.20.0(已发版,tag v0.20.0) 出口声明的兑现面收口(行为收紧):① egress.Mode / egress.Enforcer 的真相上移到上游 guardx/sandbox(EgressMode / EgressEnforcer),本仓这两个名字改为 type alias(编译期同一类型,调用面逐字兼容)——词表留在本仓时,上游任何执行后端要声明「我能兑现」都得 import 本仓 ⇒ 成环 ⇒ 那条接口在结构上只可能由宿主实现,且编译期一声不响;② ptc.SubprocessOptions 新增 EgressBackend egress.Enforcer,spawn 之前先问后端「你能强制这一档吗」,问不到就不起子进程;③ archcheck 的 egress_paths 判据补上第二半证据(proxy 档除 env 生成外还须看得见能力问询 SatisfiedBy),policy 新增可选字段 capability。另:guardx v0.19.0→v0.21.0、oekit/pkg v0.36.0→v0.37.1 ① 若你用了 ptc.SubprocessOptions.Egress 且档位不是 unrestricted:必须同时填 EgressBackend(一个实现 sandbox.EgressEnforcer 的对象,例如宿主起的强制代理;上游 guardx/egressproxy 就是现成实现,它的 Egress() 只声称 proxy 档)。不填 ⇒ 现在拒绝执行(v0.18.0 时会照常起子进程、只生成 env)——这是修复不是回归:环境变量只是建议,「生成了 env」当成「已兑现」正是这条链此前空转的原因。
② go mod tidy——地板抬升对下游是隐式 breaking,不跑就报 updates to go.mod needed,且报错不提真正原因。
③ 若你在自己仓里复用 archcheck:egress_paths 的 proxy 档现在要求两半证据(env 生成 + 能力问询),只做前一半的启动点会红。这条是刻意的(旧判据对「只生成 env」静默放行);确需过渡就在该条登记的 capability 字段指明你们的能力问询方法名。
④ 本仓 sandbox 门面新增 EgressMode / EgressEnforcer / 三个档位常量别名——宿主实现 Provider 时可在门面里拿到这两个名字,不必直接 import 上游。
⑤ 上游 guardx v0.20.0 收紧了 egress.Check / CheckWith / CheckResolving 的判定面:新增 scheme 白名单(只认 http / https),file: / gopher: / ftp: / ws(s): / 空 scheme(相对 URL)一律拒绝;Policy.NewClient 同时补上 DialTLS 这条此前被绕过的拨号路径。若你(或你的宿主)把这两个判定用于非 http(s) 出站,升级后从「通过」变成 BlockedError——这是修复不是回归(判定面本来就只该覆盖 LLM 可控的 fetch 类 URL);按 host 判定的 CheckHost / CheckHostWith 没有 scheme 概念,行为不变。
v0.18.0(已发版,tag v0.18.0) 纯新增:十个机制包 + 两条新判定面——cmdpolicy(命令级判据)、egress(出口策略)、preset(权限预设)、sessionref(跨会话引用)、taskboard + boardtest(协调面契约与并发判据)、skilldisclose、hookengine、memorypipe、delegate、worktree;ptc.SubprocessOptions 新增 Egress egress.Spec;archcheck 新增两条 go/ast 判定面(require_rule_examples 规则样例自检、egress_paths 子进程出口登记)。详见 docs/GAP-ANALYSIS-20261106.md 的第六节 无需改动——全部是新增 API 与新增 policy 字段,不写新字段即按旧行为生效。三点可选留意:
① ptc.SubprocessOptions.Egress 零值 = 不限制,与旧版逐字一致;一旦你开始用 ModeProxy,子进程会拿到注入的 HTTP(S)_PROXY 等变量(不会自动发生,必须显式声明)
② 若你在自己仓里复用 archcheck:可把 egress_paths 段抄过去(判定执行体是同一份可导入包),把「spawn 点必须登记 + proxy 必须看得见兑现证据」这条判据一起带走
③ preset 的方向是 preset → gatecheck,别反向接(低层判据认识高层组装件会立刻成环)
v0.17.0 三处破坏性收窄 + 依赖地板抬升:① drain.Budget 返回命名类型 DrainBudget(Drain / DrainSession / DetachCtx 只收它),于是 context.WithTimeout(ctx, drain.Budget(x)) 编译不过——那正是「优雅停机静默变硬切」的唯一入口;收尾预算一律 drain.ShutdownBudget(返回普通 time.Duration)。drain 包同时把 Drain / DrainSession 两份收敛循环合并成一份。② 删 netguard.CheckURL / netguard.CheckChain(双 bool 包装),放行面唯一形态是 Allow{...} + CheckURLWith / CheckChainWith。③ 删 pii.Redact / pii.RedactValue(面无关门面),唯一入口是 pii.Apply / ApplyValue(必须显式指定面)。另:ptc 三处预算归一收进 ptc/limits.go、三份快照式订阅表收编到 internal/subhub(均为内部实现,无 API 变化)。依赖:oekit / oagentkit 家族抬到本轮最新(逐项见 CHANGELOG.md 的 v0.17.0 段);根目录 tools.go 版本钉子删除 ⇒ oagentkit/{llm,obs,textx} 不再是本仓依赖 ① go mod tidy——地板抬升对下游是隐式 breaking,不跑就报 updates to go.mod needed,且报错不提真正原因。
② drain 接线改两个入口:Drain / DrainSession / DetachCtx 的实参包一层 drain.Budget(cfg.X);喂 context.WithTimeout 的那一个数改走 drain.ShutdownBudget(cfg.X)。机械改法、语义不变(Budget 对正值与负值都原样,只有 0 补缺省),但别把 Budget 的负值归一——<0 是「关闭排水」的配置语义,归一会把「少做」静默翻成「多做 60 秒」
③ netguard 改调用形态:CheckURL(u) → CheckURLWith(u, Allow{...})、CheckChain(c) → CheckChainWith(c, Allow{...})——两个 bool 的位置不再需要记(写反不报错,只是静默改变放行面)
④ pii 改调用形态:Redact / RedactValue → Apply / ApplyValue 并显式传面;面无关门面拿不到 blocked 信号,策略配成 block 时它只会静默打码
⑤ 若你的 go.mod 靠本仓间接拉起过 oagentkit/{llm,obs,textx} 的版本地板,请自己在 go.mod 里显式钉——本仓不再声明它们
v0.14.0 依赖地板抬高(oekit/pkg v0.28.0→v0.30.0、oekit/logx v0.7.1→v0.8.0、oekit/concurrency v0.22.0→v0.23.0、oekit/core v0.12.0→v0.12.1、oagentkit/guardx v0.15.0→v0.17.0、oagentkit/llm v0.17.1→v0.18.1、oagentkit/textx v0.6.2→v0.7.1、oagentkit/mcpkit v0.8.6→v0.9.0);本仓自身代码零改动 ① go mod tidy——地板抬高对下游是隐式 breaking,不跑就报 updates to go.mod needed,且报错不提真正原因。
② 若你直接 import 过 github.com/oagentkit/guardx/audit 或 github.com/oagentkit/guardx/bashguard:这两个包自 guardx v0.17.0 起不再属于 guardx 模块**,要改 import 路径**——audit → github.com/oagentkit/obs/audit;bashguard → github.com/oagentkit/tools/bashguard。本仓自身零命中这两个路径(用的是新路径 obs/audit),但你的宿主代码若直接依赖过,需跟着换这一行 import。
其余全是等价改写与纯新增,无公开 API 变更:oekit/pkg v0.30.0 补 convert.AnyToStrOr / OptStr / OptBool / OptInt32 / OptInt64 / AnyToInt32 六个 nil 安全入口(AnyToStr(nil) 此前返回 字符串 "<nil>" 这种长得像值的哨兵),并修 cache.HashKey 的参数边界歧义(("a","b") 与 ("a:b") 曾算出同一个 key ⇒ 缓存串数据);oekit/logx v0.8.0 补 Nop() 静默 logger 与 LevelOff 档;oekit/concurrency v0.23.0 修 budget.Ledger 的 int64 溢出绕过(一次 n ≈ MaxInt64 的消耗曾让该维度永久变成「无限制」)与 singleflight.DoChan 的清理漂移(Forget 后同 key 曾并发跑两次 fn),并补 Ledger.Used 观测口;oagentkit/mcpkit v0.9.0 补 Pool.Tool(server, name) 与 ToolsResult.ByServer(纯新增,Tools 的扁平列表保留)
v0.13.0 spill 路径逃逸修复(fail-closed 收口):会话标识与定位符的判定改委托 oekit/pkg/pathx.IsSafeFileName——旧实现用 filepath.Base 判等,而 filepath.Base("..") == "..",于是 sessionID=".." 能让落点指到 spill 根目录的父目录(三重守卫两道一起失效,实测可读出根外文件);检索条数钳制改用 oekit/pkg/pagination.Policy 口径(等价);schema 编译缓存收编 oekit/pkg/cache(内部实现,契约不变);依赖地板抬高(oekit/pkg v0.22.0→v0.28.0、oekit/concurrency v0.20.1→v0.22.0、oagentkit/agent v0.10.6→v0.11.0、guardx v0.14.0→v0.15.0、llm v0.16.0→v0.17.1、obs v0.9.6→v0.9.7、toolgate v0.12.4→v0.12.5、mcpkit v0.8.5→v0.8.6) ① spill 的判定口径收紧了:会话标识/定位符含路径形态(. / .. / 含 / \ / 含 Windows 盘符相对路径)此前多数静默通过,现在一律显式拒绝(返回 ErrBadName / 「会话标识非法」)。若你传的是用户可控的会话标识,非法形态从「悄悄读到别的目录」变成「拿到错误」——这是修复,不是回归。
② go mod tidy——地板抬高对下游是隐式破坏性变更,不跑就报 updates to go.mod needed,且报错不提真正原因。
③ 两处上游 BREAKING 会传导到你:oekit/concurrency v0.21.0 起 ratelimit.Limiter 接口启用了 Wait(ctx context.Context) error——自行实现该接口的宿主需补上这个方法(旧实现不再满足接口);oekit/pkg v0.26.0 起 convert.ResolveClientIP 去掉了 xri 入参(确需采信改用 ResolveClientIPWithXRI)。本仓自身两处都没直接用到,但你的宿主代码可能用了。
其余全是等价改写,无公开 API 变更:pagination.Policy 口径与旧 clampLimit 逐条等价(n≤0→默认、超限→钳到上限、区间内原样);schema 缓存与单边界钳位均为内部实现替换
v0.10.0 guardset.Assembly 对齐 netguard 的「整份原子交换」:换份走 CAS + mutate(旧值)(新增 SwapFunc);新增代次(Assembly.Generation() / Snapshot() / Outcome.Generation)与换份订阅(Subscribe/Unsubscribe);修两处静默失效:① 并发换分丢更新⇒「撤销被撤销」(被撤掉的守卫悄悄复活);② 通知期回调 panic 把已落地的换份说成失败 若你按 v0.9.0 的示例用 Swap(next) 做读改写,请改用 SwapFunc(ctx, mutate)——Swap 的语义是「整份替换为这一份」,不基于当前那一份;两个并发撤销者会互相覆盖,被撤掉的守卫悄悄复活且没有任何提示。SwapFunc 基于当前份做读改写(CAS 循环),并发下不丢更新。其余全是新增 API,无接口变更(SwapRecord/Outcome 只新增字段);Set 仍只读,guardset.Stage(set) 仍可用
v0.9.0 新增 gatecheck 包(审批层 × 沙箱层的联合判据:两层都同意才放行,含 ExecuteAndRun 与受管后端 Managed——判据与执行不可分);新增 guardset.Assembly(守卫集合的装配面:撤销 = 换整份,链不用重接);policy 门面新增 Stage(审批门接进判定环的唯一入口);archcheck 新增 import_exempt(import 路径级精确例外,可放行被禁依赖树里的某个具体子包);ptc 修复「子进程不回结果时父侧永久挂死」(上游 guardx/wire 的 Starter.Start 收了 ctx 却不用它,本仓改为取 ctx deadline 与墙钟预算较早者)+ 内存兜底改用 cgroup v2(原 RLIMIT_AS 会把 Go runtime 一起掐死)+ 执行异常终止的死因分类;依赖地板追平(oagentkit/agent v0.10.2→v0.10.4、llm v0.15.0→v0.15.2、obs v0.9.4→v0.9.5、toolgate v0.12.1→v0.12.4、mcpkit v0.8.4→v0.8.5) go mod tidy——地板抬高对下游是隐式破坏性变更,不跑就报 updates to go.mod needed,且报错不提真正原因。其余全是新增 API,无接口变更:不接 gatecheck / guardset.Assembly / policy.Stage / archcheck 的 import_exempt 的宿主代码逐字不变(policy 不写新字段即按旧行为生效)。一处行为变化需知:ptc 现在尊重 ctx deadline——此前父侧可能在子进程不回结果时永久挂死,现在到点收刀并报错;若你依赖「父侧一直等」请显式给足 deadline
v0.8.4 依赖地板追平矩阵本轮新版本(oagentkit/agent v0.10.1→v0.10.2、obs v0.9.3→v0.9.4、runtime v0.7.1→v0.7.2、textx v0.6.1→v0.6.2、toolgate v0.12.0→v0.12.1、mcpkit v0.8.3→v0.8.4);无代码变更 go mod tidy——地板抬高对下游是隐式破坏性变更:不跑就报 updates to go.mod needed,且报错不提真正原因。无行为差异:这 6 个上游版本全部是纯依赖地板追平(各仓自身代码零改动,只抬 oekit/pkg / oekit/concurrency / guardx / llm 四类地板之一),逐仓核对见 CHANGELOG.md 的 v0.8.4 段
v0.8.3 依赖地板追平上游本轮新版本(oagentkit/llm v0.14.2→v0.15.0、oekit/concurrency v0.18.0→v0.19.0);无代码变更 go mod tidy——地板抬高对下游是隐式 breaking:不跑就报 updates to go.mod needed,且报错不提真正原因。无行为差异:llm v0.15.0 只新增了一个可选字段 OpenAIProviderConfig.HTTPClient(缺省 nil 时行为逐字不变),本仓只用 llm 的空导入(tools.go 的工具依赖登记),不碰其 API。顺带值得知道:llm v0.15.0 起宿主可注入自己的 *http.Client → 配合本仓 netguard.NewClient 把出站守卫挂到 Transport.DialContext(写面校验拦不住 DNS rebinding,守卫必须在拨号层)——接线形态见 llm v0.15.0
v0.8.2 依赖地板追平上游本轮新版本(oekit/pkg v0.18.0→v0.20.0、oagentkit/guardx v0.13.0→v0.13.1);无代码变更 go mod tidy——地板抬高对下游是隐式 breaking:不跑就报 updates to go.mod needed,且报错不提真正原因。两处抬升均无行为差异(guardx v0.13.1 只改 go.mod/go.sum;oekit/pkg v0.20.0 相对 v0.18.0 是纯增量 + 一处退避溢出修复),逐项核对见 CHANGELOG.md 的 v0.8.2 段
v0.8.0 sideeffect.Store 新增 Renew 方法(续期 + 校验持有资格),并新增 sideeffect.FencedSink 契约(下游拒收陈旧写入);新增 retryplan 包(重试处置决策) 接口层面的破坏性变更:宿主若已实现 sideeffect.Store,需补上 Renew(ctx, act, lease, ttl) (Lease, error) 方法——语义见包头注释(续期必须同时校验令牌仍是最新代,否则是「假续期」)。宿主若已实现下游栅栏拒收,可改用 FencedSink 契约对齐比较口径与原子性要求。FencedSink 与 retryplan 是新增的,不影响既有代码
v0.7.0 toolfail 与 sandbox 下沉上游 oagentkit/guardx(v0.9.0),本仓转门面(类型别名 + 常量别名 + 单条上游转发);依赖地板抬到上游本轮新版本(guardx v0.8.2→v0.9.0、llm v0.14.1→v0.14.2、agent v0.10.0→v0.10.1) 调用面逐字兼容,宿主代码不用改——类型别名是编译期同一类型,宿主现有的 errors.As / errors.Is 判定链继续有效。但必须 go mod tidy:地板抬高对下游是隐式 breaking,不跑就报 updates to go.mod needed,且报错不提真正原因。下游若直接 import 过 github.com/yi-nology/goagent-kernel/toolfail 或 .../sandbox 的具体实现(如自造 *Failure、注册自定义 Provider),请改 import 上游路径 github.com/oagentkit/guardx/toolfail / .../sandbox——门面只保转发,不提供实现。逐版差异见 CHANGELOG.md 的 v0.7.0 段
v0.6.5 依赖地板追平上游本轮新版本(oekit/pkg v0.17.0→v0.18.0、oagentkit/obs v0.9.2→v0.9.3、oagentkit/mcpkit v0.8.2→v0.8.3);archcheck 的 god object 判据改比「实质方法数」(方法总数 − 转发壳数),policy 新增可选字段 forwarding_shell_max_chars go mod tidy——地板抬高对下游是隐式 breaking:不跑就报 updates to go.mod needed,且报错不提真正原因。判据变更对下游无感:policy 不写新字段即按缺省 140 生效;若宿主要保持旧语义(方法总数),在自己 policy 里显式写 forwarding_shell_max_chars: 0 即可逐字退回旧行为。逐模块版本差异与判据推导见 CHANGELOG.md 的 v0.6.5 段
v0.6.4 纯文档:新增 docs/AI-GUIDE.md(面向 AI 代理的操作手册),并校准 README「工程底座」表的三处漂移 无(无 API 变更、无地板抬高)
v0.6.3 依赖地板追平上游本轮新版本(llm / obs / mcpkit / concurrency / core 共 5 个模块) go mod tidy——地板抬高对下游是隐式 breaking:不跑就报 updates to go.mod needed,且报错不提真正原因。逐模块版本差异与「是否影响本仓」已逐个核对,写在 CHANGELOG.md 的 v0.6.3 段
v0.6.2 纯文档:发版纪律补上「发 GitHub Release」这一步,并补齐本表此前缺的 v0.6.1 与 v0.3.0–v0.3.2 两行 无(纯文档)。本仓发布页此前 v0.4.0–v0.6.1 四版是空的,已按各 tag 注解补发
v0.6.1 修 archcheck 的静默假红(import 规则不认 negative_follow 豁免);补 CHANGELOG.md 与发版纪律 若你的 policy 在 import 规则上写过 negative_follow:此前它一条都没生效,现在生效了,构建可能从红变绿;若写过 forbid_call_regex / allow_paths:那两字段一直被 YAML 静默丢弃,自校验现在会点名
v0.6.0 netguard 的放行面改为具名类型(Allow),校验层与拨号层共用同一个类型;新增接线件 NewClient / NewTransport;依赖地板抬到上游最新一轮(7 个模块) netguard.DialOptions.AllowPrivate / AllowLoopback 两字段合并为 Allow(宿主若已接线,改字段名)。其余为新增 API,旧签名保留但已废弃。地板抬高对下游是隐式 breaking:go mod tidy 不跑会报 updates to go.mod needed,且报错不提真正原因——迁移说明见 CHANGELOG.md
v0.5.0 上游把判定环原语 invoke 从 oagentkit/toolgate 迁到 oagentkit/guardx(框架无关的原语不该被 eino 重仓的地板拖住),本仓跟随;依赖地板抬到上游最新一轮 import 路径换成 github.com/oagentkit/guardx/invoke;require 升到 toolgate v0.12.0 + guardx v0.8.0。编译期报错(类型身份变了),不是静默行为变化
v0.3.0 – v0.3.2 依赖地板连续抬到上游当时最新(oagentkit 八模块 + oekit 四模块) go mod tidy——地板抬高对下游是隐式 breaking:不改代码就编不过,而报错只说 updates to go.mod needed,不提真正原因。逐版抬了哪些地板见 CHANGELOG.md 与对应 tag 的 release 说明
v0.4.0 门禁执行体从 package main 下沉为可导入包 archcheck/ 无(新增包,不改既有 API)

受影响的本仓 API(签名里带 invoke 类型):

guardset.Guard          // Check func(ctx, c invoke.Call) invoke.Verdict
guardset.Set.Evaluate   // (ctx, c invoke.Call) Outcome
guardset.Stage          // invoke.Stage
tooldisclose.Stage      // invoke.Stage
ptc.Options.Stages      // []invoke.Stage

依赖面变轻了:判定环从 eino 重仓挪到了零依赖底座。下游若自己 import 过 toolgate/invoke,那条依赖曾把 eino 拖进下游的模块图,现在没有了。

宿主接入

go get github.com/yi-nology/goagent-kernel@latest
require github.com/yi-nology/goagent-kernel v0.29.0   // 零 replace,直接版本化引入

升级前用 git ls-remote --tags origin 核候选版本真的存在—— go list -m -versions 报的是 proxy 的缓存目录,不是远端现实。 当前已发布 tag 以远端为准(写稿时为 v0.29.0)。

⚠️ require 里写的是「最低版本」,不是「锁定版本」:下游 go mod tidy 只保证不低于这个版本,不会自动升到最新版。要用新版本必须显式 go get ...@<新版本>——这也是为什么这里写当前版本号而不是 v0.0.0。

import "github.com/yi-nology/goagent-kernel/dispatch"

// 派发守卫:宿主提供拓扑,harness 提供机制
type myEdgeSource struct{ reg *myRegistry }

func (s myEdgeSource) Slugs() []string                   { return s.reg.Slugs() }
func (s myEdgeSource) DispatchAllow(slug string) []string { return s.reg.Allow(slug) }

g := dispatch.NewGuard(myEdgeSource{reg: reg}, maxDepth) // 矩阵含环会在构造期 panic
if err := g.Assert(caller, callee, depth); err != nil { /* 结构化 *dispatch.DenyError */ }

⚠️ 上面只是九组能力之一的片段。完整接入路线图见 docs/USAGE.md: 契约面(contract + statemachine)、工具判定环(tooldisclose + guardset + toolfail)、报告协议(protocol)、续跑契约(checkpoint + commitgate)、 上下文三件套(budget + spill + offload)、实时事件、流式与退出、 安全面(netguard + pii)、程序化编排(ptc)、门面三件。

宿主侧的安全不变量(如「全拓扑只有一条边能到达执行器」)的断言测试应留在宿主仓—— 那是产品级承诺,不是内核机制属性。dispatch/guard_test.go 用合成拓扑测机制, 可复用于任何宿主。

演进纪律

  • 新增能力前先问:这是机制还是资产? 资产进宿主/领域包,机制才进本仓。
  • 新依赖方向先改 policy 再写代码;例外带注释写明拆除条件。
  • 改了包或门禁,同步本 README 的包表:新增/删除包、改外部依赖、改生产行数跨过 25% 容差——三者任一发生就要改表,make docsync 会拦。
  • 内核包规模远小于宿主,max_file_lines: 400 直接阻断(宿主仓是报告口径)。 2026-10-18 起 thresholds.enforce 由 false 切 true:此前 3 处超长文件 (protocol/report_types.go 660、protocol/report_test.go 782、 cmd/archcheck/main.go 490)已拆分归零,2026-10-19 起执行体整体下沉为 archcheck 包,切换条件才成立。拆分只为满足 机械阈值,单文件读不动时该模块应重新分组,而不是继续往上加。
  • 拆自宿主仓的代码历史见 docs/SPLIT_PLAN.md——那是 2026-10-17 写于 10 个包时的分层依据快照,其中「L2/L3 待拆」一节描述的是当时的待办, 不是当前包清单(当前清单见本文「包清单」表)。

Directories

Path Synopsis
Package archcheck 是 architecture-policy.yaml 的**可执行面**。
Package archcheck 是 architecture-policy.yaml 的**可执行面**。
Package budget 上下文预算:输入侧的**分段计量**、窗口上限计算与压实计划骨架。
Package budget 上下文预算:输入侧的**分段计量**、窗口上限计算与压实计划骨架。
Package checkpoint 编排续跑契约:编排进度快照的**存取边界**与**恢复判定机制**。
Package checkpoint 编排续跑契约:编排进度快照的**存取边界**与**恢复判定机制**。
cmd
apilifecheck command
Command apilifecheck 执行 api-lifecycle.yaml 的可执行面。
Command apilifecheck 执行 api-lifecycle.yaml 的可执行面。
archcheck command
docsync command
baseline.go —— README「工程底座」表的机械判定(issue #31)。
baseline.go —— README「工程底座」表的机械判定(issue #31)。
pkgcov command
judge.go —— 事实 + 阈值 + 豁免 → 报告(判据的另一半)。
judge.go —— 事实 + 阈值 + 豁免 → 报告(判据的另一半)。
ptcchild command
Command ptcchild 是「子进程形态」的小程序,供跨环境验证用。
Command ptcchild 是「子进程形态」的小程序,供跨环境验证用。
Package cmdpolicy 命令级判据(机制面)。
Package cmdpolicy 命令级判据(机制面)。
Package commitgate 环节终态的独占提交闸:同一环节的终态只允许被提交一次。
Package commitgate 环节终态的独占提交闸:同一环节的终态只允许被提交一次。
Package contract 编排运行契约:编排引擎与宿主状态之间的**唯一边界**。
Package contract 编排运行契约:编排引擎与宿主状态之间的**唯一边界**。
Package delegate 异构委托的**契约面**:请求能不能交出去、交出去的结果算不算完成。
Package delegate 异构委托的**契约面**:请求能不能交出去、交出去的结果算不算完成。
Package delta 增量节流器(真流式观测面)。
Package delta 增量节流器(真流式观测面)。
Package dispatch 实现「权限即拓扑」:agent 间可达性由 allow 矩阵物理决定,
Package dispatch 实现「权限即拓扑」:agent 间可达性由 allow 矩阵物理决定,
Package drain 优雅排水:计划内重启的零孤儿收刀。
Package drain 优雅排水:计划内重启的零孤儿收刀。
Package egress 出口策略的**声明面与兑现判据**(机制面)。
Package egress 出口策略的**声明面与兑现判据**(机制面)。
Package eventbus 事件单源双写(PHASE1_PLAN §2 决策 7):
Package eventbus 事件单源双写(PHASE1_PLAN §2 决策 7):
examples
pgstore
board.go —— taskboard.Board 的 PostgreSQL 参考实现。
board.go —— taskboard.Board 的 PostgreSQL 参考实现。
pgstore/cmd/pgsmoke command
Command pgsmoke 参考部署的**冒烟入口**:一个宿主进程连上 PostgreSQL, 把副作用闸 + 栅栏下游 + 任务认领的关键判据各走一遍,全部通过即退出 0。
Command pgsmoke 参考部署的**冒烟入口**:一个宿主进程连上 PostgreSQL, 把副作用闸 + 栅栏下游 + 任务认领的关键判据各走一遍,全部通过即退出 0。
reference-runtime command
board_store.go —— taskboard.Board 的参考实现(内存版)。
board_store.go —— taskboard.Board 的参考实现(内存版)。
Package gatecheck 审批层与沙箱层的**联合判据**。
Package gatecheck 审批层与沙箱层的**联合判据**。
Package guardset 守卫装配契约:一组**相互独立**的判定被一并求值,拒绝被合成一条。
Package guardset 守卫装配契约:一组**相互独立**的判定被一并求值,拒绝被合成一条。
Package hookengine Hooks 的**判定面**:什么时候该跑、跑了算不算拒绝。
Package hookengine Hooks 的**判定面**:什么时候该跑、跑了算不算拒绝。
internal
subhub
Package subhub 是「快照式订阅表」的**唯一实现**(本仓内部件,不进公开 API)。
Package subhub 是「快照式订阅表」的**唯一实现**(本仓内部件,不进公开 API)。
Package memorypipe 记忆的**注入-引用-打标**三段(读写时机归宿主)。
Package memorypipe 记忆的**注入-引用-打标**三段(读写时机归宿主)。
Package netguard 出站 URL 守卫(SSRF 防护):凡是「服务端代发 HTTP」的出口都统一
Package netguard 出站 URL 守卫(SSRF 防护):凡是「服务端代发 HTTP」的出口都统一
媒体类型 → 落盘扩展名的**单源**推导。
媒体类型 → 落盘扩展名的**单源**推导。
Package pending 反问/消歧挂起态的结构化载体。
Package pending 反问/消歧挂起态的结构化载体。
Package pii PII 价值模式检测与打码(批次二百七十八,对标 octop-harness
Package pii PII 价值模式检测与打码(批次二百七十八,对标 octop-harness
Package policy 操作审计门薄转发:核心已沉淀 oagentkit/toolgate/policy(含
Package policy 操作审计门薄转发:核心已沉淀 oagentkit/toolgate/policy(含
Package preset 命名权限预设:把「隔离档位 × 审批模式 × 出口策略」三件散参数
Package preset 命名权限预设:把「隔离档位 × 审批模式 × 出口策略」三件散参数
Package protocol 统一报告协议:Report 契约 + 五级容错解析 + 白名单校验 + Markdown 渲染。
Package protocol 统一报告协议:Report 契约 + 五级容错解析 + 白名单校验 + Markdown 渲染。
诊断面:出错位置与面向模型的修复建议。
诊断面:出错位置与面向模型的修复建议。
Package reminder 系统注入提示的**防伪包裹通道**(门面)。
Package reminder 系统注入提示的**防伪包裹通道**(门面)。
Package retryplan 重试处置决策:把「该不该重试」与「重试会不会重复施加」
Package retryplan 重试处置决策:把「该不该重试」与「重试会不会重复施加」
Package sandbox 执行沙箱的档位语义与后端协议(门面)。
Package sandbox 执行沙箱的档位语义与后端协议(门面)。
Package sessionref 跨会话引用快照:把**另一个会话的结论**以**有界、带来源、
Package sessionref 跨会话引用快照:把**另一个会话的结论**以**有界、带来源、
Package sideeffect 副作用「至多一次」的跨进程契约。
Package sideeffect 副作用「至多一次」的跨进程契约。
Package sidetest 副作用闸(sideeffect.Store)的**共享契约测试套件**。
Package sidetest 副作用闸(sideeffect.Store)的**共享契约测试套件**。
Package skilldisclose 技能披露:**确定性**挑出该给模型看的那几个技能。
Package skilldisclose 技能披露:**确定性**挑出该给模型看的那几个技能。
Package spill 溢出存档平面单源(批三百零一):budgetTool 截断落盘(engine/runner)/
Package spill 溢出存档平面单源(批三百零一):budgetTool 截断落盘(engine/runner)/
Package statemachine 环节状态机(PHASE1_PLAN §6.2):合法转移表硬编码 + 全覆盖单测。
Package statemachine 环节状态机(PHASE1_PLAN §6.2):合法转移表硬编码 + 全覆盖单测。
contention.go —— 认领的并发正确性**判据**(判据,不是测试风格)。
contention.go —— 认领的并发正确性**判据**(判据,不是测试风格)。
boardtest
Package boardtest @@Board@@ 的**共享契约测试套件**。
Package boardtest @@Board@@ 的**共享契约测试套件**。
Package ticket 一次性短时授权票据(机制面)。
Package ticket 一次性短时授权票据(机制面)。
Package tooldisclose 渐进式工具披露 + 执行门禁。
Package tooldisclose 渐进式工具披露 + 执行门禁。
Package toolfail 工具失败的错误分类契约(门面)。
Package toolfail 工具失败的错误分类契约(门面)。
Package toolname 工具名 → 程序内可用的标识符:一条**单一真相**。
Package toolname 工具名 → 程序内可用的标识符:一条**单一真相**。
otelmap.go —— 轨迹行 → OTel 属性的**契约表**(属性名 / 来源字段 / 取值域 / 基数上界)。
otelmap.go —— 轨迹行 → OTel 属性的**契约表**(属性名 / 来源字段 / 取值域 / 基数上界)。
Package watchdog 被动配对不变量监视(spec 2026-09-15-tool-identity-otel-guard-design P3):
Package watchdog 被动配对不变量监视(spec 2026-09-15-tool-identity-otel-guard-design P3):
Package worktree 工作区隔离的**声明面与冲突判据**(不碰真实文件系统)。
Package worktree 工作区隔离的**声明面与冲突判据**(不碰真实文件系统)。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL