CCDV-F 备考手册 · Claude Certified Developer – Foundations
Chapter 04 · Domain 2(本章 skill 权重合计 4.1%)

配置管理
CLAUDE.md、settings.json、model pinning、prompt versioning、plugin dependencies

blueprint 在这条 skill 里点了五样东西的名字,却一个行为定义都没给。它们表面上是五个不相干的文件,实际只回答两个问题:同一个设置写在好几处时,谁说了算;以及哪些东西必须钉死版本,钉死之后还剩多少不确定性。本章按这两个问题组织,核心交付是三张真伪速查栏——考场上区分「真键名」与「听起来很合理的键名」,靠的就是这种肌肉记忆。

读法提示 — 本章几乎每一句都是产品行为,因此逐条挂了 vb: 出处(登记表见 reference/verified-behaviors.md,含官方原句与查证日期)。三张速查栏里标为「真实」的每一行都有登记支撑;标为「不存在」的每一行都是本书按 ch01 的 D1 工艺自制的干扰项,没有官方出处,捏造只发生在「名字」这一层,不涉及会误导你的机制行为。

一、四个 settings 文件与那条优先级栈

Claude Code 从四个 settings 文件读配置,组织还可以从 claude.ai console 下发一份 managed settings。每个来源有自己的 scope——一个键写在哪个文件里,就决定了它作用于谁:只有你、这个项目的所有人,还是整个组织的所有人。

Scope文件影响谁
User~/.claude/settings.json这台机器上你的每一个项目
Shared project.claude/settings.json在该目录里启动 Claude Code 的所有人;提交进仓库即可分发给同事
Project local.claude/settings.local.json只有你,只在这一个项目里
Managedmanaged-settings.json 与其他 managed 来源组织下发到的所有人

同一个键出现在多处时,取「设置了它的最高层」的值。由高到低是五级:managed settings → command line(claude --settings)→ project local → shared project → user。注意 command line 排在第二而不是第一:--settings 传进去的键压得过本地文件,压不过 managed。

两条最容易在集成里踩的补充规则。第一,list 型的键是合并而不是择一。官方对 permissions.allow 这类键的说明是:同一个 list 键出现在多个文件里时,Claude Code 把这些 list 合起来,于是每个文件都能往里加条目而不会删掉别的文件加的。所以「我在 local 里加了一条 allow,团队那条怎么还在」不是 bug,是设计。

第二,环境变量根本不在这条栈里。官方把它讲得很清楚:环境变量不是这个栈里的一级,某个 shell 变量和某个 settings 键谁生效是逐对决定的,不按层级。具体到最常撞的一对——ANTHROPIC_MODEL 压过任何文件里的 model 键,而 ANTHROPIC_DEFAULT_MODEL 只在没有任何文件设置 model 时才生效。团队里「我明明在项目配置里写了模型,起来还是别的」,十次里有九次是某个人的 shell profile 里躺着一行 export。

速查栏 1 · settings 文件与键名

~/.claude/settings.json                    ✔ 真实 · user scope
.claude/settings.json                      ✔ 真实 · shared project,提交进仓库
.claude/settings.local.json                ✔ 真实 · project local,只在你这台机器
managed-settings.json                      ✔ 真实 · managed,组织下发
.claude/settings.priority.json             ✘ 不存在 · 自制干扰项
.claude/settings.override.json             ✘ 不存在 · 自制干扰项

settings.json · "model"                    ✔ 真实 · 改会话起始模型
settings.json · "env"                      ✔ 真实 · 每个会话及其子进程的环境变量
settings.json · "permissions"              ✔ 真实 · allow / ask / deny 与起始 permission mode
settings.json · "hooks"                    ✔ 真实 · 在生命周期点跑自定义命令
settings.json · "enabledPlugins"           ✔ 真实 · 按 scope 开关插件
settings.json · "extraKnownMarketplaces"   ✔ 真实 · 为仓库或组织登记 marketplace
settings.json · "claudeMdExcludes"         ✔ 真实 · 按路径或 glob 跳过指定 CLAUDE.md
settings.json · "$schema"                  ✔ 真实 · 指向已发布的 JSON schema,给编辑器补全与校验
settings.json · "modelPin"                 ✘ 不存在 · 自制干扰项
settings.json · "promptVersion"            ✘ 不存在 · 自制干扰项
settings.json · "settingsPriority"         ✘ 不存在 · 自制干扰项

还有一条工程上极常见、考场上也很好用的细节:settings 文件是严格 JSON。一个 // 注释或一个尾随逗号就是语法错误,Claude Code 会在下次启动时把该文件报成 Settings Error。所以「我加了配置但完全没生效,连报错都没有」,第一件事不是查优先级,是查这个文件还是不是合法 JSON。$schema 那一行的价值也在这里:它指向已发布的 JSON schema,编辑器能给补全与内联校验;但官方同时提醒 schema 可能落后于最新 CLI 版本,所以对一个刚进文档的新键报告警,不代表你的配置无效。

改完不用重启 — Claude Code 监听 settings 文件并在变更时重载,多数改动不用重启就作用于当前会话,permissions、hooks 与 apiKeyHelper 这类凭据 helper 都在内。出题时这条常被反用:题干说「改了配置、重启过、还是老样子」,那问题就不在「有没有生效」,而在「哪一层压着它」。

二、CLAUDE.md:加载顺序、@import,与「它不是闸门」

CLAUDE.md 可以放在好几个位置,各有各的 scope;官方文档的那张表按 load order 从最宽到最窄排列,于是 project 指令在 context 里出现在 user 指令之后。四个位置的路径要能默写:

速查栏 2 · CLAUDE.md 家族

/Library/Application Support/ClaudeCode/CLAUDE.md   ✔ 真实 · managed policy(macOS)
/etc/claude-code/CLAUDE.md                          ✔ 真实 · managed policy(Linux / WSL)
C:\Program Files\ClaudeCode\CLAUDE.md               ✔ 真实 · managed policy(Windows)
~/.claude/CLAUDE.md                                 ✔ 真实 · user,你的所有项目
./CLAUDE.md                                         ✔ 真实 · project,随版本控制分发
./.claude/CLAUDE.md                                 ✔ 真实 · project 的另一个合法位置
./CLAUDE.local.md                                   ✔ 真实 · local,进 .gitignore
./.claude/CLAUDE.priority.md                        ✘ 不存在 · 自制干扰项
./CLAUDE.override.md                                ✘ 不存在 · 自制干扰项

@docs/git-instructions.md                           ✔ 真实 · @path 导入语法,最大深度四跳
`@README`(反引号包住)                              ✔ 真实 · 代码 span 内的 @ 不触发导入
!include docs/git-instructions.md                   ✘ 不存在 · 自制干扰项
@docs/git-instructions.md --depth=6                 ✘ 不存在 · 自制干扰项

这些文件是拼接,不是覆盖。官方原话是:发现的所有文件是concatenated 进 context 而不是互相覆盖;跨目录树的顺序是从文件系统根往下排到你的工作目录,同一个目录内 CLAUDE.local.md 排在 CLAUDE.md 之后。所以「离启动点越近的指令,被读到得越晚」,而不是「越近的把远的顶掉」。这条决定了两个工程后果:其一,两份 CLAUDE.md 说了相反的话,不会有一份「赢」,两份都在 context 里;其二,monorepo 里父目录的规则会一路跟下来——要甩掉它得用 claudeMdExcludes 按路径或 glob 跳过,这个键可以配在任意一层且数组跨层合并。

子目录里的 CLAUDE.md 不在启动时加载。工作目录之上的 CLAUDE.md 与 CLAUDE.local.md 在启动时加载;工作目录之下的那些是按需的——Claude 在那个子目录里读、写或编辑另一个文件时才加载。这是 monorepo 场景最常见的困惑来源:在仓库根启动,然后奇怪为什么 packages/api/CLAUDE.md 的约定没从一开始就生效。答案不是文件写错了,是它还没被触发。

@path 导入有三条要记的性质。相对路径相对于含该导入的那个文件,不是工作目录;可以递归导入,最大深度四跳;被导入的文件在启动时就随引用它的 CLAUDE.md 一起展开进 context。最后这条推出一个反直觉的结论:拆成 import 有助于组织,但不省 context——官方明说 splitting into @path imports 不减少 context,因为被导入的文件在 launch 时就加载了。真要省,得靠把单文件压到 200 行以内这类做法,或者用 path-scoped rules 让指令只在 Claude 处理匹配文件时才进来。另外,导入解析会跳过 Markdown code span 与围栏代码块,想在文中提一个路径而不触发导入,用反引号包起来即可。

为什么 CLAUDE.md 拦不住事——本章最该记住的一句

官方对 CLAUDE.md 与 auto memory 的定位是同一句话:两者都在每次会话开始时加载,但 Claude 把它们当 context 而不是被强制的配置;要在不管 Claude 怎么判断的情况下阻断某个动作,该用 PreToolUse hook。文档另一处把这条分工做成了对照:managed settings 用于 technical enforcement,managed CLAUDE.md 用于 behavioral guidance;settings 规则由 client 强制执行、不看 Claude 怎么决定,而 CLAUDE.md 指令塑造行为、不是一个硬强制层。

诉求该配在哪为什么
阻断特定工具、命令或路径permissions.deny(managed settings)由 client 强制执行
代码风格与质量约定managed CLAUDE.md行为指导,塑造倾向
某个动作在每次编辑后都要跑hooks固定生命周期点触发
合规提醒与数据处理注意事项managed CLAUDE.md行为指导

这正是 ch01 P3「拦得住的才叫控制」在配置这一层的落点:把 NEVER delete production data 写进 CLAUDE.md,得到的是一个请求;把它写进 permissions.deny 或一个 PreToolUse hook,得到的才是一道闸门。考场上凡是「往 CLAUDE.md 里加一句更强硬的话」的选项,先按 D3 处理。

Claude Code 还有一条相关但不在 blueprint 措辞里的机制:/compact 之后,项目根的 CLAUDE.md 会被从磁盘重读并重新注入会话,而子目录里的嵌套 CLAUDE.md 与带 paths: frontmatter 的规则只是按需重新加载,不会随 compaction 立刻回来。这条对理解「压缩之后指令为什么像丢了」很有用,但 .claude/rules/ 与 auto memory 属于 TS 3.1 的 Claude Code 组件面(见 ch15),本章不展开、也不进题。

三、model version pinning:无日期的 ID 也是钉住的

这一节有一个几乎人人搞反的点,值得单独立一段。官方文档专门写了一句 A common misconception is that… 来澄清它。

每个 Claude model ID 都是一个 pinned snapshot。带日期的 ID(例如后缀 20250929)固定到那一次发布;而从 4.6 代起的无日期 ID 同样是 pinned snapshot,不是 evergreen 指针。用某个 model ID 发请求时,其底层模型在该 ID 的整个生命周期内保持不变;Anthropic 不更新既有 model ID 的权重或配置,有更新版本时以新的 model ID 发布。

那 alias 是什么?4.6 代之前的模型在 Claude API 上另有短 alias(例如 claude-sonnet-4-5),它是一个 convenience pointer,解析到该 minor 版本最近的 dated snapshot——这一类才是会浮动的。而 4.6 代的 claude-sonnet-4-6 不是 alias,它本身就是那个 snapshot。两者长得一样都不带日期,语义却相反,这是本章最容易被做成干扰项的地方。

写法形态会不会随时间指向别的权重
claude-sonnet-4-5-202509294.6 代之前的 dated ID不会——固定到那次发布(这个 ID 已于 2026-09-30 deprecated、2026-11-30 退役,这里只当命名示例;pinned 管的是权重不变,不管服役期限)
claude-sonnet-4-54.6 代之前的 alias会——指向该 minor 版本最近的 dated snapshot
claude-sonnet-4-6 / claude-opus-54.6 代及之后的 dateless ID不会——它就是 snapshot 本身

钉住了就万事大吉吗?不。官方给了一条边界,它比上面那条更值得记:权重对给定 ID 是固定的,但模型周边的 serving infrastructure——请求路由、安全分类器、采样逻辑——会随时间变化;偶尔,基础设施更新会在 model ID 与权重都没变的情况下产生可观察的细微行为差异。文档甚至给了排查提示:在一个此前稳定的 model ID 上注意到意外的行为差异,最可能的原因就是一次基础设施更新。「钉住版本」买到的是「权重不变」,不是「输出逐字不变」。这条正是 ch01 P8 的边界——版本轴与采样轴要分开,而这里还多了第三条轴。

版本钉住之后还要管生命周期。官方用四个状态描述模型生命周期:Active(完全支持、推荐使用)、Legacy(不再更新,未来可能被 deprecate)、Deprecated(仍可用但不再推荐,Anthropic 给出推荐替代与退役日期)、Retired(不再可用,Requests to retired models will fail)。每个 model ID,无论带不带日期,都有各自独立的 deprecation 与 retirement 时间表。通知与迁移的节奏也是写死的:对公开发布的模型,退役前至少提前 60 天通知;一旦 deprecated 就要在 retirement date 之前把用量迁完,过了那个日期的请求会失败;官方还建议提前充分测试替代模型,而不是等到最后一周。

顺带把另一条「版本」讲清楚,它和模型版本不是一回事:每次 API 请求都要带 anthropic-version 请求头(例如 anthropic-version: 2023-06-01),用官方 client SDK 时它被自动处理。在给定 API 版本内,Anthropic 保留既有输入参数与既有输出参数;但可能新增可选输入、新增输出值、改变特定错误类型的触发条件、给 enum 类输出值新增变体(例如 streaming 事件类型)。最后这一条是集成层的直接约束:你的 SSE 事件分派、你的 stop_reason 分支,都要按「将来会多出没见过的取值」来写,否则一次向后兼容的平台更新就能打翻你的 parser。这一点与 ch02 的流式事件序列、ch10 的 defensive parsing 是同一条线。

四、prompt versioning:产品机制在服务端,工程实践在你这边

blueprint 把 prompt versioning 和 model version pinning 并列写进同一条 skill。但这两样东西的「产品支持程度」差得很远,得分开讲。

走 Messages API 时,prompt 没有产品级的版本机制。请求体里没有任何一个字段承载「这是第几版 prompt」——system 字符串就是一串字节,服务端不替你记它的历史。所以在这条路径上,prompt versioning 是工程实践,不是产品机制:把 prompt 从散落在三个源文件里的字符串常量,变成有版本号的构件(独立文件、随代码一起走 code review、发布时带 tag、灰度时新旧并跑对照)。这套做法与 ch05 的版本控制与 SDLC 集成是同一套工具,本书不在此把它包装成 Claude 特性。

标注 — 上一段的做法不是产品机制,是通用工程实践,因此没有 vb: 出处。本书的纪律是:查不到官方行为就不写行为,但可以写「这里没有产品机制、于是落到工程实践」——后者是对锚点缺失的如实陈述,不是对行为的猜测。

走 Managed Agents 时,prompt versioning 是真的产品机制。官方对 agent 的定义是:一份 reusable, versioned configuration,打包 model、system prompt、tools、MCP servers 与 skills;创建一次,之后每次开 session 用 ID 引用它。也就是说 system prompt 不再住在你的代码里,而是住在服务端的一个带版本的资源里。

动作行为
更新 agent配置真的变了才生成新版本;version 从 1 开始,每次「改变了 agent 的更新」递增
更新但内容没变不创建新版本,直接返回现有版本(no-op detection)
请求里带 version乐观并发:与当前版本不匹配返回 409
请求里省略 version无条件应用,last write wins
List versions返回完整版本历史,可用来追踪一段时间内的变化

做题时的判据很干脆:题干问「怎么知道上周改了什么、怎么退回去」,答案在版本历史与乐观并发这一侧;题干问「两个人同时改把对方覆盖了」,答案是带上 version——省略它的语义就是 last write wins,两边都不会收到错误。这是 P8「版本钉住,变更当 breaking」在服务端配置上的形态。

五、plugin dependencies:把上游按住在测过的范围里

Claude Code 的 plugin 可以打包 skills、agents、hooks 与 MCP servers。一旦一个 plugin 依赖另一个 plugin,配置管理的问题就从「我这台机器」变成了「谁的发布会打翻谁」。

plugin manifest 是 .claude-plugin/plugin.json,定义插件的元数据与配置。有两件事常被记反:manifest 本身是可选的——省略时 Claude Code 按标准布局加载组件,插件名取自 marketplace 条目(用 --plugin-dir 加载时取目录名);而如果你写了 manifest,name 是唯一必填字段。目录布局也有一条硬规则:.claude-plugin/ 里只放 plugin.json,skills/、commands/、hooks/ 等其余插件文件都放在插件根目录,不能塞进 .claude-plugin/ 里面。

速查栏 3 · plugin 文件与依赖字段

.claude-plugin/plugin.json                 ✔ 真实 · plugin manifest,可选;写了则 name 必填
.claude-plugin/marketplace.json            ✔ 真实 · marketplace manifest
hooks/hooks.json                           ✔ 真实 · hook 配置的默认位置
.mcp.json                                  ✔ 真实 · MCP server 定义的默认位置
plugin.json · "dependencies"               ✔ 真实 · 数组,元素可为裸字符串或对象
plugin.json · dependencies[].name          ✔ 真实 · 依赖的插件名
plugin.json · dependencies[].version       ✔ 真实 · semver range,如 ~2.1.0 / ^2.0 / >=1.4 / =2.1.0
plugin.json · dependencies[].marketplace   ✔ 真实 · 换一个 marketplace 解析 name
marketplace.json · allowCrossMarketplaceDependenciesOn ✔ 真实 · 放行跨 marketplace 依赖
.claude-plugin/dependencies.json           ✘ 不存在 · 自制干扰项
plugin.json · "peerPlugins"                ✘ 不存在 · 自制干扰项
plugin.json · "pinVersion"                 ✘ 不存在 · 自制干扰项
plugin.lock                                ✘ 不存在 · 自制干扰项

依赖写在 plugin.json 的 dependencies 数组里,元素可以是只有插件名的裸字符串,也可以是带 name / version / marketplace 的对象;version 取 semver range,依赖会被取到满足该 range 的最高 tagged 版本。不写约束的代价是明说的:默认情况下依赖跟踪最新可用版本,上游一次发布就可能在无预警的情况下把你脚下的依赖换掉;版本约束的作用,就是把依赖按住在已测过的范围里,直到你决定移动。

解析与冲突的行为有四条要记:

启用与禁用也是连带的:启用一个插件会一并启用它已安装但被禁用的依赖(依赖没装则启用失败,并提示先装);反过来,若还有别的已启用插件需要某个依赖,禁用它会被拒绝,并打印一条按正确顺序禁用两者的串联命令。还有一个很实用的打包用法:manifest 只需要 name,所以一个只含 name 与 dependencies 数组的插件就是合法插件——安装它就把整组依赖拉进来,于是「后端标准插件集」可以做成一次安装。这正是 P4「能力放对载体」在插件层的形态:要跨人复用、要独立维护,就做成一个可依赖的单元,而不是让每个人往自己的 settings.json 里手抄一遍。

本章题组

QUESTION 01 · TS 2.6 · CLAUDE.MD IS NOT A GATE
A platform team ships a repository CLAUDE.md whose first line reads NEVER run `terraform apply` against the prod workspace. Over three weeks of Claude Code sessions the command still executed twice, both times during long refactors after the context had been compacted. The team's postmortem needs a control that holds regardless of what the model decides. What should they ship?
正确 B官方把这条分工写死了:CLAUDE.md 与 auto memory 都在会话开始时加载,但 Claude 把它们当 context 而不是被强制的配置;要在不管 Claude 怎么判断的情况下阻断动作,用 PreToolUse hook。另一处进一步说明 settings 规则由 client 强制执行,而 CLAUDE.md 不是硬强制层。题干要的正是「regardless of what the model decides」,只有这一项落在强制层。
D6 半对半错违反 P3A:前半句真——managed policy 的 CLAUDE.md 确实不可被个人 settings 排除。但「不可排除」管的是它一定会进 context,不是它一定会被执行。换了个位置的同一句话,仍然是行为指导而不是闸门。
D3 措辞冒充控制违反 P3C:全大写、放两遍、再抄进 local——三个动作都停留在措辞层。CLAUDE.local.md 在同目录内确实排在 CLAUDE.md 之后,但那是拼接顺序,官方没有把它描述成权重,更不构成强制。
D4 换模型当解法违反 P1D:题干的约束是「要一个与模型判断无关的控制」,换模型恰好把结果继续押在模型判断上。样题 Q2 的同型排除理由说得更狠:更听话的模型在这类问题上 can be more susceptible, not less。
QUESTION 02 · TS 2.6 · WHO WINS THE MODEL KEY
A repo commits .claude/settings.json with "model": "claude-sonnet-5". One engineer's sessions keep starting on a different model. Their ~/.claude/settings.json also sets model, their .claude/settings.local.json does not, no --settings or --model flag is passed, and their organization deploys no managed settings. Their shell profile contains export ANTHROPIC_MODEL=claude-opus-4-8. What explains it, and what is the fix?
正确 D官方明说环境变量不是这条优先级栈里的一级,某个 shell 变量与某个键谁生效是逐对决定的:ANTHROPIC_MODEL 压过任何文件里的 model 键,而 ANTHROPIC_DEFAULT_MODEL 只在没有文件设置 model 时生效。题干把四个文件层逐一排除干净了,剩下的唯一解释就是那行 export。
D1 凭空捏造违反 P5A:「最后读到的文件赢」是编出来的裁决规则。官方的规则是取设置了该键的最高层的值,由高到低为 managed → command line → project local → shared project → user;而且 project local 排在 shared project 之上,把键搬过去只会让这位工程师更偏离团队值。
D1 凭空捏造违反 P5B:$schema 是给编辑器做补全与内联校验用的,不是键生效的前置条件;官方甚至提醒它可能落后于最新 CLI 版本,对新键报告警也不代表配置无效。这里把一个真实字段安上了它没有的门禁职责。
D6 半对半错违反 P1C:「重启就好」在别的场景常常成立——Claude Code 确实监听 settings 文件并重载多数改动。但前半句给环境变量硬塞了一个栈内位置,而官方的说法恰恰是它不在栈里;重启一百次,那行 export 还在。
QUESTION 03 · TS 2.6 · WHERE NESTED CLAUDE.MD LOADS
A monorepo has ./CLAUDE.md (4-space indentation) and packages/api/CLAUDE.md (2-space, plus the service's error-format rules). Engineers start Claude Code at the repository root. Session transcripts show the API rules appearing only after Claude has opened a file under packages/api/; before that, generated code uses the root conventions. The team wants the API rules in effect from the first turn of a root-launched session. What is happening, and what fixes it?
正确 A官方把这条讲得很直接:工作目录之上的 CLAUDE.md 与 CLAUDE.local.md 在启动时加载,子目录里的不在启动时加载,而是 Claude 在那个子目录里读、写或编辑另一个文件时才加载。要在第一轮就生效,要么把启动点挪进去,要么用 @path 从根文件导入——被导入的文件在 launch 时随引用它的 CLAUDE.md 一起展开。
D1 凭空捏造违反 P5B:「override each other」正好说反了。官方原话是所有发现到的文件被拼接进 context 而不是互相覆盖,跨目录树从文件系统根排到工作目录。claudeMdExcludes 是真实存在的键,但它只能少加载一个文件,不能让另一个文件提前加载。
D6 半对半错违反 P5C:前半句真——同一目录内 CLAUDE.local.md 确实排在 CLAUDE.md 之后。后半句是私接的:改名不改变「在工作目录之下」这个事实,按需加载的判据是位置而不是文件名。
D4 换模型当解法违反 P1D:题干没有任何一处指向容量——没有截断、没有 Settings Error、没有 token 数字。按需加载是加载策略,不是空间不够时的降级;换更大 context 的模型作用在一个题干没有指认的变量上。
QUESTION 04 · TS 2.6 · DATELESS IDS ARE NOT POINTERS
A service pins "model": "claude-sonnet-4-6" in code and has run unchanged for four months. On one Tuesday, latency-sensitive classification outputs shift: same prompts, same parameters, a measurable change in edge-case wording, no code deploy. A teammate argues that the dateless ID is an evergreen pointer that has just rolled onto a newer snapshot. How should the on-call engineer respond?
正确 C官方专门写了这条 misconception:每个 Claude model ID 都是 pinned snapshot,从 4.6 代起的 dateless 格式同样是 pinned snapshot 而不是 evergreen 指针;Anthropic 不更新既有 ID 的权重或配置,有更新版本时以新 ID 发布。同一页也给了正确的排查方向:权重固定但 serving infrastructure(请求路由、安全分类器、采样逻辑)会变,偶尔在 ID 与权重都没变时产生可观察的细微行为差异。
D1 凭空捏造违反 P5A:claude-sonnet-4-6 没有「加上日期的同一个 snapshot」这种写法。4.6 代及之后的 ID 就是 dateless 格式,带日期的形态属于 4.6 代之前的模型。那个标识符是本书自制的干扰项,没有官方出处——格式看起来完全合理,只是不存在。
D6 半对半错违反 P8B:前半句对了一半——4.6 代之前的短 alias 确实是 convenience pointer,解析到该 minor 版本最近的 dated snapshot。但官方紧接着说 4.6 代的 ID is not an alias, it is the snapshot。把 alias 的语义推广到「每一个不带日期的标识符」,正是这道题要考的那个错。
D2 无关杠杆违反 P6D:题干给的信号是「某一天起集体变化、同 prompt 同参数、无部署」,这是版本/基础设施轴的时间信号,不是逐次调用的采样抖动。先分源头再开药(P6);这里连源头都还没分,就先去动一个作用在另一条轴上的旋钮。
QUESTION 05 · TS 2.6 · RETIREMENT IS NOT A TRANSIENT ERROR
A nightly batch job pins a dated Opus model ID that Anthropic deprecated months ago with a published retirement date, which passed last week. Since then 100% of the job's requests fail immediately; the failure rate is flat rather than bursty, and the same credentials work against the team's other services. The job's retry layer has already burned 6,000 attempts. What is the correct remediation?
正确 B官方对 Retired 的定义就是一句话:模型不再可用,Requests to retired models will fail。配套的处置也写明了:一旦 deprecated 就要在 retirement date 之前迁移完,过期后的请求会失败,并建议提前对替代模型做充分测试。「先测再切」是这条里被明确写出来的部分,不是本书加的。
D2 无关杠杆违反 P6A:题干已经把源头指认清楚了——失败率平坦、即时、同凭据在别处正常。重试作用于瞬时失败,而这里的失败是稳定且由请求内容决定的;加 jitter 只是把同一个错误记录得更整齐。
D1 凭空捏造违反 P8C:「短 alias 永远指向当前 snapshot」这条被拉得太远了。alias 只存在于 4.6 代之前的模型上,且只解析到该 minor 版本最近的 dated snapshot,那个 minor 版本整体退役时它一样会失效——每个 model ID 都有各自独立的 deprecation 与 retirement 时间表。用取消钉版本来换「再也不会被退役影响」,方向刚好反了。
D4 换模型当解法违反 P1D:换到最小的模型确实能让请求跑通,但它把「迁到推荐替代并验证」偷换成了「随便挑一个能跑的」,还顺手把成本当成了本题的优化目标——题干里没有任何成本约束。留着原样的重试层同样没被处理。
QUESTION 06 · TS 2.6 · PROMPT VERSIONING SERVER-SIDE
Two engineers maintain a Managed Agents agent whose system prompt is edited through the API. On Thursday a quality regression appears; nobody can say which of the week's edits caused it, and one engineer's edit silently replaced the other's from twenty minutes earlier. Neither call reported an error. What should the team change?
正确 C两个症状对应同一套机制的两半。更新在配置真的变了时生成新版本,version 从 1 开始递增,List versions 返回完整版本历史用来追踪变化;而「谁也没收到错误就被覆盖了」正是省略 version 的语义——省略即无条件应用、last write wins,带上它则不匹配时返回 409。
D5 错层承载违反 P4A:CLAUDE.md 是 Claude Code 每次会话加载的项目指令,不是 Managed Agents 的 system 字段。而 agent 本身就是一份 reusable, versioned configuration,system prompt 已经住在一个带版本的服务端资源里;再搭一条自制同步链路,等于把现成的版本轴换成一条没有并发保护的。
D6 半对半错违反 P8B:手写 banner 确实能让人读出「现在是哪一版」,但它解决不了题干的第二个症状——省略 version 的语义就是 last write wins,两位工程师都不会收到错误,banner 只会跟着被一起覆盖。前半句甚至把病因当成了处方。
D2 无关杠杆违反 P8D:钉模型作用在模型轴上,题干的两个症状都在 prompt 轴上。何况「钉住就可复现」本身就过头了——权重对给定 ID 固定,但 serving infrastructure 会变,偶尔产生可观察的行为差异。
QUESTION 07 · TS 2.6 · RANGE CONFLICT
An internal marketplace hosts secrets-vault. Plugin deploy-kit is installed and declares { "name": "secrets-vault", "version": "~2.1.0" }. An engineer now runs claude plugin install release-bot, whose manifest declares { "name": "secrets-vault", "version": "~3.0.0" }. The install fails with a has conflicting version requirements error and the engineer reports that deploy-kit "seems fine, so the dependency must be broken." What is actually going on?
正确 D官方写明多个已安装插件约束同一依赖时取交集并解析到满足全部约束的最高版本;~2.1 与 ~3.0 这一组的结果就是后装的那个插件安装失败、报 has conflicting version requirements,而原插件与依赖保持原状。所以「deploy-kit 看着没事」不是矛盾,正是预期行为,处置方向是调整两个 range 之一。
D6 半对半错违反 P5A:「先手工装上依赖就能满足约束」这句话在官方文档里确实存在,但它属于跨 marketplace 被拒的那条路径。这里报的是 has conflicting version requirements,两个 range 不相交,手工装哪个版本都会让另一个插件不满足。真机制、错场景。
D1 凭空捏造违反 P5B:「per-plugin override / 两份并存」是编出来的。官方的模型是一个依赖被交集出一个版本,冲突时安装失败,不存在同一 marketplace 内同名依赖并行两份的机制。
D6 半对半错违反 P8C:去掉约束确实能让这次安装过去,但后半句把利弊说反了。官方对无约束依赖的描述是:默认跟踪最新可用版本,上游一次发布就可能在无预警的情况下把依赖换掉——版本约束的存在意义正是防这个。
QUESTION 08 · TS 2.6 · UNRESOLVED DEPENDENCY
A platform team publishes backend-standard, a manifest that is little more than a dependencies array of four plugins. On a new hire's machine the install pulls in three of them; the fourth stays unresolved and claude plugin list reports a dependency error naming it. That fourth plugin lives in a second internal marketplace that the new hire has never added. What is the correct fix?
正确 A官方的默认规则是:Claude Code 不安装位于另一个 marketplace 的依赖,除非用户已在同一 scope 装好并启用了它;要放行,由 root marketplace(托管 backend-standard 的那个)的维护者把目标 marketplace 加进 allowCrossMarketplaceDependenciesOn。题干的「三个装上、第四个卡住、它在另一个 marketplace」正是这条默认规则的形状;两条出路都来自同一段官方原文。
D5 错层承载违反 P4B:.mcp.json 是插件里放 MCP server 定义的默认位置,不是声明「我依赖另一个插件」的地方——那是 plugin.json 的 dependencies 数组。把一个插件依赖降级成一份内联的 server 配置,等于放弃版本约束、自动安装与启用连带这一整套。
D7 过度工程违反 P10C:轻的一档是改一行允许清单或先装那一个插件,而这里直接跳到了「把上游代码抄进自己仓库并从此自己维护」。题干没有任何证据表明轻手段失败过;复制一份还顺手废掉了上游的 tag 版本解析。
D2 无关杠杆违反 P6D:prune 清理的是「已经没有任何已装插件需要它」的孤儿依赖,方向与本题相反——这里的依赖不是多余的,是缺的。而且题干已经指认了源头:那个 marketplace 从来没被添加过。