gbrain 引擎动态导入调和设计:engine 路径静态导入强约束与 `engine-dynamic-import-ok` 守护机制的落地实践

发布时间:2026/9/20 23:32:09
gbrain 引擎动态导入调和设计:engine 路径静态导入强约束与 `engine-dynamic-import-ok` 守护机制的落地实践
gbrain 引擎动态导入调和设计engine 路径静态导入强约束与engine-dynamic-import-ok守护机制的落地实践【免费下载链接】gbrainGarrys Opinionated OpenClaw/Hermes Agent Brain项目地址: https://gitcode.com/gh_mirrors/gb/gbrain导读本文围绕 gbrainGarrys Opinionated OpenClaw/Hermes Agent Brain的一份内部设计文档展开讲解该项目如何将pglite-engine、postgres-engine、migrate三条引擎热路径上的动态导入await import()系统性地收敛为静态顶层导入同时为必须保持惰性加载的 AI gateway 模块保留显式的行级豁免标记engine-dynamic-import-ok并配套实现一个可脚本化、可测试的仓库级守护检查guard。读完本文你将理解引擎路径动态导入为何被视为需要治理的硬化不变量hardening invariant、调和过程如何在两个历史分支与当前 trunk 之间取舍以及 gbrain 如何用check-engine-dynamic-import.sh TypeScript AST 检查 回归测试把这一约束固化进 CI。背景三处重叠的引擎动态导入变更设计文档记录于 2026-07-28其源头是两条互不兼容的历史特性分支claude/hungry-edison-8bb1cd对应的发布提交为48ada48f与248bfe55claude/elegant-gates-e5275e对应提交为ef4cf7a8该提交本身携带一版动态导入守护脚本。两条分支都涉及把引擎路径上的动态导入改回静态导入这一主题但实现互有出入且都不是当前 trunkorigin/master的祖先。当时的 trunk 位于6136e139972a5449630b4f47f5ed7b4cbe5b811b版本0.42.67.0而上游 PR #3511 仍处于打开状态因此 trunk 中并不包含该 PR 对chronicle/ontology.ts的两个导入提升hoist。文档明确排除了用git log -S追踪此类改动的做法因为动态转静态替换中相关 token如import(可能始终存在、只是上下文变化必须用git log -G按行内容变化追踪 ontology、helper、migration、gateway 各自独立的历史。调查时刻 trunk 上三个引擎文件中共有17 处动态导入分为两类13 个可安全提升safe-hoist的候选两个 ontology 导入、九个引擎辅助/审计模块导入、两个 migration 导入4 个必须保留惰性的ai/gateway.ts导入全部位于try/catch回退路径内部。调和策略直接在当前 trunk 上重建目标状态设计文档选定的方案不是把任何一条旧分支整体 merge 或 cherry-pick而是以当前origin/master为基准直接在干净分支上重建本应存在的当前状态选择性复刻期望的源码变更只搬 13 个安全提升 4 个 gateway 豁免点把守护脚本适配到当前 trunk 的四个 gateway 调用点而不是ef4cf7a8时代只认识两个豁免点的旧版本撰写面向当前状态的文档。这样做的动机很明确避免引入旧发布元数据stale release metadata、旧 TODO 声明以及其他与主题无关的分支历史变更。值得注意的是ef4cf7a8的守护脚本在 trunk 上会失败——不仅因为两个_upsertChunksOnce的 gateway 查询是 trunk 后加的还因为旧脚本只认识两个 gateway 豁免点。源码变更13 个静态导入提升 4 个受保护惰性点安全静态导入的落点13 个安全候选全部提升为顶层静态导入分布在三个文件中以下路径均可在当前仓库直接核对src/core/pglite-engine.ts自chronicle/ontology.ts提升valueHash、normalizeDimension、isNovelDimension经现有retry.ts导入复用isRetryableConnError连同withRetry、BULK_RETRY_OPTS等既有导出自search/recency-decay.ts提升resolveRecencyDecayMap、DEFAULT_FALLBACK。src/core/postgres-engine.ts同样的 ontology、retry、recency 助手自retry-matcher.ts提升isConnectionEndedError自audit/db-disconnect-audit.ts提升logDbDisconnect自audit/pool-recovery-audit.ts提升logPoolRecovery。src/core/migrate.ts自retry-matcher.ts提升isStatementTimeoutError、isRetryableConnError自timeline-dedup-repair.ts提升repairTimelineDedupIndex。以当前源码为准这些提升确实已经落地。例如 pglite-engine.ts 顶部 同时静态导入了retry.ts的isRetryableConnError与chronicle/ontology.ts的三个维度函数并在注释中明确写道Engine-path imports stay static unless a call site carries an explicitengine-dynamic-import-okjustification.引擎路径导入保持静态除非调用点携带显式豁免理由。postgres-engine.ts 则同时静态导入retry.ts、retry-matcher.ts、chronicle/ontology.ts、audit/db-disconnect-audit.ts、audit/pool-recovery-audit.ts。migrate.ts 的模块注释说明runMigrations在引擎存活期间执行因此其辅助模块必须留在静态依赖图中而不是在异步处理器里再导入。文档还强调两个引擎在共享行为上必须保持对等parity注释应描述当前不变量而不要重复这些提升修复了 Windows 测试运行器崩溃这一未经证实的因果断言——这是整个设计中反复出现的措辞纪律。刻意保持惰性的 gateway 导入四个await import(./ai/gateway.ts)调用点被明确要求继续惰性加载PGLiteinitSchemaPGLite_upsertChunksOncePostgresinitSchemaPostgres_upsertChunksOnce每一行都带有显式的engine-dynamic-import-ok标记与就近的理由注释。理由分两部分启动成本gateway 的静态闭包含有 AI SDK、provider 包以及校验/配置机制若急切加载会让不需要它的引擎启动路径背上额外负担软回退语义更关键每次查询都位于try/catch内catch 保留软回退编译期默认值或大脑存储的 embedding 模型配置。若把该模块提升为顶层静态导入模块会在 catch 有机会执行之前就被求值原本可恢复的配置/导入失败会退化为模块加载期的硬失败。当前源码完全印证了这一设计。以 pglite-engine.ts_upsertChunksOnce中的 gateway 查询为例let dims: number DEFAULT_EMBEDDING_DIMENSIONS; let model: string DEFAULT_EMBEDDING_MODEL; try { // Keep the gateway lazy: its static closure is large, and evaluation inside // this try/catch preserves the unconfigured-gateway default fallback. const gw await import(./ai/gateway.ts); // engine-dynamic-import-ok // Both accessors THROW when the gateway is unconfigured (they never // return falsy), so the catch below is the only fallback path (#3461). dims gw.getEmbeddingDimensions(); model gw.getEmbeddingModel(); } catch { /* gateway not configured — use defaults */ }postgres-engine.ts的 initSchema 对应点 结构完全一致先给dims/model赋ai/defaults.ts中的规范默认值再在try内惰性加载 gateway 并调用两个会抛错的访问器catch兜底回退。四个调用点pglite-engine 两处、postgres-engine 两处均以此模式存在守护脚本不允许出现未标记的 gateway 导入也不允许文件级大范围豁免。守护脚本check-engine-dynamic-import.sh的实现原理设计文档要求的守护脚本已落地为两个文件shell 入口 scripts/check-engine-dynamic-import.sh 与真正的检查逻辑 scripts/check-engine-dynamic-import.ts。Shell 入口的默认扫描集无参数运行时脚本默认扫描三条引擎热路径FILES( src/core/pglite-engine.ts src/core/postgres-engine.ts src/core/migrate.ts )更关键的是脚本还动态发现从门面类剥离出去的引擎方法模块目录for d in src/core/pglite-engine src/core/postgres-engine; do if [ -d $d ]; then while IFS read -r f; do FILES($f); done (find $d -name *.ts | sort) fi done注释说明了原因这些从门面类剥离的方法模块同样是引擎热路径模块抽取绝不能缩小守护覆盖范围。脚本同时支持显式传文件参数bash scripts/check-engine-dynamic-import.sh FILE [FILE...]便于测试或定向扫描最终通过exec bun $SCRIPT_DIR/check-engine-dynamic-import.ts ${FILES[]}交给 TypeScript 检查器执行。TypeScript AST 检查器的工作方式check-engine-dynamic-import.ts的核心逻辑值得展开标记识别用MARKER engine-dynamic-import-ok配合 Unicode token 字符类[\p{ID_Continue}$-]确认标记是独立成词的并通过ts.getTokenAtPosition判断标记不在某个标识符 token 内部避免误认随后把标记所在行号收集进markerLines集合。两种惰性加载形式都要抓检查器同时匹配import(...)调用表达式node.expression.kind ts.SyntaxKind.ImportKeyword与require(...)调用。源码注释披露了一个真实教训W0 ship-review catch: match BOTH lazy-loading forms. The guard previously matched onlyimport(...)call expressions, so arequire(...)on an engine-live path passed silently and its engine-dynamic-import-ok marker was decorative.——旧版只匹配import()导致引擎路径上的require()静默通过、豁免标记形同虚设本次实现专门补齐了这一点。注释行豁免由于用ts.createSourceFile做真实解析再遍历 AST 节点而非简单 grep注释文本内的await import(不会产生误报行级豁免只认markerLines中的真实标记行。违规上报任何未标记的import(/require(都会以文件:行号:源码行的形式输出最后统一打印提示Prefer a static top-level import. If lazy loading is load-bearing, append engine-dynamic-import-ok to that exact line and document the startup or soft-failure boundary that requires it.优先静态顶层导入若惰性加载是功能依赖请在该行附加标记并说明所需的启动或软失败边界。并exit 1。CRLF 防御读取源文件后按split(/\r?\n/)分行行级匹配不受 CRLF 换行符影响Windows 检出下的 CRLF 行尾无法绕过检查。脚本头部的注释同样贯彻了文档的措辞纪律——Historical Windows runs associated imports on these paths with abrupt Bun test-process exits, but system-wide commit exhaustion remained a confound——即历史 Windows 测试进程崩溃与导入有关联但系统级提交耗尽仍是混杂因素因此守护强制执行的是引擎路径硬化不变量而不是断言每个动态导入都必然导致 Windows 崩溃。接线到包脚本与验证流程package.json中已存在对应接线见 package.jsoncheck:engine-dynamic-importbash scripts/check-engine-dynamic-import.shverifybash scripts/run-verify-parallel.sh守护被要求接入check:all与 scripts/run-verify-parallel.sh并遵循 trunk 现行规则包脚本通过bash调用仓库 shell 脚本而非直接执行。回归测试用临时 fixture 覆盖守护行为设计文档要求自动化测试覆盖六类场景全部在 test/scripts/check-engine-dynamic-import.test.ts 中落地共 356 行真实动态导入 → 退出码 1 且被报告fixture 中写await import(./helper.ts)断言result.code 1、stderr 含violator.ts:2:与原始源码行携带engine-dynamic-import-ok的行被允许同一行两个非 gateway 导入共享一个行级标记也能通过行注释与块注释内的文本不产生发现// await import(...)、块注释体内的文本均被忽略且 rejects live code after a closed leading block comment 反向验证了注释关闭后的真实代码仍会被抓CRLF 输入同样能抓住违规通过跨平台BASH解析与\r?\n分行保证默认仓库扫描在调和后通过测试用expectedDefaultScanCount()动态推导默认扫描集——3 个门面文件加上src/core/pglite-engine、src/core/postgres-engine两个目录下的全部.ts文件数——并强调该计数派生自真实仓库而非硬编码新增引擎模块不会破坏测试跨多文件时报告每个违规。测试基建的关键约束与文档一致使用mkdtempSync临时目录构造 fixture绝不改动被跟踪的源码文件断言保持路径可移植用basename而非绝对路径拼接同时验证run-verify-parallel.sh在仓库中的存在性。文档还要求pre-fix 红方演示是旧版ef4cf7a8守护在 trunk 上运行、以退出码 1 报告既有未标记导入post-fix 守护与测试必须全部通过——这一先红后绿的证据链被完整保留在验证说明中。文档策略与发布边界调和不止改代码还包含严格的文档与 Git 纪律文档政策面向当前状态而非旧发布叙事不修改VERSION、不新增发布级CHANGELOG.md条目不复制旧版本标题或已完成的发布 TODO 块不保留抽取 gateway 访问器必然修复问题的旧 TODO——惰性导入是刻意由本地软失败边界保护的将跨切面的无未标记动态导入不变量补充进 CLAUDE.md按需更新 docs/architecture/KEY_FILES.md 中三个引擎文件pglite-engine.ts、postgres-engine.ts、migrate.ts的当前状态条目文档编辑后重新生成 llms.txt 与 llms-full.txt仅当实现过程中发现真实未决行动时才新增 TODO。验证清单完整输出落盘后再查看摘要至少执行守护回归测试bash scripts/check-engine-dynamic-import.sh覆盖引擎、迁移、retry、audit、recency 模块的定向测试bun run typecheckbun run verifybun run build:llms后接bun test test/build-llms.test.tsgit diff --check及最终的干净状态/diff 复查。文档明确要求若平台资源争用或既有 Windows 套件缺陷阻碍了全量测试必须报告精确命令、退出码与责任分类ownership classification不得以部分运行为由宣称成功。Git 与发布边界在claude/kind-meitner-330c90上工作本地重置到精确调查过的origin/master基线上将先前 worktree 顶端保留在claude/kind-meitner-330c90-pre-reconcile实现与验证提交保持本地未经用户明确批准不 push、不开 PR、不在上游评论或以其他方式发布。设计要点复盘从这份调和设计可以提炼出几条可复用的工程原则用目标状态重建替代历史搬运当两条旧分支各有合理部分但都偏离 trunk 时直接在干净基线上选择性复刻期望变更避免吞入过时元数据与无关历史把惰性变成显式豁免而非默认许可engine-dynamic-import-ok行级标记 就近理由注释让每个惰性导入都可审计、可解释杜绝文件级大范围豁免软回退边界优先于尽量静态静态导入是默认但 gateway 之所以被豁免恰恰因为它的模块求值必须发生在try/catch软回退之后——静态化若破坏可恢复性就不是改进守护必须与真实语法对齐用 TypeScript AST 而非文本匹配同时捕获import()与require()两种形式注释文本零误报CRLF 无法绕过证据与措辞纪律把历史 Windows 崩溃表述为引擎路径硬化不变量明确承认提交耗尽等混杂因素避免把相关性写成因果性——这条纪律贯穿设计文档、shell 注释、公共文档与最终代码注释。相关资源设计文档docs/superpowers/specs/2026-07-28-engine-dynamic-import-reconciliation-design.md守护 shell 入口scripts/check-engine-dynamic-import.sh守护 TypeScript 检查器scripts/check-engine-dynamic-import.ts回归测试test/scripts/check-engine-dynamic-import.test.ts被守护的引擎热路径src/core/pglite-engine.ts、src/core/postgres-engine.ts、src/core/migrate.ts包脚本接线package.json、scripts/run-verify-parallel.sh【免费下载链接】gbrainGarrys Opinionated OpenClaw/Hermes Agent Brain项目地址: https://gitcode.com/gh_mirrors/gb/gbrain创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考