pstack 架构拆解
H08 / 十层 · 油门和刹车

17 行角色表,
外面只包着一个文件

先纠正一个直觉:上游 pstack 的 50 个技能里没有 Models 这一节。 默认模型是手写进各技能正文的——interrogate 里是一张三行表,feature.md 里是一个括号。 外置的东西只有一个:~/.cursor/rules/pstack-models.mdc,17 行,由 /setup-pstack 生成。 有这一行就整行覆盖,没这一行就落回技能里的默认。

图 8-1那张表的原样

下面是 setup-pstack/SKILL.md 第 5 步(L39–66)给出的文件形状,逐字照抄。三行注释 + 17 行角色。青 = grok-4.7-xhigh-fast,蓝 = claude-opus-5-5-max,橙 = gpt-5.6-sol-max。全站只用到这三个 slug。

~/.cursor/rules/pstack-models.mdc --- description: pstack per-role model choices   (overrides skill defaults) alwaysApply: true --- # pstack model configuration. One line per role. # Delete a line to fall back to the skill default. # budget: unlimited (max) feature, refactoring: grok-4.7-xhigh-fast bug-fix: grok-4.7-xhigh-fast perf-issue: grok-4.7-xhigh-fast hillclimb: grok-4.7-xhigh-fast judgment and prose: claude-opus-5-5-max hardest tasks: claude-opus-5-5-max how explorer: grok-4.7-xhigh-fast how explainer: claude-opus-5-5-max why investigators: grok-4.7-xhigh-fast why synthesizer: claude-opus-5-5-max reflect tooling: gpt-5.6-sol-max reflect judgment, divergent, synthesizer: claude-opus-5-5-max …面板四行见右侧 面板 · 值是列表 arena runners: opus, gpt, grok architect runners: opus, gpt, grok interrogate reviewers: opus, gpt, grok arena cross-judge pool: opus, gpt, grok 前三行:一个条目 = 一个子代理 列表长度就是并发数 第四行是池:只从中挑 1 个 挑的规则:家族与父级不同 默认值 · 单值 swarm workers: grok-4.7-xhigh-fast 它是每个 worker 的默认, race 可以按臂另派模型 (17 行的最后一行) 两个别名,不是型号 inherit-parent  ·  auto 两者同义:这次调用省略 Task 的 model 参数,跟父会话走
图 8-1 · 原文形状取自 pstack/skills/setup-pstack/SKILL.md L41–66 的代码块(提交 e43c7ee)。alwaysApply: true 是关键:它是 Cursor 的 rules 机制,让这 17 行每次会话都在上下文里,不用谁去"想起来读"。

图 8-2一次 spawn 的模型是怎么定下来的

这条链没有任何一处是"猜"。每一步的原文判据都在下面。

1 · 技能内默认 表就写在该技能 正文里 interrogate: Reviewer A = opus-5-5-max B = gpt-5.6-sol-max C = grok-4.7-xhigh-fast feature.md:一个括号 2 · 覆盖行 读 .mdc 里同名角色行 有 → 整行替换 无 → 落回第 1 步 "Delete a line to fall back to the skill default." 退役角色(how critics)丢掉 3 · 别名短路 值是 inherit-parent 或 auto → 省略 Task 的 model 面板里的别名条目 仍然计入并发数 这是 Auto 用户的留法 4 · 被 Task 工具拒绝时的兜底 家族只看前缀:claude-* gpt-* grok-* ① 换该家族默认的对应 seat,并说明 ② 无家族匹配 → 落到 Reviewer A 的默认 ③ 默认也被拒 → 从报错里挑最接近的 slug (优先同家族最高推理档) 并且:另开一个 PR 去更新默认表 绝不因为 slug 问题卡住这次评审 第 4 步是整个配置层最有信息量的一段:它承认默认值会过期,并且把"过期"当成一条待办而不是事故。
图 8-2 · 第 4 步原文在 interrogate/SKILL.md L49;arena/SKILL.md L28 有一份等价的。setup-pstack 第 4 步还有一条硬校验:写进表的真实 slug 必须在本次会话探测到的集合里,否则停下重问——"Never write a real slug you have not confirmed is available."

图 8-3预算档:一行配置改写 17 行

/setup-pstack 只问你一个问题——愿意花多少推理预算——然后机械地重写所有角色行。这是这张表唯一的批量开关。

四档预算 · setup-pstack 要求用这四个标签原样提问 unlimited — keep max不动任何一行,表保持默认 large — xhigh reasoning每个 slug 的 effort → xhigh medium — high reasoning每个 slug 的 effort → high small — medium reasoning每个 slug 的 effort → medium 改写算法:找到 effort 那个 token,换成目标档 阶梯(从高到低) max  >  xhigh  >  high  >  medium  >  low effort token 的位置:slug 的最后一个 token;如果结尾是 fast,则是它前一个 token。 面板里的每个条目一起改,别名 inherit-parent / auto 不参与改写。 原文给的两个例子(small 档) claude-opus-5-5-max → claude-opus-5-5-medium grok-4.7-xhigh-fast → grok-4.7-medium-fast
图 8-3 · 取自 setup-pstack/SKILL.md L22–29。如果改完的 slug 不在探测集合里,规则是"取同家族里不高于目标档的最高 effort",再不行就把该角色标成"需要你选"。

8-4这一层的三笔账

账 1
默认值没有单一真源
默认散写在 50 份正文里,没有一个 models.json 生成它们(上游确实没有这个文件)。所以 slug 一过期,就会同时散在多处——这才逼出了图 8-2 第 4 步那条兜底链。
账 2
表只对自己有效
它是你这台机器上的 Cursor 的属性:Detect the model slugs you can pass to a Task subagent in this session. 换账号、换 entitlement,表就重新生成一次。
账 3
规则一旦写歪就全局生效
alwaysApply: true 意味着这 17 行每次会话都进上下文。写错一个角色名不报错,只是永远匹配不上——它靠"整文件覆盖写"保证重跑幂等。
端口版不同步:社区移植版 michael-denyer/pstack-claude 把这张表换成了 pstack-models.md,按 runtime 各存一份(~/.claude、Codex home、~/.pi/agent、 ~/.config/opencode、~/.gemini),角色名换成了 opus / fable / sonnet / haiku 四个家族而不是具体 slug,另加 default effort 和 session hook 两行开关。 上游没有这些:它只认 Cursor 的 .mdc rules,只写具体 slug,也从不跨 runtime 复制这张表。
本层的检验问题:为什么"面板列表的长度 = 并发子代理数"这条要专门写进文档? 因为扇出是唯一会成倍花钱的地方。把规模做成一行配置,就能在不改流程的前提下调成本。 下一层 H09 讲这套东西绑死了 Cursor 的哪些接口。