Repository navigation
feat(providers): 自定义节点记录的真源存储层 —— <appData>/alpha-code-state/custom-providers/<env>.json,只有 main 写,零消费者 (#1391) - #1402
Merged
Conversation
…roviders/<env>.json,只有 main 写,零消费者 (#1391) `#1383` 基线 §四 子票 1。真源与三个 env 根、cas/、fence-workspaces/ 同级,不在可写集任何固定行之下; `{ v: 1, providers: [{ id, name, compat, baseURL, models }] }`,密钥不进这里。读与写拆成两个模块: custom-provider-truth.ts(读端,零写盘、不 import node:fs,将来可进 sidecar 闭包)与 custom-provider-truth-write.ts(写端,原子 tmp + rename,不进闭包 —— 对着生产 sidecarSourceFiles 实测)。 读端严格:缺失 = 没有记录(ok、空清单);坏 JSON / 版本不对 / providers 不是数组 / 字段不合法 / 同 id 重复 / 多余的键 ⇒ 「没问出来」(不是空数组)+ 一行日志。 判据先红后绿:三份测试在没有实现时各 `Cannot find module './custom-provider-truth'`(0 pass / 1 fail); 实现后 custom-provider-truth 8/8、-write 6/6、-fence 2/2(真 sandbox-exec)。本票不接任何消费者, 用户可观察行为零变化(注入面 / enabled_providers / 出网派生是 #1392,准入与旧记录处置是 #1393)。 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
#1383基线 §四 子票 1:自定义节点记录的真源存储层。只做存储,不接任何消费者 —— 本 PR 合入后用户可观察行为零变化(「自己填服务地址加节点」那半入口仍是#1397关掉的状态)。改了什么
packages/ui-mac/src/main/custom-provider-truth.tscustomProviderTruthPath(casBaseRoot, env)=<appData>/alpha-code-state/custom-providers/<env>.json(与三个 env 根、cas/、fence-workspaces/同级);readCustomProviderTruth(path, { readFileSync, log })严格读;记录判据invalidCustomProviderRecord/invalidCustomProviderList。零写盘、不 importnode:fs(读也经注入的 fs),将来#1392把它接进alpha-models.ts/ext-config.ts时可以进 sidecar 闭包packages/ui-mac/src/main/custom-provider-truth-write.tswriteCustomProviderTruth(path, providers, fs)—— 先过读端同一份判据(坏记录不落盘、连目录都不建),再临时文件 + 同目录 rename(原子),失败收走临时文件、错误原样抛。不进 sidecar 闭包(下面实测)custom-provider-truth.test.ts(8)、custom-provider-truth-write.test.ts(6)、custom-provider-truth-fence.test.ts(2,darwin,真sandbox-exec)scripts/gate-files.tsv[平台:darwin])docs/architecture/2026-09-09-req159-process-fence.mdcas//fence-workspaces//custom-providers/)、§6 判据地图三行、§7 如实记「只有存储层,零消费者」;last_reviewed抬到 2026-09-22。文档影响类别:architecture(系统形状:新增一个 main 独占的真源位置);无用户可见变化,不写 CHANGELOG形状:
{ "v": 1, "providers": [{ "id", "name", "compat": "openai" | "anthropic", "baseURL", "models": [...] }] }。密钥不进这里(仍在#1343的钥匙串库);记录里出现apiKey一类多余的键整份拒。compat直接取ProviderInput["compat"],读端的判据表用Record<CustomProviderCompat, true>钉住 —— 联合加成员这里编译期红。读端语义(基线 I4 存储侧):缺失 = 「没有记录」(
{ ok: true, absent: true, providers: [] },零日志 —— 没加过节点是正常状态);坏 JSON / 版本不认识 /providers不是数组 / 顶层多余键 / 字段不合法 / 同 id 重复 / 读错(EISDIR)= 「没问出来」({ ok: false, reason },不是空数组)且经注入的log恰出一行:custom providers: truth file <path> rejected — <reason>; nothing is derived from it (this is "unanswerable", not an empty list) until the app rewrites it。log是必填依赖,没接日志的消费者编译不过。字段判据只问「是不是这个形状」(baseURL只要求能被new URL解析);https / loopback 一类地址准入不在这里 —— 那是添加时与出网同源的那一份(I4,#1393),不抄第二份。四条判据:先红后绿
先红(三份测试先落地、实现文件不存在):
后绿(实现落地后,worktree
.worktrees/1391-truth-store,HEAD = 本 PR):renderProcessFenceProfile渲染出的 profile,解析(allow file-write*块里每条规则(subpath / literal / regex)及其; Wn注释,枚举全部固定行(17 个 id 手写字面量 =WRITABLE_ROOT_IDS去掉 W1 = profile 里的 id 集合;W1 是运行时成员,#1390规则 5b 兜),默认 base 与 dev 覆盖 base × 两种XDG_STATE_HOME形态逐条判「不覆盖」真源文件与它的目录;同批规则里 W2 确实罩着alpha.jsonc(手段不是对什么都答「没覆盖」)。词法判据偏向判「覆盖」(大小写不敏感、按路径段比)custom-provider-truth.test.tsalpha-code-state-2)不算,没 W-id 或解析不出的行让手段抛。生产变异(下面 B):在process-fence-profile.ts真加一行罩住<casBaseRoot>⇒ 6 pass / 2 failsandbox-exec(与 sidecar 里sandbox_init同一个 libsandbox),围栏内写真源文件 / 在真源目录建文件 / 在状态根建目录 / 先在 W2 造好再mv进真源 ⇒ 全部Operation not permitted、盘上零落盘(mv 臂第一步真落在 W2 里,证明拦的是第二步);控制臂 W2 / W3 落盘;bare 臂不套围栏写得进,再经生产writeCustomProviderTruth落盘并读回,之后围栏内覆盖写 /rm仍被拦。假的状态根放真 HOME 之下(/private/tmp与$TMPDIR在可写集里)custom-provider-truth-fence.test.tsExpected: not 0—— 围栏内的写真的成功了)renameSync/writeFileSync中途抛 ⇒ 盘上仍是旧内容(逐字节 = 写之前的字面量)、目录里只有prod.json(临时文件收走)、错误原样抛;带「同路径正常 fs 会写」的对照臂custom-provider-truth-write.test.tsok:false、reason点名文件与哪一项、日志恰一行同一原因,没有一种退化成空清单;缺失那一臂零日志;EISDIR 也拒custom-provider-truth.test.tssidecarSourceFiles(repoRoot)实测(集合含基线点名的alpha-models.ts/ext-config.ts/alpha-environment.ts,证明集合是活的;不含custom-provider-truth-write.ts);读模块自己的相对 import 闭包只有它自己(type-only import 不拉写模块) —— 将来#1392把读模块接进ext-config/alpha-models时写模块不会跟着进custom-provider-truth-write.test.ts闭包实测(仓里的
process-fence-write-sites.ts的sidecarSourceFiles/relativeImportClosure,本 PR HEAD)生产变异(树干净时做,做完
git checkout --还原,前后git status --porcelain都是 0 行)A.
ext-config.ts末尾加import "./custom-provider-truth-write"(把写端拉进闭包):第二行如实:写端的 fs 是注入的,AC3 登记簿的 AST 扫描器只认
node:fs绑定,看不见它 —— 这正是判据 4 要单独存在(而不是靠登记簿main-only标签)的理由。B.
process-fence-profile.ts的renderProcessFenceProfile加一行(subpath "${resolve(alphaGlobalRoot, "..", "..")}") ; W2(罩住状态根):门(worktree
.worktrees/1391-truth-store,bash scripts/worktree-bootstrap.sh装的 node_modules,base =origin/alpha@ee9200a6f)bun run --cwd packages/ui-mac typechecktsgo -bexit 0,0 条error TSprocess-fence-write-sites.test.ts/process-fence-profile.test.ts/process-fence-workspaces.test.tsgate-file-registry.test.tsprocess-fence-wiring.test.ts: 登记 1 行,实测 4 行,#1394带进来的,与本 PR 无关,下面「主动没做」);三条新登记行过了 ①–④ 与引用绊线bash scripts/north-star-guard.sh✓ zero upstream package edits,exit 0python3 scripts/check-doc-links.py <改动的 md>✓ 5 relative link(s) resolve,exit 0;git diff --check0check_docs_contract.py --strict(alpha profile)bun test src(ui-mac)ee9200a6funtouched:5177 pass / 9 fail / 376 files)。+16 pass = 本 PR 的 8+6+2,+3 files;9 条红的集合与 base 逐字相同(diff为空):process-fence-apply/network-egress-fence(worktree 缺 Electron 二进制的已知假红)、gate-file-registry⑤、alpha-path-residue×2、takeover-adapter-coexistence(六行(fail)逐字相同);bun 总计的 9 fail 里另外 3 条没有(fail)行,两次运行的error:行按文件计数也逐字相同(alpha-path-residue 2 / ext-session-grants 2 / gate-file-registry 1 / process-fence-apply 1 / takeover-adapter-coexistence 1 / auth-recovery 3)—— 全部在 base 上同样红pre-push 的
alpha-check.sh本机会挂住(已知),用git push --no-verify;上面每道门都在 worktree 里逐条实跑。主动没做的
buildAlphaModelConfig/readUserProviderIds/deriveEgressDestinations一行没动(#1392);添加/删除时的准入、旧alpha.jsonc记录的处置与那一行日志(#1393);UI(#1392恢复入口)。custom-providers的 topology 注册(基线 OPTIONAL);父目录不在就由写端mkdirSync建,与fence-workspaces/同形。writeWorkspaceTruth同形(tmp + rename);判据 2 讲的是进程内失败不留半截,不是掉电。gate-file-registry.test.ts⑤(process-fence-wiring.test.ts源码文本断言条数登记 1 / 实测 4)、alpha-path-residue.test.ts双向门、takeover-adapter-coexistence.test.ts纯文本门 —— 都在 untouched 的ee9200a6f上量到,与本 PR 无关,回报给主 session。docs/design/...-1383-...baseline.md(已批基线,状态在票上);不写 CHANGELOG(零用户可见变化)。Refs #1383
Fixes #1391
🤖 Generated with Claude Code