= l1} />
@@ -167,61 +169,184 @@ const ThreePositions: React.FC<{l1: number; l2: number; l3: number; layerAt: num
);
};
-/** 6-B 一句话合同金句卡(core)+ 边界两句小字跟随 */
-const ContractQuote: React.FC<{boundAt: number}> = ({boundAt}) => {
+/** 6-B 免责两句前置(p6-13/14 信源样式小字)→ 一句话合同金句卡(p6-07)
+ * → p6-09 钟挪进管家过渡 → 守时的自主边界两句(p6-10/11)
+ * → 四件套回收条(p6-12)+ 本集钉进「循环」格(p6-12a)。 */
+const ContractAndRecap: React.FC<{disclaim2At: number; quoteAt: number; boundAt: number; fourAt: number; pinAt: number}> = ({
+ disclaim2At,
+ quoteAt,
+ boundAt,
+ fourAt,
+ pinAt,
+}) => {
const frame = useCurrentFrame();
const {fps} = useVideoConfig();
- const enter = spring({frame: frame - 4, fps, config: {damping: 200}});
- const bound = interpolate(frame - boundAt, [0, 18], [0, 1], {
+ // 免责两行:p6-13 句首浮出第一行,p6-14 句锚浮出第二行(信源样式小字)
+ const d1 = interpolate(frame - 6, [0, 12], [0, 1], {
+ extrapolateLeft: 'clamp',
+ extrapolateRight: 'clamp',
+ });
+ const d2 = interpolate(frame - disclaim2At, [0, 12], [0, 1], {
+ extrapolateLeft: 'clamp',
+ extrapolateRight: 'clamp',
+ });
+ // 金句卡(p6-07)spring 淡入;免责区随之淡出让位
+ const quote = spring({frame: frame - quoteAt, fps, config: {damping: 200}});
+ const disclaimGone = interpolate(frame - quoteAt, [0, 16], [1, 0], {
+ extrapolateLeft: 'clamp',
+ extrapolateRight: 'clamp',
+ });
+ // p6-09 过渡 + p6-10/11 边界两句
+ const clock = interpolate(frame - boundAt, [0, 18], [0, 1], {
+ extrapolateLeft: 'clamp',
+ extrapolateRight: 'clamp',
+ });
+ // 四件套回收条(p6-12)四格依次亮
+ const four = interpolate(frame - fourAt, [0, 16], [0, 1], {
+ extrapolateLeft: 'clamp',
+ extrapolateRight: 'clamp',
+ });
+ const FOUR = ['循环', '工具', '上下文管理', '护栏'];
+ // p6-12a 本集钉进「循环」格:core 描边 + 呼吸辉光(帧驱动 sin,确定性)
+ const pin = interpolate(frame - pinAt, [0, 14], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
+ const glow = 0.55 + 0.45 * Math.sin(frame / 5);
return (
-
-
-
- {'按下的不一定是它,'}
-
- {'等的一定不是你。'}
-
+
+ {/* 免责两句前置:信源样式小字(p6-13 官方文档 / p6-14 第三方标注) */}
+ {disclaimGone > 0.01 ? (
+
+
+
+ {'本片机制依据官方文档 · 取数 2026 年 8 月,以官方最新表述为准'}
+
+
+ {'产品内部的部分为第三方源码分析 · 片中已逐处标注'}
+
+
+
+ ) : null}
+ {/* 一句话合同金句卡 + 边界两句(p6-07 起,随后 p6-09..11 跟随;
+ paddingBottom 抬高中线——p6-12 四件套回收条在 bottom:246 带下方让位) */}
+ {quote > 0 ? (
+
+
+
+ {'按下的不一定是它,'}
+
+ {'等的一定不是你。'}
+
+ {/* p6-09「钟挪进不睡的管家」过渡 + p6-10/11 守时的自主边界 */}
+
+ {'钟曾经装在它身体里,现在挪进了一个不睡的管家。'}
+
+
+ {'它的自主,到此为止是「守时的自主」——'}
+
+ {'「自己决定该干什么活」是更大的一步,我们后面再拆。'}
+
+
+
+
+ ) : null}
+ {/* 四件套回收条(p6-12 四格依次亮;反枚举:四格不给四色)。
+ 带 bottom:246 与 6-A「机制外挪」同一水平带,避开 Footnote(bottom:168)压字 */}
+ {four > 0 ? (
- {'有人在等它,但它从不等你;它也开始学着不等任何东西。'}
-
-
{'时钟装在它的身体里,不在墙上 —— 守时的自主'}
+ {FOUR.map((t, i) => {
+ const cell = interpolate(frame - fourAt - i * 6, [0, 10], [0, 1], {
+ extrapolateLeft: 'clamp',
+ extrapolateRight: 'clamp',
+ });
+ const isLoop = i === 0;
+ return (
+
+
0 ? `3px solid ${theme.core}` : `2px solid ${theme.panelBorder}`,
+ background: isLoop && pin > 0 ? `${theme.coreDeep}` : theme.panel,
+ fontFamily: theme.sans,
+ fontSize: 26,
+ color: isLoop && pin > 0 ? theme.text : theme.dim,
+ opacity: cell,
+ whiteSpace: 'nowrap',
+ boxShadow: isLoop && pin > 0 ? `0 0 ${26 * glow}px ${theme.core}` : 'none',
+ }}
+ >
+ {t}
+
+ {/* p6-12a 本集钉进「循环」格:图钉落下 */}
+ {isLoop && pin > 0 ? (
+
+ ) : null}
+
+ );
+ })}
-
+ ) : null}
+ {/* 四件套画像的出处角标(p6-12 起,【二】官方工程博客) */}
+
+ {'循环 · 工具 · 上下文管理 · 护栏 —— 官方工程博客'}
+
);
};
/**
- * 6-C 信源卡 + 身份卡 + 渐黑。
+ * 6-C 身份卡 → 下期卡 → 渐黑(单句 p6-15)。
* `beatDurationInFrames` 是**本 beat 的总时长**,渐黑窗口据此反推(红线四:
* 不写死帧数、不用末句时长——末句短于 beat 时会提前收尾)。
+ * 下期卡在句内后半段浮出:窗口从 beat 总时长按固定比例推导(句锚不可分——
+ * 单句镜无第二锚,比例锚定在句中段「为什么还有人这么干」分句处)。
*/
-const SourceAndFade: React.FC<{beatDurationInFrames: number; seriesAt: number}> = ({
- beatDurationInFrames,
- seriesAt,
-}) => {
+const IdentityAndFade: React.FC<{beatDurationInFrames: number}> = ({beatDurationInFrames}) => {
const frame = useCurrentFrame();
const {fps} = useVideoConfig();
const enter = spring({frame: frame - 4, fps, config: {damping: 200}});
- const rows = [
- ['官方文档', 'code.claude.com/docs · 取数2026年8月'],
- ['工程博客', 'anthropic.com/engineering'],
- ['源码分析', '第三方逆向分析 · 片中逐处标注'],
- ['数字口径', '开源仓库钉版 67a9126c 实测 · 字节归档'],
- ];
- const seriesT = interpolate(frame - seriesAt, [0, 20], [0, 1], {
+ // 下期卡:身份卡稳住一拍后浮出(beat 总时长 × 0.52 处——口播念到
+ // 「为什么还有人这么干」时下期卡就位,「下期见」前视觉先到)
+ const nextAt = Math.round(beatDurationInFrames * 0.52);
+ const nextT = interpolate(frame - nextAt, [0, 18], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
@@ -234,72 +359,23 @@ const SourceAndFade: React.FC<{beatDurationInFrames: number; seriesAt: number}>
});
return (
-
-
-
- {'信源'}
-
- {rows.map(([k, v], i) => (
-
- ))}
- {/* 诚实行:产品内部断言均为第三方的源码分析(【三】归属句的公开落点) */}
-
- {'涉及产品内部的部分,均为第三方的源码分析,片中已逐处标注;取证字节已随片归档'}
-
-
-
- {seriesT > 0 ? (
+
+
+ {'Claude Code Harness Engineering'}
+
-
- {'Claude Code Harness Engineering'}
-
-
- {'时机层:谁来按下开始'}
-
- {/* 下期预告卡:标题只在画面(反串线纪律) */}
+ {'时机层:谁来按下开始'}
+
+ {/* 下期预告卡:标题只在画面(反串线纪律) */}
+ {nextT > 0 ? (
border: `1.5px solid ${theme.panelBorder}`,
borderRadius: 12,
background: theme.panel,
+ opacity: nextT,
+ transform: `translateY(${(1 - nextT) * 14}px)`,
+ display: 'inline-block',
}}
>
{'下期 · 协作层'}
-
-
- ) : null}
+ ) : null}
+
{/* 渐黑遮罩 */}
@@ -328,28 +407,36 @@ export const P6Ending: React.FC<{scene: SceneRange}> = ({scene}) => {
const w = (fromId: string, toId?: string) => beatWindow(scene.sentences, scene.from, fromId, toId);
const at = (id: string) => w(id).from;
const bA = w('p6-01', 'p6-06');
- const bB = w('p6-07', 'p6-12');
- const bC = w('p6-13', 'p6-15');
+ const bB = w('p6-13', 'p6-12a');
+ const bC = w('p6-15');
return (
+
+ {/* l1 锚 p6-01(W10 抽帧实拍:原锚 p6-02 使首句 5.3s 全幕只剩抬头,
+ 收尾句「这一集其实只回答了一个问题:谁来按开始」配真空画面)。
+ 第一张卡随首句浮出,p6-02 口播「有人按并等着」时它已就位 */}
-
-
+
+ {/* p6-13/14 免责前置;p6-07 金句;p6-09..11 边界;p6-12 四件套;p6-12a 钉进循环格 */}
+
-
+
{/* 渐黑窗口从**本 beat 总时长**推导,不是末句时长(红线四) */}
-
+
);
diff --git a/apps/negentropy-influence/episodes/claude-code-explained-video/research/verification-2026-08-24.md b/apps/negentropy-influence/episodes/claude-code-explained-video/research/verification-2026-08-24.md
new file mode 100644
index 00000000..f400d41e
--- /dev/null
+++ b/apps/negentropy-influence/episodes/claude-code-explained-video/research/verification-2026-08-24.md
@@ -0,0 +1,25 @@
+# 阶段④ 双校验记录(B 方案品控改造 · 2026-08-24)
+
+> 39 条双票 findings 落地后的新增断言校验。前版记录见 verification-2026-08-23.md。
+
+## 新增断言回溯
+
+| 口播句 | 断言 | 证据级 | 事实源 | 判定 |
+|---|---|---|---|---|
+| p0-03a..c | 同一模型换套脚手架,成绩 22%→49% | 【二】 | 官方 SWE-bench 脚手架实验(口径 2025-01-06 进角标) | ✅ VERIFIED |
+| p0-06/p5-05 | 官方四件套:循环/工具/上下文管理/护栏 | 【二】 | claude.com/blog/harnessing-claudes-intelligence(全系列口径统一) | ✅ VERIFIED |
+| p3-19a..d | OS 级沙箱:文件+网络双边界、权限提示 −84% | 【二】 | 官方工程博客(取数 2025-10 进角标);Seatbelt/bubblewrap 只进角标 | ✅ VERIFIED |
+| p3-31a/b | 分类器成绩单:快筛误杀 8.5% → 复核后 0.4% | 【二】 | 官方 auto-mode 博客(2026-03 口径) | ✅ VERIFIED |
+| p4-28a..c | 退出码契约:唯一硬信号阻断,惯用失败码不阻断 | 【二】 | 官方 hooks reference | ✅ VERIFIED |
+| p1-12a/b | 「教学版」=开源最小实现(首现锚定) | 【三】 | 归属句「有人拆过它的源码」保留;不点名站点 | ✅ VERIFIED(合规) |
+
+## 删除项核对
+
+- p1-07/p2-04/p3-13/p3-16..19/p3-27/p3-33/p4-20+21/p5-12+13/p6-03:全部为复述/咏叹/情绪垫场,
+ 语义由存活句或画面承载(分镜动效列已注明承载方式)。
+- 收尾改序(免责前置):句 id 与文本零改动,TTS 缓存全保。
+
+## 口径切换记录
+
+- 四件套第三件:全系列统一为「上下文管理」(EP4 p6-12 已同步)。
+- 「教学版」17 处全系列首次锚定(p1-12a),后续使用均有指代。
diff --git a/apps/negentropy-influence/episodes/claude-code-explained-video/script/narration.json b/apps/negentropy-influence/episodes/claude-code-explained-video/script/narration.json
index 59cfc801..3de04e1b 100644
--- a/apps/negentropy-influence/episodes/claude-code-explained-video/script/narration.json
+++ b/apps/negentropy-influence/episodes/claude-code-explained-video/script/narration.json
@@ -2,17 +2,32 @@
{
"id": "p0-01",
"scene": "P0",
- "text": "你让 AI 跑个脚本:它把命令写好,然后就停在那儿了。"
+ "text": "你让 AI 跑个脚本。它把命令写得漂漂亮亮的,然后就停在那儿了。"
},
{
"id": "p0-02",
"scene": "P0",
- "text": "复制、粘贴、回车、再把输出贴回去——每一轮的搬运工,是你。"
+ "text": "复制、粘贴、回车,再把输出贴回去。每一轮的搬运工,是你。"
},
{
"id": "p0-03",
"scene": "P0",
- "text": "从「写出命令」到「真的跑起来」,缺的那一层,官方起了名字。"
+ "text": "能写出命令,和真的跑起来,中间隔着一层。这一层,官方起过名字。"
+ },
+ {
+ "id": "p0-03a",
+ "scene": "P0",
+ "text": "这一层到底有多要紧?他们自己的评测战报里,写过一句话。"
+ },
+ {
+ "id": "p0-03b",
+ "scene": "P0",
+ "text": "同一个底层模型,换一套脚手架,成绩能差出一大截。"
+ },
+ {
+ "id": "p0-03c",
+ "scene": "P0",
+ "text": "你出多少力,它得多少分。"
},
{
"id": "p0-04",
@@ -22,22 +37,22 @@
{
"id": "p0-05",
"scene": "P0",
- "text": "官方说得直白:这套东西是 Harness,模型装在 Harness 里面。"
+ "text": "官方自己说得很直白:这套东西就是 Harness,模型装在里面。"
},
{
"id": "p0-06",
"scene": "P0",
- "text": "Harness 供给它五样东西:文件、命令、权限、记忆、护栏。"
+ "text": "Harness 给它四件:一个循环、一套工具、上下文管理、一道护栏。"
},
{
"id": "p0-07",
"scene": "P0",
- "text": "这集就把这个 Harness,一层一层拆开看。"
+ "text": "这集,就一层一层拆开它。"
},
{
"id": "p1-01",
"scene": "P1",
- "text": "先说结论:那一层的核心是一个循环,像一条不停转的传送带。"
+ "text": "先说结论。核心就是一个循环,像一条不停转的传送带。"
},
{
"id": "p1-02",
@@ -47,62 +62,67 @@
{
"id": "p1-03",
"scene": "P1",
- "text": "每问一次,就看模型的回答里有没有夹着一次工具调用。"
+ "text": "每问一次,就翻开它的回答,看里面有没有夹着一次工具调用。"
},
{
"id": "p1-04",
"scene": "P1",
- "text": "有,就说明模型举手了,它要用工具。"
+ "text": "有,模型举手了。"
},
{
"id": "p1-05",
"scene": "P1",
- "text": "那就替它把工具跑一遍,把结果原样塞回对话里,再问下一轮。"
+ "text": "那就替它把这件工具跑一遍,结果原样塞回对话里,然后接着问下一轮。"
},
{
"id": "p1-06",
"scene": "P1",
- "text": "没有,就说明它觉得活干完了,循环停机,把话交给你。"
- },
- {
- "id": "p1-07",
- "scene": "P1",
- "text": "就这么两个信号,决定这条传送带转不转。"
+ "text": "没有,就是它觉得活干完了。循环停机,把话交还给你。"
},
{
"id": "p1-08",
"scene": "P1",
- "text": "把这件事翻成代码,只有五步。"
+ "text": "翻成代码,只有五步。"
},
{
"id": "p1-09",
"scene": "P1",
- "text": "先把你的问题放进消息列表,再连同「我有哪些工具」发给模型;"
+ "text": "先把你的问题放进消息列表,再连同「我有哪些工具」一起发给模型;"
},
{
"id": "p1-10",
"scene": "P1",
- "text": "把模型的回答追加进列表,挑出里面所有的工具调用;"
+ "text": "把模型的回答追加进列表,再挑出里面所有的工具调用;"
},
{
"id": "p1-11",
"scene": "P1",
- "text": "一个都没有就地返回,有就逐个执行、收好每条结果;"
+ "text": "一个都没有,就地返回;有,就逐个执行,收好每条结果;"
},
{
"id": "p1-12",
"scene": "P1",
- "text": "最后把结果打包成新消息追加进去,回到第二步。写完了。"
+ "text": "最后把结果打包成一条新消息追加进去,回到第二步。写完了。"
+ },
+ {
+ "id": "p1-12a",
+ "scene": "P1",
+ "text": "说明一下:有人照着这套机制,写过一个开源的小版本。"
+ },
+ {
+ "id": "p1-12b",
+ "scene": "P1",
+ "text": "下面就拿它当例子,边读边说真正的做法。"
},
{
"id": "p1-13",
"scene": "P1",
- "text": "有人给它的评价是:三十多行,最小可运行的内核。"
+ "text": "有人给它的评价是:三十多行,一个最小的、能跑的内核。"
},
{
"id": "p1-14",
"scene": "P1",
- "text": "我自己数过这一版:光循环那个函数,去掉空行和注释,二十三行。"
+ "text": "我自己数了一遍:光循环那个函数,去掉空行和注释,二十三行。"
},
{
"id": "p1-15",
@@ -112,22 +132,22 @@
{
"id": "p1-16",
"scene": "P1",
- "text": "脚手架负责真的去跑、把结果搬回来——判断力在那边,手脚在这边。"
+ "text": "壳负责真的去跑,把结果搬回来。判断力在那边,手脚在这边。"
},
{
"id": "p1-17",
"scene": "P1",
- "text": "模型回话时会附一个「我为什么停下来」的标记。"
+ "text": "模型每次回话,都会附一个「我为什么停下来」的标记。"
},
{
"id": "p1-18",
"scene": "P1",
- "text": "最直觉的写法是直接看它;但教学版改了,"
+ "text": "最直觉的写法就是直接看这个标记。可这份代码偏不这么干。"
},
{
"id": "p1-19",
"scene": "P1",
- "text": "改成翻开回答本身,看里面有没有工具调用。为什么?"
+ "text": "它翻开回答本身,去看里面到底有没有工具调用。为什么这么绕?"
},
{
"id": "p1-20",
@@ -142,12 +162,12 @@
{
"id": "p1-22",
"scene": "P1",
- "text": "那个标记却可能还没更新——他说源码注释写得很直白,这标记不可靠。"
+ "text": "那个标记却可能还没更新。他说源码注释写得很直白:这东西不可靠。"
},
{
"id": "p1-23",
"scene": "P1",
- "text": "所以真正的做法是一边流一边看,见到工具调用就记下还得再来一轮。"
+ "text": "所以真正的做法是一边流一边看。见到工具调用,就记下还得再来一轮。"
},
{
"id": "p1-24",
@@ -157,12 +177,12 @@
{
"id": "p1-25",
"scene": "P1",
- "text": "官方给这个循环画过像:三件事交融着转——"
+ "text": "官方给这个循环画过像:三件事交融着转。"
},
{
"id": "p1-26",
"scene": "P1",
- "text": "收集上下文、采取行动、验证结果,不是硬阶段。"
+ "text": "收集上下文、采取行动、验证结果,它们不是硬阶段。"
},
{
"id": "p1-27",
@@ -172,27 +192,27 @@
{
"id": "p1-28",
"scene": "P1",
- "text": "教学版身上只带一份消息列表;真实产品带着十样,"
+ "text": "玩具版身上只带一份消息列表。成型的那套呢?整整带了十样:"
},
{
"id": "p1-29",
"scene": "P1",
- "text": "压缩进度、补救次数、钩子拦没拦过停机、轮数——细节进角标。"
+ "text": "压缩进度、补救次数、钩子拦没拦过停机、轮数。细节都进角标。"
},
{
"id": "p1-30",
"scene": "P1",
- "text": "循环没长个,长出来的是它随身带的状态。"
+ "text": "循环没长个。长出来的是它随身带的那些状态。"
},
{
"id": "p1-31",
"scene": "P1",
- "text": "退出的路子也有一大把:报错、中断、钩子叫停、预算烧完。"
+ "text": "退出的路子还有一大把:报错、中断、钩子叫停、预算烧完。"
},
{
"id": "p1-32",
"scene": "P1",
- "text": "但它们全是保护装置,不是骨架;骨架还是那二十来行。"
+ "text": "可它们全是保护装置,不是骨架。骨架还是那二十来行。"
},
{
"id": "p2-01",
@@ -202,22 +222,17 @@
{
"id": "p2-02",
"scene": "P2",
- "text": "想读文件,它得拼出一条读文件的命令;想写文件,得拼一条重定向。"
+ "text": "想读文件,它得拼出一条读文件的命令。想写文件,再拼一条重定向。"
},
{
"id": "p2-03",
"scene": "P2",
- "text": "模型心里想的是「读这个文件」,嘴上却要翻译成一条命令行。"
- },
- {
- "id": "p2-04",
- "scene": "P2",
- "text": "多这一层翻译,费字数,还容易拼错。"
+ "text": "模型心里想的是「读这个文件」,嘴上却得翻成一条命令行。"
},
{
"id": "p2-05",
"scene": "P2",
- "text": "那就把常用动作直接做成工具,于是从一件变成五件:"
+ "text": "那就干脆把常用动作直接做成工具。于是一件变成了五件:"
},
{
"id": "p2-06",
@@ -227,7 +242,7 @@
{
"id": "p2-07",
"scene": "P2",
- "text": "问题是循环怎么知道该调哪一个?答案朴素得让人意外:查一张表。"
+ "text": "可循环怎么知道该调哪一个?答案朴素得让人意外:查一张表。"
},
{
"id": "p2-08",
@@ -242,17 +257,17 @@
{
"id": "p2-10",
"scene": "P2",
- "text": "于是加一个新工具只要两件事:在清单里加一条描述,在表里加一行。"
+ "text": "加一个新工具就两件事:清单里加一条描述,表里加一行。"
},
{
"id": "p2-11",
"scene": "P2",
- "text": "循环那边呢?我把两个版本逐行比过,"
+ "text": "那循环那边呢?我把前后两个版本逐行比过。"
},
{
"id": "p2-12",
"scene": "P2",
- "text": "实质改动只有一行——原来写死了去跑命令,现在改成查表。"
+ "text": "实质改动只有一行。原来写死了去跑命令,现在改成查表。"
},
{
"id": "p2-13",
@@ -267,12 +282,12 @@
{
"id": "p2-15",
"scene": "P2",
- "text": "教学版老实排队,一个一个来;真实产品里能并行的就并行。"
+ "text": "玩具版是老实排队,一个一个来。真的那个呢?能并行就并行。"
},
{
"id": "p2-16",
"scene": "P2",
- "text": "而它判断能不能并行的方式,是这一集我最想让你记住的一处。"
+ "text": "而它怎么判断能不能并行?这是这一集我最想让你记住的一处。"
},
{
"id": "p2-17",
@@ -282,7 +297,7 @@
{
"id": "p2-18",
"scene": "P2",
- "text": "但按第三方的源码分析,这两件事其实不是一回事。"
+ "text": "但有人读源码读出来:这两件事根本不是一回事。"
},
{
"id": "p2-19",
@@ -292,12 +307,12 @@
{
"id": "p2-20",
"scene": "P2",
- "text": "同一件工具,同一个名字,结论相反。"
+ "text": "同一件工具,结论相反。"
},
{
"id": "p2-21",
"scene": "P2",
- "text": "因为判断的不是这件工具,是这一次具体要干什么。"
+ "text": "判断的根本不是这件工具,是这一次具体要干什么。"
},
{
"id": "p2-22",
@@ -307,7 +322,7 @@
{
"id": "p2-23",
"scene": "P2",
- "text": "原因是它每次改的都是不同的文件,谁也不挡谁。"
+ "text": "因为它每次改的都是不同的文件,谁也不挡谁。"
},
{
"id": "p2-24",
@@ -317,7 +332,7 @@
{
"id": "p2-25",
"scene": "P2",
- "text": "判断完了怎么排?按原来的顺序切段:连着能并行的划成一组一起跑,"
+ "text": "判断完了怎么排?按原来的顺序切段:连着能并行的划成一组,一起跑,"
},
{
"id": "p2-26",
@@ -327,12 +342,12 @@
{
"id": "p2-27",
"scene": "P2",
- "text": "他还提到,工具输出太长时产品不会全喂给模型,而是存成文件,"
+ "text": "他还提到一件事:工具输出太长,它不会全喂给模型,而是存成文件,"
},
{
"id": "p2-28",
"scene": "P2",
- "text": "只给它一段预览和一个路径。"
+ "text": "只给一段预览和一个路径。"
},
{
"id": "p2-29",
@@ -347,7 +362,7 @@
{
"id": "p2-31",
"scene": "P2",
- "text": "读、存、再读、再存,它会自己套住自己,转不出来。"
+ "text": "读、存、再读、再存。它自己把自己套住了,转不出来。"
},
{
"id": "p3-01",
@@ -357,7 +372,7 @@
{
"id": "p3-02",
"scene": "P3",
- "text": "读写文件那几件都被拦在工作目录里,但跑命令那件,没人管它。"
+ "text": "读写文件那几件,都被拦在工作目录里。跑命令那件,没人管。"
},
{
"id": "p3-03",
@@ -367,12 +382,7 @@
{
"id": "p3-04",
"scene": "P3",
- "text": "它完全可能给你递上一条把整块盘删干净的命令,"
- },
- {
- "id": "p3-05",
- "scene": "P3",
- "text": "而按前面的设计,循环会老老实实替它执行。"
+ "text": "它完全可能给你递上一条把整块盘删干净的命令。循环照跑。"
},
{
"id": "p3-06",
@@ -382,7 +392,7 @@
{
"id": "p3-07",
"scene": "P3",
- "text": "拦法是三道闸门,顺序写死。"
+ "text": "三道闸门,顺序写死。"
},
{
"id": "p3-08",
@@ -392,7 +402,7 @@
{
"id": "p3-09",
"scene": "P3",
- "text": "命中就直接挡回去,连问都不问。"
+ "text": "命中就挡回去,问都不问。"
},
{
"id": "p3-10",
@@ -402,82 +412,67 @@
{
"id": "p3-11",
"scene": "P3",
- "text": "读写工作目录之外的东西,或者命令里带着删除、改权限这类动作。"
+ "text": "读写工作目录之外的东西;或者命令里带着删除、改权限这类动作。"
},
{
"id": "p3-12",
"scene": "P3",
- "text": "第三道就是问你:画面停下来,把工具名和参数摆给你看,等你点头。"
- },
- {
- "id": "p3-13",
- "scene": "P3",
- "text": "三道都没命中呢?兜底永远再问你一句——弃权不等于放行。"
+ "text": "第三道就是问你:画面停住,把工具名和参数摆给你看,等你点头。"
},
{
"id": "p3-14",
"scene": "P3",
- "text": "教学版里有一句很诚实的话,我想念出来。"
+ "text": "这份代码自己也承认:拒绝表只是字符串匹配,换个拼法就绕过去。"
},
{
"id": "p3-15",
"scene": "P3",
- "text": "它说,这张拒绝表用的是最简单的字符串匹配,"
+ "text": "它告诉你闸门装在哪儿,不替你把门守住。"
},
{
- "id": "p3-16",
+ "id": "p3-19a",
"scene": "P3",
- "text": "它的作用是告诉你「闸门应该装在哪儿」,"
+ "text": "真正的边界在更底下一层。跑命令那件工具,被套进了一层沙箱。"
},
{
- "id": "p3-17",
+ "id": "p3-19b",
"scene": "P3",
- "text": "而不能被当成一道真正的安全边界——"
+ "text": "能写哪些目录、能走哪个网络出口,由操作系统说了算。"
},
{
- "id": "p3-18",
+ "id": "p3-19c",
"scene": "P3",
- "text": "因为同一件坏事有无数种写法,换个拼法就绕过去了。"
+ "text": "规则是劝,沙箱是锁。两道墙少一道,笼子就不成立。"
},
{
- "id": "p3-19",
+ "id": "p3-19d",
"scene": "P3",
- "text": "一个教学项目愿意在自己的示例代码旁写下这句话,挺难得。"
+ "text": "官方工程博客给了数:内部用下来权限提示少八成四。"
},
{
"id": "p3-20",
"scene": "P3",
- "text": "那真实产品里的门长什么样?"
- },
- {
- "id": "p3-21",
- "scene": "P3",
- "text": "按第三方的源码分析,结论不是三种,是四种。"
+ "text": "回到闸门这边。有人读源码读出来:判出来是四种,不是三种。"
},
{
"id": "p3-23",
"scene": "P3",
- "text": "多出来的第四种最有意思:工具选择不表态。"
+ "text": "多出来的第四种最有意思:这件工具选择不表态。"
},
{
"id": "p3-24",
"scene": "P3",
- "text": "也就是说,弃权不等于放行,兜底永远是问你一句。"
+ "text": "也就是说,弃权不等于放行。兜底永远是问你一句。"
},
{
"id": "p3-25",
"scene": "P3",
- "text": "规则求值的顺序官方也写死了:拒绝、询问、放行,"
+ "text": "规则是一条条往下判的,顺序官方也写死了:拒绝、询问、放行。"
},
{
"id": "p3-26",
"scene": "P3",
- "text": "头一个命中就出局——规则写得多具体,都改变不了顺序。"
- },
- {
- "id": "p3-27",
- "scene": "P3",
- "text": "所以一条宽拒绝,靠一条窄放行开不出例外。"
+ "text": "头一个命中就出局。规则写得再具体,也改不了顺序。"
},
{
"id": "p3-28",
@@ -487,12 +482,12 @@
{
"id": "p3-29",
"scene": "P3",
- "text": "就算全放行模式,也压不住一条写死的拒绝。"
+ "text": "就算你开了全放行模式,也压不住一条写死的拒绝。"
},
{
"id": "p3-30",
"scene": "P3",
- "text": "全自动模式不靠你点头的次数,靠另一个模型当闸门。"
+ "text": "全自动模式不靠你点头的次数,靠另一个模型来当闸门。"
},
{
"id": "p3-31",
@@ -500,24 +495,29 @@
"text": "官方遥测补一刀:权限提示,九成三都被点了批准。"
},
{
- "id": "p3-32",
+ "id": "p3-31a",
"scene": "P3",
- "text": "审判者的视野是做减法的:只看用户消息和裸命令。"
+ "text": "这个当闸门的模型,自己也进过考场。"
},
{
- "id": "p3-33",
+ "id": "p3-31b",
+ "scene": "P3",
+ "text": "快筛误杀率百分之八点五,复核之后压到千分之四。"
+ },
+ {
+ "id": "p3-32",
"scene": "P3",
- "text": "模型的自我辩解、工具的输出,全被剥在视野外。"
+ "text": "这个审判者的视野是做减法的:只看用户消息和裸命令。"
},
{
"id": "p3-34",
"scene": "P3",
- "text": "评它做了什么,不看它说了什么——注入的谎话骗不到它。"
+ "text": "评它做了什么,不看它说了什么。注入的谎话骗不到它。"
},
{
"id": "p3-35",
"scene": "P3",
- "text": "让模型帮忙看门,同时不敢把门全交给模型。"
+ "text": "让模型帮忙看门,又不敢把门全交给模型。"
},
{
"id": "p4-01",
@@ -527,7 +527,7 @@
{
"id": "p4-02",
"scene": "P4",
- "text": "接下来的麻烦所有做过工程的人都熟悉:你想加点东西。"
+ "text": "接下来的麻烦,做过工程的人都熟悉:你想给它加点东西。"
},
{
"id": "p4-03",
@@ -537,17 +537,17 @@
{
"id": "p4-04",
"scene": "P4",
- "text": "每加一样,你都得回去改那个循环。"
+ "text": "加一样,改一次循环。"
},
{
"id": "p4-05",
"scene": "P4",
- "text": "有人画过一张很扎心的图:循环体里记日志一行、查权限一行、"
+ "text": "有人画过一张很扎心的图:循环体里,记日志一行、查权限一行、"
},
{
"id": "p4-06",
"scene": "P4",
- "text": "发通知一行、自动提交又一行,加到后面那段代码已经认不出来了。"
+ "text": "发通知一行、自动提交又一行。加到后面,那段代码已经认不出来了。"
},
{
"id": "p4-07",
@@ -567,7 +567,7 @@
{
"id": "p4-10",
"scene": "P4",
- "text": "循环到了某个时机只喊一声名字,至于该跑什么,由那张表说了算。"
+ "text": "循环到了某个时机,只喊一声名字。至于该跑什么,那张表说了算。"
},
{
"id": "p4-11",
@@ -582,7 +582,7 @@
{
"id": "p4-13",
"scene": "P4",
- "text": "教学版留了四个插口,正好串成一整轮。"
+ "text": "这个小版本留了四个插口,正好串成一整轮。"
},
{
"id": "p4-14",
@@ -612,17 +612,12 @@
{
"id": "p4-19",
"scene": "P4",
- "text": "它返回一句话,这句话就被当成新输入塞回去,循环继续转。"
+ "text": "它返回一句话,这句话就被当成新输入塞回去,循环接着转。"
},
{
"id": "p4-20",
"scene": "P4",
- "text": "四个插口覆盖一整轮:进来、动手前、动手后、结束前,"
- },
- {
- "id": "p4-21",
- "scene": "P4",
- "text": "而循环只负责在这四个点上喊一声。"
+ "text": "四个插口覆盖一整轮,循环只在这四个点上喊一声。"
},
{
"id": "p4-22",
@@ -632,7 +627,7 @@
{
"id": "p4-23",
"scene": "P4",
- "text": "真实产品里这样的时机,官方文档口径三十一个,"
+ "text": "成型的那套里,这样的时机官方文档口径三十一个,"
},
{
"id": "p4-24",
@@ -642,18 +637,33 @@
{
"id": "p4-25",
"scene": "P4",
- "text": "处理器也不只是跑脚本,还能让小模型单轮判决。"
+ "text": "挂上去的也不只是脚本,还能让小模型单轮判一次。"
},
{
"id": "p4-27",
"scene": "P4",
- "text": "表态的契约值得单独记:沉默,不等于批准——"
+ "text": "这里有个契约值得单独记下来:沉默,不等于批准。"
},
{
"id": "p4-28",
"scene": "P4",
"text": "不吭声的意思是「我不作决定」,流程照走。"
},
+ {
+ "id": "p4-28a",
+ "scene": "P4",
+ "text": "想真拦住,只有一个硬信号。写错成惯用的那个失败码,"
+ },
+ {
+ "id": "p4-28b",
+ "scene": "P4",
+ "text": "闸门不响,动作照跑。"
+ },
+ {
+ "id": "p4-28c",
+ "scene": "P4",
+ "text": "第一次给循环挂扩展的人,几乎都栽在这一脚上。"
+ },
{
"id": "p4-29",
"scene": "P4",
@@ -662,7 +672,7 @@
{
"id": "p4-30",
"scene": "P4",
- "text": "它的放行,仍要穿过后面的拒绝与询问两站。"
+ "text": "这句「批了」,后面还得穿过拒绝和询问两站。"
},
{
"id": "p4-31",
@@ -672,32 +682,32 @@
{
"id": "p4-32",
"scene": "P4",
- "text": "而且这条单向,官方画成过六道闸门的流水线。"
+ "text": "官方还给权限判定画过一张图:六道闸门,一道一道过。"
},
{
"id": "p4-33",
"scene": "P4",
- "text": "教学版没有这一层,拆源码的人写下评语:"
+ "text": "这一步,开源那版根本没做。读源码的人给的评语是:"
},
{
"id": "p4-34",
"scene": "P4",
- "text": "教学里够用,生产环境就是一个安全漏洞。"
+ "text": "当例子够用,放进生产就是个安全漏洞。"
},
{
"id": "p4-35",
"scene": "P4",
- "text": "那个能说「别停」的插口本身很危险——它叫模型接着干,"
+ "text": "那个能说「别停」的插口,自己就很危险。它叫模型接着干,"
},
{
"id": "p4-36",
"scene": "P4",
- "text": "模型干完又要停,它又叫接着干,就转不出来了。"
+ "text": "模型干完又要停,它又叫接着干。转不出来了。"
},
{
"id": "p4-37",
"scene": "P4",
- "text": "产品里放了个标记,后面的轮次看到它就不再拦第二次。"
+ "text": "据他所说,它放了个标记,后面的轮次看到就不再拦。"
},
{
"id": "p5-01",
@@ -722,7 +732,7 @@
{
"id": "p5-05",
"scene": "P5",
- "text": "官方给 Harness 的定义正好四件:循环、工具、上下文管理、护栏。"
+ "text": "回到官方那四件:循环、工具、上下文管理、护栏。"
},
{
"id": "p5-06",
@@ -732,17 +742,17 @@
{
"id": "p5-07",
"scene": "P5",
- "text": "这不是我的说法,是可以数出来的。"
+ "text": "这不是我一个人的说法,是能数出来的。"
},
{
"id": "p5-08",
"scene": "P5",
- "text": "我把四章的代码逐一量过:整个程序从一百四十行出头,"
+ "text": "我把四个版本的代码逐一量过:整个程序从一百四十行出头,"
},
{
"id": "p5-09",
"scene": "P5",
- "text": "长到两百五十多行,长了八成;"
+ "text": "长到两百五十多行,整整长了八成;"
},
{
"id": "p5-10",
@@ -754,15 +764,10 @@
"scene": "P5",
"text": "骨架一字未改:调模型,看回答里有没有工具调用,执行,填回结果。"
},
- {
- "id": "p5-12",
- "scene": "P5",
- "text": "变的只有「执行」那一步怎么写——"
- },
{
"id": "p5-13",
"scene": "P5",
- "text": "从写死一个命令,到查一张表,到先过一道闸门,到交给一排插口。"
+ "text": "变的只有「执行」那一步:从写死命令,到查表,到闸门,到一排插口。"
},
{
"id": "p5-14",
@@ -787,7 +792,7 @@
{
"id": "p5-18",
"scene": "P5",
- "text": "这个项目的作者把它的作用写成一句话,我觉得是全篇最好的一句:"
+ "text": "这个项目的作者,把这层壳的作用写成了一句话。我觉得是全篇最好的:"
},
{
"id": "p5-19",
@@ -797,27 +802,17 @@
{
"id": "p6-01",
"scene": "P6",
- "text": "它敢在你的电脑上动手,不是因为它足够聪明,"
+ "text": "它敢动手,不是因为聪明。"
},
{
"id": "p6-02",
"scene": "P6",
- "text": "而是因为有人在它周围,搭了这几层结构。"
- },
- {
- "id": "p6-03",
- "scene": "P6",
- "text": "循环给它手脚,分发表给它工具,闸门守着底线,插口留给你发挥。"
+ "text": "是因为有人在它周围,一层一层,把这些结构搭了出来。"
},
{
"id": "p6-04",
"scene": "P6",
- "text": "这几层结构不神秘,加起来两百多行,一个下午能读完。"
- },
- {
- "id": "p6-05",
- "scene": "P6",
- "text": "学会给 Harness 做加减法,这本身就是一门手艺。"
+ "text": "两百多行,一下午读完。"
},
{
"id": "p6-06",
@@ -827,11 +822,16 @@
{
"id": "p6-07",
"scene": "P6",
- "text": "教学代码来自开源实现,行数均为实测,链接在简介。"
+ "text": "示例代码来自开源实现,行数均为实测,链接在简介。"
+ },
+ {
+ "id": "p6-05",
+ "scene": "P6",
+ "text": "学会给 Harness 做加减法,这本身就是一门手艺。"
},
{
"id": "p6-08",
"scene": "P6",
- "text": "下期,拆它怎么安排自己看到的东西:计划写在哪,活儿怎么分出去。"
+ "text": "下期一个反直觉的事实:它这一轮看见什么,从来不是它自己说了算。下期见。"
}
]
diff --git a/apps/negentropy-influence/episodes/claude-code-explained-video/script/narration.md b/apps/negentropy-influence/episodes/claude-code-explained-video/script/narration.md
index 16c91206..acb783d8 100644
--- a/apps/negentropy-influence/episodes/claude-code-explained-video/script/narration.md
+++ b/apps/negentropy-influence/episodes/claude-code-explained-video/script/narration.md
@@ -7,67 +7,90 @@
>
> **字数是硬约束**:`chars_per_min = 280` 是**含停顿的等效口径**(实测纯语速 301 字/分,
> 停顿开销另占约 1 分钟)——EP1 用它预测成片 14:01 只差 1%。故 14 分钟 ≈ 3900 字。
-> 各幕字数预算:P0 200 · P1 780 · P2 760 · P3 740 · P4 800 · P5 400 · P6 190。
+> 各幕字数预算(2026-08-28 评审改造后重排):P0 280 · P1 790 · P2 760 · P3 725 · P4 890 · P5 405 · P6 180。
+> P0 上浮吸收官方脚手架赌注三句,P4 上浮吸收退出码陷阱三句;P3 在补入沙箱四句与分类器成绩单两句的
+> 同时净减 17 字(删掉六句咏叹与四处同义复述),全片净 +46 字。
+>
+> **叙事框架**:全篇不做「教学版 ↔ 真实产品」的两轨对照解说,改为单主语直陈——
+> 「这套 Harness 是怎样的 → 拿一份开源小版本当例子 → 而成型的那套是怎样的」。示例是讲解的脚手架,
+> 不是与产品对立的另一极。同幕连续「官方」变换主语(官方文档 / 官方工程博客 / 他们)以避复读感;
+> 三级证据归属句(「有人读源码读出来」「据他所说」)语义不动。
+>
+> **口语化定式(2026-08-29 重铸,本文件唯一的行文纪律)**:口播是说给耳朵的,三条硬指标——
+> ① **禁书面对照名词**:「最简实现 / 实际实现 / 最简的做法」一律不用(当前 0 次)。指代示例用
+> 「玩具版 / 这个小版本 / 这份代码 / 开源那版」,指代产品用「成型的那套 / 真的那个 / 它」,
+> 同一说法全片 ≤2 次,超了就换。
+> ② **口播行禁破折号**:`——` 是书面修辞,口播零容忍(当前口播 0 处;画面备注与角标不受限)。
+> 拆成两句、换冒号、或直接删——短句收尾比长句拖尾更有力。
+> ③ **长短交错**:每幕至少 2 处 ≤12 字的短句爆点,落在转折/金句/惊讶处;禁连续中长句排比成朗读腔。
+> 单句 6–35 字(下限 6 是为短句留出的空间,不是鼓励凑字)。
+>
+> 本轮 167 句不增删、句 id 与句序零变动,94 句重铸,净 −25 字(4046 → 4021),
+> 94 句待重配音(TTS 缓存按文本哈希,其余 73 句命中缓存)。
## P0 你在当那个中间人
> 画面:终端窗口,光标闪烁。用户提问逐字打出,模型回一条命令后画面凝住。
> 随后「复制 → 粘贴 → 复制 → 粘贴」三轮,把画面塞满灰色的搬运痕迹。
-- [p0-01] 你让 AI 跑个脚本:它把命令写好,然后就停在那儿了。
-- [p0-02] 复制、粘贴、回车、再把输出贴回去——每一轮的搬运工,是你。
-- [p0-03] 从「写出命令」到「真的跑起来」,缺的那一层,官方起了名字。
+- [p0-01] 你让 AI 跑个脚本。它把命令写得漂漂亮亮的,然后就停在那儿了。
+- [p0-02] 复制、粘贴、回车,再把输出贴回去。每一轮的搬运工,是你。
+- [p0-03] 能写出命令,和真的跑起来,中间隔着一层。这一层,官方起过名字。
+- [p0-03a] 这一层到底有多要紧?他们自己的评测战报里,写过一句话。
+- [p0-03b] 同一个底层模型,换一套脚手架,成绩能差出一大截。
+- [p0-03c] 你出多少力,它得多少分。
- [p0-04] 名字就叫 Harness:把动力源,套进一个可控的结构里。
-- [p0-05] 官方说得直白:这套东西是 Harness,模型装在 Harness 里面。
-- [p0-06] Harness 供给它五样东西:文件、命令、权限、记忆、护栏。
-- [p0-07] 这集就把这个 Harness,一层一层拆开看。
+- [p0-05] 官方自己说得很直白:这套东西就是 Harness,模型装在里面。
+- [p0-06] Harness 给它四件:一个循环、一套工具、上下文管理、一道护栏。
+- [p0-07] 这集,就一层一层拆开它。
## P1 一个循环,就是全部
> 画面:陶土橙闭合轨道成形(全片恒定视觉锚)。角标:Agent Loop · "One Loop Is All You Need"
-- [p1-01] 先说结论:那一层的核心是一个循环,像一条不停转的传送带。
+- [p1-01] 先说结论。核心就是一个循环,像一条不停转的传送带。
- [p1-02] 它只做一件事:反复去问模型,接下来干什么。
-- [p1-03] 每问一次,就看模型的回答里有没有夹着一次工具调用。
-- [p1-04] 有,就说明模型举手了,它要用工具。
-- [p1-05] 那就替它把工具跑一遍,把结果原样塞回对话里,再问下一轮。
-- [p1-06] 没有,就说明它觉得活干完了,循环停机,把话交给你。
-- [p1-07] 就这么两个信号,决定这条传送带转不转。
+- [p1-03] 每问一次,就翻开它的回答,看里面有没有夹着一次工具调用。
+- [p1-04] 有,模型举手了。
+- [p1-05] 那就替它把这件工具跑一遍,结果原样塞回对话里,然后接着问下一轮。
+- [p1-06] 没有,就是它觉得活干完了。循环停机,把话交还给你。
> 角标:`tool_use` block · 「有工具调用 → 继续 / 没有 → 退出」
-- [p1-08] 把这件事翻成代码,只有五步。
-- [p1-09] 先把你的问题放进消息列表,再连同「我有哪些工具」发给模型;
-- [p1-10] 把模型的回答追加进列表,挑出里面所有的工具调用;
-- [p1-11] 一个都没有就地返回,有就逐个执行、收好每条结果;
-- [p1-12] 最后把结果打包成新消息追加进去,回到第二步。写完了。
-- [p1-13] 有人给它的评价是:三十多行,最小可运行的内核。
-- [p1-14] 我自己数过这一版:光循环那个函数,去掉空行和注释,二十三行。
+- [p1-08] 翻成代码,只有五步。
+- [p1-09] 先把你的问题放进消息列表,再连同「我有哪些工具」一起发给模型;
+- [p1-10] 把模型的回答追加进列表,再挑出里面所有的工具调用;
+- [p1-11] 一个都没有,就地返回;有,就逐个执行,收好每条结果;
+- [p1-12] 最后把结果打包成一条新消息追加进去,回到第二步。写完了。
+- [p1-12a] 说明一下:有人照着这套机制,写过一个开源的小版本。
+- [p1-12b] 下面就拿它当例子,边读边说真正的做法。
+- [p1-13] 有人给它的评价是:三十多行,一个最小的、能跑的内核。
+- [p1-14] 我自己数了一遍:光循环那个函数,去掉空行和注释,二十三行。
> 角标:实测 `agent_loop` 非空非注释 23 行 @ pinned commit(口径与取数见 sources.toml)
- [p1-15] 分工也说清楚了:模型决定要不要用工具、用哪一个,
-- [p1-16] 脚手架负责真的去跑、把结果搬回来——判断力在那边,手脚在这边。
-- [p1-17] 模型回话时会附一个「我为什么停下来」的标记。
-- [p1-18] 最直觉的写法是直接看它;但教学版改了,
-- [p1-19] 改成翻开回答本身,看里面有没有工具调用。为什么?
+- [p1-16] 壳负责真的去跑,把结果搬回来。判断力在那边,手脚在这边。
+- [p1-17] 模型每次回话,都会附一个「我为什么停下来」的标记。
+- [p1-18] 最直觉的写法就是直接看这个标记。可这份代码偏不这么干。
+- [p1-19] 它翻开回答本身,去看里面到底有没有工具调用。为什么这么绕?
- [p1-20] 有人拆过它的源码,他给的理由是:
- [p1-21] 回答是一个字一个字流出来的,工具调用早就流出来了,
-- [p1-22] 那个标记却可能还没更新——他说源码注释写得很直白,这标记不可靠。
-- [p1-23] 所以真正的做法是一边流一边看,见到工具调用就记下还得再来一轮。
+- [p1-22] 那个标记却可能还没更新。他说源码注释写得很直白:这东西不可靠。
+- [p1-23] 所以真正的做法是一边流一边看。见到工具调用,就记下还得再来一轮。
- [p1-24] 别听它说「我说完了」,去看它手里还有没有活。
> 金句卡(serif):「别听它说『我说完了』,去看它手里还有没有活。」
> 角标:`stop_reason` is unreliable —— 第三方的源码分析
-- [p1-25] 官方给这个循环画过像:三件事交融着转——
-- [p1-26] 收集上下文、采取行动、验证结果,不是硬阶段。
+- [p1-25] 官方给这个循环画过像:三件事交融着转。
+- [p1-26] 收集上下文、采取行动、验证结果,它们不是硬阶段。
- [p1-27] 而且你随时可以打断它、补一句、让它改道。
-- [p1-28] 教学版身上只带一份消息列表;真实产品带着十样,
-- [p1-29] 压缩进度、补救次数、钩子拦没拦过停机、轮数——细节进角标。
-- [p1-30] 循环没长个,长出来的是它随身带的状态。
-- [p1-31] 退出的路子也有一大把:报错、中断、钩子叫停、预算烧完。
-- [p1-32] 但它们全是保护装置,不是骨架;骨架还是那二十来行。
+- [p1-28] 玩具版身上只带一份消息列表。成型的那套呢?整整带了十样:
+- [p1-29] 压缩进度、补救次数、钩子拦没拦过停机、轮数。细节都进角标。
+- [p1-30] 循环没长个。长出来的是它随身带的那些状态。
+- [p1-31] 退出的路子还有一大把:报错、中断、钩子叫停、预算烧完。
+- [p1-32] 可它们全是保护装置,不是骨架。骨架还是那二十来行。
## P2 加一个工具,只改一行
@@ -75,44 +98,43 @@
> 角标:Tool Use · "Add a Tool, Add Just One Line"
- [p2-01] 刚才那个循环,手里只有一件工具:一个能跑命令的壳。
-- [p2-02] 想读文件,它得拼出一条读文件的命令;想写文件,得拼一条重定向。
-- [p2-03] 模型心里想的是「读这个文件」,嘴上却要翻译成一条命令行。
-- [p2-04] 多这一层翻译,费字数,还容易拼错。
-- [p2-05] 那就把常用动作直接做成工具,于是从一件变成五件:
+- [p2-02] 想读文件,它得拼出一条读文件的命令。想写文件,再拼一条重定向。
+- [p2-03] 模型心里想的是「读这个文件」,嘴上却得翻成一条命令行。
+- [p2-05] 那就干脆把常用动作直接做成工具。于是一件变成了五件:
- [p2-06] 跑命令、读文件、写文件、改文件、按模式找文件。
-- [p2-07] 问题是循环怎么知道该调哪一个?答案朴素得让人意外:查一张表。
+- [p2-07] 可循环怎么知道该调哪一个?答案朴素得让人意外:查一张表。
- [p2-08] 一张字典,左边写工具的名字,右边写对应的处理函数。
- [p2-09] 像前台的转接号码簿:来电报个名字,前台照着簿子转接。
-- [p2-10] 于是加一个新工具只要两件事:在清单里加一条描述,在表里加一行。
-- [p2-11] 循环那边呢?我把两个版本逐行比过,
-- [p2-12] 实质改动只有一行——原来写死了去跑命令,现在改成查表。
+- [p2-10] 加一个新工具就两件事:清单里加一条描述,表里加一行。
+- [p2-11] 那循环那边呢?我把前后两个版本逐行比过。
+- [p2-12] 实质改动只有一行。原来写死了去跑命令,现在改成查表。
- [p2-13] 换掉的是「执行」这一个动作,传送带一寸没动。
> 角标:实测 diff —— 唯一实质变更即 `run_bash(...)` → 查表调用
> 金句卡:「换掉的是执行那一步,传送带一寸没动。」
- [p2-14] 接着来了个新麻烦:模型经常一次要好几个工具。
-- [p2-15] 教学版老实排队,一个一个来;真实产品里能并行的就并行。
-- [p2-16] 而它判断能不能并行的方式,是这一集我最想让你记住的一处。
+- [p2-15] 玩具版是老实排队,一个一个来。真的那个呢?能并行就并行。
+- [p2-16] 而它怎么判断能不能并行?这是这一集我最想让你记住的一处。
- [p2-17] 直觉会说:只读的可以一起跑,要写的不行。
-- [p2-18] 但按第三方的源码分析,这两件事其实不是一回事。
+- [p2-18] 但有人读源码读出来:这两件事根本不是一回事。
- [p2-19] 同一个跑命令的工具,让它列个目录算能并行,让它删个文件就不能。
-- [p2-20] 同一件工具,同一个名字,结论相反。
-- [p2-21] 因为判断的不是这件工具,是这一次具体要干什么。
+- [p2-20] 同一件工具,结论相反。
+- [p2-21] 判断的根本不是这件工具,是这一次具体要干什么。
- [p2-22] 反过来还有更绕的:有的工具明明在改东西,却可以并行,
-- [p2-23] 原因是它每次改的都是不同的文件,谁也不挡谁。
+- [p2-23] 因为它每次改的都是不同的文件,谁也不挡谁。
- [p2-24] 所以「能不能一起跑」跟「是不是只读」,是两条独立的判断。
> 金句卡(serif):「『能不能一起跑』跟『是不是只读』,是两条独立的判断。」
> 角标:`isConcurrencySafe` ≠ `isReadOnly` —— 第三方的源码分析
-- [p2-25] 判断完了怎么排?按原来的顺序切段:连着能并行的划成一组一起跑,
+- [p2-25] 判断完了怎么排?按原来的顺序切段:连着能并行的划成一组,一起跑,
- [p2-26] 中间夹着不能并行的就单独一组;组内并行,组间严格按顺序。
-- [p2-27] 他还提到,工具输出太长时产品不会全喂给模型,而是存成文件,
-- [p2-28] 只给它一段预览和一个路径。
+- [p2-27] 他还提到一件事:工具输出太长,它不会全喂给模型,而是存成文件,
+- [p2-28] 只给一段预览和一个路径。
- [p2-29] 但「读文件」这件工具被特别开了口子:输出永远不许存成文件。
- [p2-30] 因为一旦存了,模型下次去读那个文件,输出又太长,又被存成文件。
-- [p2-31] 读、存、再读、再存,它会自己套住自己,转不出来。
+- [p2-31] 读、存、再读、再存。它自己把自己套住了,转不出来。
## P3 执行之前,先过闸门
@@ -120,45 +142,45 @@
> 角标:Permission · "Check Permissions Before Execution"
- [p3-01] 现在这个 AI 有五件工具,其中一件能跑任意命令。
-- [p3-02] 读写文件那几件都被拦在工作目录里,但跑命令那件,没人管它。
+- [p3-02] 读写文件那几件,都被拦在工作目录里。跑命令那件,没人管。
- [p3-03] 你随口说一句「帮我把项目清理一下」,
-- [p3-04] 它完全可能给你递上一条把整块盘删干净的命令,
-- [p3-05] 而按前面的设计,循环会老老实实替它执行。
+- [p3-04] 它完全可能给你递上一条把整块盘删干净的命令。循环照跑。
- [p3-06] 官方把话说死了:权限由这层代码执行,不由模型执行。
-- [p3-07] 拦法是三道闸门,顺序写死。
+- [p3-07] 三道闸门,顺序写死。
- [p3-08] 第一道是一张硬拒绝表:删根目录、提权、关机、格式化。
-- [p3-09] 命中就直接挡回去,连问都不问。
+- [p3-09] 命中就挡回去,问都不问。
- [p3-10] 第二道是规则匹配,管「看情况」的操作:
-- [p3-11] 读写工作目录之外的东西,或者命令里带着删除、改权限这类动作。
-- [p3-12] 第三道就是问你:画面停下来,把工具名和参数摆给你看,等你点头。
-- [p3-13] 三道都没命中呢?兜底永远再问你一句——弃权不等于放行。
-- [p3-14] 教学版里有一句很诚实的话,我想念出来。
-- [p3-15] 它说,这张拒绝表用的是最简单的字符串匹配,
-- [p3-16] 它的作用是告诉你「闸门应该装在哪儿」,
-- [p3-17] 而不能被当成一道真正的安全边界——
-- [p3-18] 因为同一件坏事有无数种写法,换个拼法就绕过去了。
-- [p3-19] 一个教学项目愿意在自己的示例代码旁写下这句话,挺难得。
-
-> 金句卡(serif):「它告诉你闸门装在哪儿,不是替你把门守住。」
-
-- [p3-20] 那真实产品里的门长什么样?
-- [p3-21] 按第三方的源码分析,结论不是三种,是四种。
-- [p3-23] 多出来的第四种最有意思:工具选择不表态。
-- [p3-24] 也就是说,弃权不等于放行,兜底永远是问你一句。
-- [p3-25] 规则求值的顺序官方也写死了:拒绝、询问、放行,
-- [p3-26] 头一个命中就出局——规则写得多具体,都改变不了顺序。
-- [p3-27] 所以一条宽拒绝,靠一条窄放行开不出例外。
+- [p3-11] 读写工作目录之外的东西;或者命令里带着删除、改权限这类动作。
+- [p3-12] 第三道就是问你:画面停住,把工具名和参数摆给你看,等你点头。
+- [p3-14] 这份代码自己也承认:拒绝表只是字符串匹配,换个拼法就绕过去。
+- [p3-15] 它告诉你闸门装在哪儿,不替你把门守住。
+
+> 金句卡(serif):「它告诉你闸门装在哪儿,不是替你把门守住。」(与 p3-15 音画同句)
+
+- [p3-19a] 真正的边界在更底下一层。跑命令那件工具,被套进了一层沙箱。
+- [p3-19b] 能写哪些目录、能走哪个网络出口,由操作系统说了算。
+- [p3-19c] 规则是劝,沙箱是锁。两道墙少一道,笼子就不成立。
+- [p3-19d] 官方工程博客给了数:内部用下来权限提示少八成四。
+
+> 角标:Seatbelt / bubblewrap + socat · 文件系统与网络双边界 · 权限提示 −84%(官方工程博客,取数 2025-10)
+
+- [p3-20] 回到闸门这边。有人读源码读出来:判出来是四种,不是三种。
+- [p3-23] 多出来的第四种最有意思:这件工具选择不表态。
+- [p3-24] 也就是说,弃权不等于放行。兜底永远是问你一句。
+- [p3-25] 规则是一条条往下判的,顺序官方也写死了:拒绝、询问、放行。
+- [p3-26] 头一个命中就出局。规则写得再具体,也改不了顺序。
- [p3-28] 裸名拒绝更狠:整件工具从模型身上直接消失。
-- [p3-29] 就算全放行模式,也压不住一条写死的拒绝。
+- [p3-29] 就算你开了全放行模式,也压不住一条写死的拒绝。
> 角标:user < project < local < flag < policy(+ cliArg / command / session)——第三方的源码分析
-- [p3-30] 全自动模式不靠你点头的次数,靠另一个模型当闸门。
+- [p3-30] 全自动模式不靠你点头的次数,靠另一个模型来当闸门。
- [p3-31] 官方遥测补一刀:权限提示,九成三都被点了批准。
-- [p3-32] 审判者的视野是做减法的:只看用户消息和裸命令。
-- [p3-33] 模型的自我辩解、工具的输出,全被剥在视野外。
-- [p3-34] 评它做了什么,不看它说了什么——注入的谎话骗不到它。
-- [p3-35] 让模型帮忙看门,同时不敢把门全交给模型。
+- [p3-31a] 这个当闸门的模型,自己也进过考场。
+- [p3-31b] 快筛误杀率百分之八点五,复核之后压到千分之四。
+- [p3-32] 这个审判者的视野是做减法的:只看用户消息和裸命令。
+- [p3-34] 评它做了什么,不看它说了什么。注入的谎话骗不到它。
+- [p3-35] 让模型帮忙看门,又不敢把门全交给模型。
## P4 挂在循环上,不写进循环里
@@ -166,51 +188,53 @@
> 角标:Hooks · "Hang on the Loop, Don't Write into It"
- [p4-01] 到这一步,这个 AI 已经能安全地动手了。
-- [p4-02] 接下来的麻烦所有做过工程的人都熟悉:你想加点东西。
+- [p4-02] 接下来的麻烦,做过工程的人都熟悉:你想给它加点东西。
- [p4-03] 比如每次跑命令都记一笔日志,或者某些操作做完自动提交一次代码。
-- [p4-04] 每加一样,你都得回去改那个循环。
-- [p4-05] 有人画过一张很扎心的图:循环体里记日志一行、查权限一行、
-- [p4-06] 发通知一行、自动提交又一行,加到后面那段代码已经认不出来了。
+- [p4-04] 加一样,改一次循环。
+- [p4-05] 有人画过一张很扎心的图:循环体里,记日志一行、查权限一行、
+- [p4-06] 发通知一行、自动提交又一行。加到后面,那段代码已经认不出来了。
- [p4-07] 原话说得很准:你想扩展的是它的行为,你改的却是循环本身。
> 金句卡(serif):「你想扩展的是它的行为,你改的却是循环本身。」
- [p4-08] 正确的做法是把循环留成稳定的内核,让扩展挂在外面。
- [p4-09] 具体做法又是一张表:左边是「时机」,右边是这个时机要跑的函数。
-- [p4-10] 循环到了某个时机只喊一声名字,至于该跑什么,由那张表说了算。
+- [p4-10] 循环到了某个时机,只喊一声名字。至于该跑什么,那张表说了算。
- [p4-11] 像墙上的插线口:循环是插座,功能是插头。
- [p4-12] 加功能是插一个插头,不是重新布线。
-- [p4-13] 教学版留了四个插口,正好串成一整轮。
+- [p4-13] 这个小版本留了四个插口,正好串成一整轮。
- [p4-14] 第一个,你的话交出去、还没进模型之前:校验输入,或者补点背景。
- [p4-15] 第二个,工具要执行之前。刚才那三道闸门就搬到了这个插口上,
- [p4-16] 循环从此不再认识任何一个检查函数。
- [p4-17] 第三个,工具执行完、还没进下一轮之前:收尾的副作用挂这儿。
- [p4-18] 第四个,循环准备停机之前。这个插口最有权力:
-- [p4-19] 它返回一句话,这句话就被当成新输入塞回去,循环继续转。
-- [p4-20] 四个插口覆盖一整轮:进来、动手前、动手后、结束前,
-- [p4-21] 而循环只负责在这四个点上喊一声。
+- [p4-19] 它返回一句话,这句话就被当成新输入塞回去,循环接着转。
+- [p4-20] 四个插口覆盖一整轮,循环只在这四个点上喊一声。
- [p4-22] 有句话我很喜欢:这个循环是故意保持无聊的。
> 金句卡(serif):「这个循环是故意保持无聊的。」
-- [p4-23] 真实产品里这样的时机,官方文档口径三十一个,
+- [p4-23] 成型的那套里,这样的时机官方文档口径三十一个,
- [p4-24] 分三种节奏:会话一次、回合一次、每次调用前后。
-- [p4-25] 处理器也不只是跑脚本,还能让小模型单轮判决。
-- [p4-27] 表态的契约值得单独记:沉默,不等于批准——
+- [p4-25] 挂上去的也不只是脚本,还能让小模型单轮判一次。
+- [p4-27] 这里有个契约值得单独记下来:沉默,不等于批准。
- [p4-28] 不吭声的意思是「我不作决定」,流程照走。
+- [p4-28a] 想真拦住,只有一个硬信号。写错成惯用的那个失败码,
+- [p4-28b] 闸门不响,动作照跑。
+- [p4-28c] 第一次给循环挂扩展的人,几乎都栽在这一脚上。
- [p4-29] 插口上的函数说「这个我批了」,就真的放行吗?不。
-- [p4-30] 它的放行,仍要穿过后面的拒绝与询问两站。
+- [p4-30] 这句「批了」,后面还得穿过拒绝和询问两站。
- [p4-31] 方向是单向的:扩展点能加限制,不能解除限制。
-- [p4-32] 而且这条单向,官方画成过六道闸门的流水线。
-- [p4-33] 教学版没有这一层,拆源码的人写下评语:
-- [p4-34] 教学里够用,生产环境就是一个安全漏洞。
+- [p4-32] 官方还给权限判定画过一张图:六道闸门,一道一道过。
+- [p4-33] 这一步,开源那版根本没做。读源码的人给的评语是:
+- [p4-34] 当例子够用,放进生产就是个安全漏洞。
> 金句卡(serif):「扩展点能加限制,不能解除限制。」
> 角标:hook `allow` 不能绕过 settings 的 deny/ask —— 第三方的源码分析
-- [p4-35] 那个能说「别停」的插口本身很危险——它叫模型接着干,
-- [p4-36] 模型干完又要停,它又叫接着干,就转不出来了。
-- [p4-37] 产品里放了个标记,后面的轮次看到它就不再拦第二次。
+- [p4-35] 那个能说「别停」的插口,自己就很危险。它叫模型接着干,
+- [p4-36] 模型干完又要停,它又叫接着干。转不出来了。
+- [p4-37] 据他所说,它放了个标记,后面的轮次看到就不再拦。
## P5 四层叠起来是什么
@@ -221,15 +245,14 @@
- [p5-02] 第一层,一个循环:反复问模型,替它执行,把结果搬回来。
- [p5-03] 第二层,一张分发表:让工具可以随便加。
- [p5-04] 第三层,三道闸门:让危险的事在执行之前被拦住。
-- [p5-05] 官方给 Harness 的定义正好四件:循环、工具、上下文管理、护栏。
+- [p5-05] 回到官方那四件:循环、工具、上下文管理、护栏。
- [p5-06] 这集填上了三件,上下文管理那格先空着,留到后面拆。
-- [p5-07] 这不是我的说法,是可以数出来的。
-- [p5-08] 我把四章的代码逐一量过:整个程序从一百四十行出头,
-- [p5-09] 长到两百五十多行,长了八成;
+- [p5-07] 这不是我一个人的说法,是能数出来的。
+- [p5-08] 我把四个版本的代码逐一量过:整个程序从一百四十行出头,
+- [p5-09] 长到两百五十多行,整整长了八成;
- [p5-10] 而那个循环函数,一直在二十到二十八行之间。
- [p5-11] 骨架一字未改:调模型,看回答里有没有工具调用,执行,填回结果。
-- [p5-12] 变的只有「执行」那一步怎么写——
-- [p5-13] 从写死一个命令,到查一张表,到先过一道闸门,到交给一排插口。
+- [p5-13] 变的只有「执行」那一步:从写死命令,到查表,到闸门,到一排插口。
- [p5-14] 同一个位置,换了四次写法。
> 金句卡(serif):「骨架一字未改,变的只有『执行』那一步怎么写。」
@@ -238,7 +261,7 @@
- [p5-15] 所以再回到开头那个问题:
- [p5-16] 从能写出一条命令,到真的在你电脑上跑起来,缺的那一层是什么?
- [p5-17] 就是这个:一个愿意反复跑、能查表、会拦门、留了插口的壳。
-- [p5-18] 这个项目的作者把它的作用写成一句话,我觉得是全篇最好的一句:
+- [p5-18] 这个项目的作者,把这层壳的作用写成了一句话。我觉得是全篇最好的:
- [p5-19] 能动性来自模型,而这层壳,给能动性一个落地的地方。
> 金句卡(serif,英文原文并列):
@@ -249,11 +272,10 @@
> 画面:信源卡(官方文档 + 取数日期 / 第三方源码分析已逐处标注 / 数字均为实测口径)→ 系列身份卡 → 渐黑。
> 渐黑窗口从**末 beat** 总时长推导(不是末句),见 skills/06 红线四。
-- [p6-01] 它敢在你的电脑上动手,不是因为它足够聪明,
-- [p6-02] 而是因为有人在它周围,搭了这几层结构。
-- [p6-03] 循环给它手脚,分发表给它工具,闸门守着底线,插口留给你发挥。
-- [p6-04] 这几层结构不神秘,加起来两百多行,一个下午能读完。
-- [p6-05] 学会给 Harness 做加减法,这本身就是一门手艺。
+- [p6-01] 它敢动手,不是因为聪明。
+- [p6-02] 是因为有人在它周围,一层一层,把这些结构搭了出来。
+- [p6-04] 两百多行,一下午读完。
- [p6-06] 本片机制依据官方文档,取数2026年8月,以官方最新表述为准。
-- [p6-07] 教学代码来自开源实现,行数均为实测,链接在简介。
-- [p6-08] 下期,拆它怎么安排自己看到的东西:计划写在哪,活儿怎么分出去。
+- [p6-07] 示例代码来自开源实现,行数均为实测,链接在简介。
+- [p6-05] 学会给 Harness 做加减法,这本身就是一门手艺。
+- [p6-08] 下期一个反直觉的事实:它这一轮看见什么,从来不是它自己说了算。下期见。
diff --git a/apps/negentropy-influence/episodes/claude-code-explained-video/script/storyboard.md b/apps/negentropy-influence/episodes/claude-code-explained-video/script/storyboard.md
index e23b8423..4111b0ff 100644
--- a/apps/negentropy-influence/episodes/claude-code-explained-video/script/storyboard.md
+++ b/apps/negentropy-influence/episodes/claude-code-explained-video/script/storyboard.md
@@ -6,24 +6,27 @@
> `mech` 石青(外挂机制)· `deny` 警示红(拒绝)。
> **镜号必须与 `scenes/*.tsx` 里 `
` 逐字一致**(抽帧 QA 对照用)。
>
-> 注:p3-22 与 p4-26 在定稿时删除,句号留空档(不重编号,保 TTS 缓存);区间按 narration.json
-> 的**实际顺序**展开,故 `p3-20..24` 覆盖 20/21/23/24 四句。
+> 注:句号留空档是常态(不重编号,保 TTS 缓存)——定稿期删了 p3-22 / p4-26,评审改造期又删了
+> p1-07 / p2-04 / p3-05 / p3-13 / p3-16..19 / p3-21 / p3-27 / p3-33 / p4-21 / p5-12 / p6-03;
+> 新增句一律用字母后缀(p0-03a..c / p1-12a..b / p3-19a..d / p3-31a..b / p4-28a..c)。
+> 区间按 narration.json 的**实际顺序**展开,故 `p3-20..24` 覆盖 20/23/24 三句,
+> `p6-06..08` 覆盖重排后的 06/07/05/08 四句(P6 改序:免责两句 → 金句 → 预告)。
## P0 你在当那个中间人
| 镜 | 句区间 | 画面 | 动效 |
|---|---|---|---|
| 0-A | p0-01..02 | 全屏终端窗口(`panel` 底 + `panelBorder` 框),顶栏三个灰点。命令打完光标变灰凝住;右侧对话框浮出 | 终端打字机(每字 2 帧);命令打完后光标**停止闪烁并变灰**,画面凝住 0.6s;p0-02 三轮「复制→粘贴→回车→回贴」搬运弧线**加速**塞满灰色残影(节奏比旧版快一倍) |
-| 0-B | p0-03..05 | 全部搬运残影瞬间收束成一条外框线,包住一枚「模型」内核——**命名帧**:外框即Harness | 外框自四面向内合拢包住内核(spring);「Harness」二字落框(serif 大字),英文 agentic harness 进角标;p0-05 官方引文条压上("Claude Code is the harness, Claude is the model" 中英并列淡入) |
-| 0-C | p0-06..07 | Harness供给图:外框上长出五根辐条(文件 / 命令 / 权限 / 记忆 / 护栏),内侧连着模型内核 | p0-06 五根辐条逐根点亮(`mech` 辉光各 8 帧);「记忆」那根先灰并浮「后面拆」小标;p0-07 外框整体轻旋一次,片名标题卡压入(`core` 色细线自中心向两侧生长) |
+| 0-B | p0-03..05 | 全部搬运残影瞬间收束成一条外框线,包住一枚「模型」内核——**命名帧**:外框即Harness。p0-03a..c 期间底部(bottom:210,避让字幕带)浮出**官方赌注卡**:中文引句 +双分数条 | p0-03a 赌注卡自底浮出(`FadeUp`);p0-03b 两条 `core` 横条在 320px 轨道上 `interpolate` 增长(0→22% / 0→49%),mono 角标「SWE-bench Verified 500 题 · 同脚手架代际 22%→49% · 官方自报 2025-01-06」;p0-03c 分数条定格并压「你的力气 = 它的分数」;外框合拢 spring **改锚 p0-04**(「名字就叫」句才落框,消除头空转);「Harness」二字落框(serif 大字),英文 agentic harness 进角标;p0-05 官方引文条压上("Claude Code is the harness, Claude is the model" 中英并列淡入) |
+| 0-C | p0-06..07 | Harness四件套图:外框上长出**四根辐条**(循环 / 工具 / 上下文管理 / 护栏),内侧连着模型内核。辐点均布(`fx = CX − 225 + i*150`) | p0-06 四根辐条逐根点亮(`mech` 辉光各 8 帧),副标依次「问模型·执行·填回」/「查表分发」/ 灰置 +「后面单独拆」/「闸门·沙箱·插口」;「上下文管理」那根**先灰**(与 5-A 标尺空格、p5-06 承诺同构);p0-07 外框整体轻旋一次,片名标题卡压入(`core` 色细线自中心向两侧生长) |
## P1 一个循环,就是全部
| 镜 | 句区间 | 画面 | 动效 |
|---|---|---|---|
-| 1-A | p1-01..07 | **环形循环**(全片视觉锚):`core` 色闭合轨道居中,轨道上四个节点「问模型 / 看回答 / 执行工具 / 填回结果」。右侧分出一支「停机」出口 | 轨道用 `interpolate` 描线成形(0→1,1.2s);一个 `core` 光点沿轨匀速巡游(周期 2.5s);讲到「有」时工具节点 `mech` 辉光脉冲一次并让光点继续,讲到「没有」时光点滑出到「停机」出口并定格 |
+| 1-A | p1-01..06 | **环形循环**(全片视觉锚):`core` 色闭合轨道居中,轨道上四个节点「问模型 / 看回答 / 执行工具 / 填回结果」。右侧分出一支「停机」出口 | 轨道用 `interpolate` 描线成形(0→1,1.2s);一个 `core` 光点沿轨匀速巡游(周期 2.5s);讲到「有」时工具节点 `mech` 辉光脉冲一次并让光点继续,讲到「没有」时光点滑出到「停机」出口并**定格收镜**(原 p1-07 已删,语义收在这一格上) |
| 1-B | p1-08..12 | 环形循环缩至左侧 40%,右侧展开五步编号列表 + 一个 `messages[]` 面板 | 五步逐句点亮(每句一条,`FadeUp` 12px);每点亮一步,`messages[]` 面板**长高一格**并压入一个色块(用户句=dim / 模型回答=core / 工具结果=mech);第五步点亮后一条弧线从列表尾弹回第二步 |
-| 1-C | p1-13..16 | 全屏代码卡:`agent_loop` 的真实骨架(mono,约 12 行精简版),右下角计数「23」 | 代码逐行渲染(每行 3 帧);「二十三行」时行号列整体 `core` 辉光一次,计数器从 0 滚动到 23;p1-15/16 时画面左右分屏——左「模型:决定」右「脚手架:执行」,中间一条竖线 |
+| 1-C | p1-12a..p1-16 | 全屏代码卡:`agent_loop` 的真实骨架(mono,约 12 行精简版),右下角计数「23」。卡眉挂一条**来源标签条**「开源最小实现 · 照着同一套机制搭的」 | p1-12a 标签条自卡眉滑入(`mech` 描边,无具名信息)→ p1-12b 右侧浮出「边读边说:实际实现」半亮列;代码逐行渲染(每行 3 帧);「二十三行」时行号列整体 `core` 辉光一次,计数器从 0 滚动到 23;p1-15/16 时画面左右分屏——左「模型:决定」右「脚手架:执行」,中间一条竖线 |
| 1-D | p1-17..24 | 上半:模型回答的字符流从左向右逐字流出,其中一段 `mech` 色的「工具调用」块提前出现;下半:一个「停止原因」标记牌仍显示「进行中」,滞后一拍才翻牌 | 字符流以 12 字/秒流动;工具调用块出现时 `mech` 辉光;标记牌**延迟 25 帧**才翻转(视觉化「不可靠」);p1-22 时标记牌打上 `deny` 色斜叉;p1-24 金句卡 `serif` 居中淡入 |
| 1-E | p1-25..29 | **三相环升级**:主环上浮现三个相位标签(收集 / 行动 / 验证)交融旋转;一只「手」图标随时可插入环上任意点;右侧十格抽屉墙以角标清单形式一闪 | 三个相位标签沿环依次点亮并**保持同时可见**(交融感,非硬阶段);p1-27 手图标落向环上一节点、环上光点被拨偏一档(人可打断);p1-28/29 十格抽屉墙快速亮起(20ms/格)后整体退至 40% 亮度、State 字段清单进角标 |
| 1-F | p1-30..32 | 环形循环的「停机」出口分岔成五条细支线,各标一个退出原因;随后五条支线淡为 `dim`,只留主环 | 支线逐条以 `interpolate` 描线(每句一条);p1-32 时五条支线亮度降到 30% 并后移,主环 `core` 亮度不变——「它们是保护装置,不是骨架」 |
@@ -32,7 +35,7 @@
| 镜 | 句区间 | 画面 | 动效 |
|---|---|---|---|
-| 2-A | p2-01..04 | 环形循环留在左上角(缩小但**同色同线宽**)。主体是终端:模型的意图气泡「读这个文件」旁边,笨拙地拼出一条命令 | 意图气泡与命令行之间画一条「翻译」箭头,箭头上叠一个 `deny` 色的小计数「+1 层」;p2-02 打出两条拼接命令(读/写各一条);p2-04 时命令里一个字符被高亮成 `deny` 并抖动(拼错) |
+| 2-A | p2-01..03 | 环形循环留在左上角(缩小但**同色同线宽**)。主体是终端:模型的意图气泡「读这个文件」旁边,笨拙地拼出一条命令 | 意图气泡与命令行之间画一条「翻译」箭头,箭头上叠一个 `deny` 色的小计数「+1 层」;p2-02 打出两条拼接命令(读/写各一条);p2-03 尾(`+22` 帧)命令里一个字符被高亮成 `deny` 并抖动(拼错),角标「多一层翻译:费 token,还容易拼错」——原 p2-04 已删,语义由角标承载 |
| 2-B | p2-05..06 | 五张工具卡横排弹出(panel 底 + 编号,反枚举)+ 卡下官方五类分区标注;两个虚线空槽(执行/网页——官方地图另有) | 卡片 spring 弹入(仅编号染 mech);分区小字浮现;两空槽淡入(`panelBorder` 虚线);角标「官方内置五类:文件/搜索/执行/网页/代码智能」 |
| 2-C | p2-07..10 | **字典分发表**:两列表格,左列工具名(mono),右列处理函数名。旁边一个前台号码簿的极简图形 | p2-07 表格框线描出;p2-08 逐行填入(每行 4 帧);p2-09 一个「来电」光点从左飞入命中某一行 → 整行推入 `mech` 辉光 → 右列函数名弹出;p2-10 表格底部**多出一行空槽**,一条新映射滑入落槽 |
| 2-D | p2-11..13 | 左右并置两段代码(前后两版),仅执行那一行不同 | 两段代码同时淡入,**逐行做同色对齐比对**;唯一不同的那一行左侧打 `mech` 竖标并整行高亮,其余行降到 40% 亮度;p2-13 时左上角环形循环 `core` 脉冲一次(强调"它没动") |
@@ -45,13 +48,14 @@
| 镜 | 句区间 | 画面 | 动效 |
|---|---|---|---|
-| 3-A | p3-01..06 | 五张工具卡回归,其中四张外面套着一个 `mech` 色的「工作目录」框,跑命令那张**在框外** | p3-02 框线描出并把四张卡收进去,第五张卡留在框外并轻微抖动;p3-04 该卡吐出一条 `deny` 色命令,命令文字放大并使画面整体压暗一档;p3-05 环形循环的执行节点照常亮起("老老实实替它执行"的反讽);p3-06 一道竖线从顶部落下 |
+| 3-A | p3-01..06 | 五张工具卡回归,其中四张外面套着一个 `mech` 色的「工作目录」框,跑命令那张**在框外** | p3-02 框线描出并把四张卡收进去,第五张卡留在框外并轻微抖动;p3-04 该卡吐出一条 `deny` 色命令,命令文字放大并使画面整体压暗一档,该句尾(`+16` 帧)环形循环的执行节点照常亮起("循环照跑"的反讽——原 p3-05 已删,动效锚点改挂 p3-04 尾);p3-06 一道竖线从顶部落下 |
| 3-B | p3-07..09 | **闸门路由**成形:请求从左侧进入,前方三道竖闸依次落下(1 号 `deny` 描边、2 号 `mech`、3 号 `mech` 虚线) | 三道闸门以 `spring` 自上落下(每句一道);p3-08 第一道闸门上浮出四个禁令词;p3-09 一个请求光点撞上第一道闸门被**反弹回左侧**并碎成 `deny` 色粒子 |
-| 3-C | p3-10..13 | 第二、三道闸门工作:一个请求穿过一道被标记,停在第三道前等待;另一个请求三道全过直接抵达执行口 | p3-10/11 请求光点穿过一闸后头顶浮出「看情况」标签;p3-12 第三道闸门前画面**整体降速并压暗**,弹出「工具名 + 参数」审批卡与 y/N 光标;p3-13 另一条通路上一个光点畅通无阻直达执行口,并在轨迹上留下 `core` 尾迹 |
-| 3-D | p3-14..19 | 拒绝表代码卡(mono,七个字符串)居中;随后表下方浮出原话引用 | p3-15 代码卡逐行渲染;p3-16/17 表的边框由 `deny` 变为 `dim` 虚线("示意"而非"边界"的视觉降级);p3-18 表右侧生成三条变体写法,逐条**绕过**表框飞向执行口;p3-19 金句卡 `serif` 居中 |
-| 3-E | p3-20..24 | 四个结果徽标呈四宫格:放行(`core`)、拒绝(`deny`)、问你(`mech`)、不表态(`dim` 描边) | p3-21 前三格先亮(对应三道闸门),第四格空着并有呼吸描边;p3-23 第四格亮起「不表态」;p3-24 从第四格拉出一条弧线**回连到「问你」格**,弧线上压「兜底」二字 |
-| 3-F | p3-25..29 | **官方求值顺序流水线**:拒绝 → 询问 → 放行 三站闸门横排,一枚请求光点沿轨道行进 | 三站以 spring 落下(顺序点亮,每站 7 帧);「头一个命中,就出局」横幅压上;p3-27 光点在拒绝站被截获、`deny` 辉光 + 「出局」标签;p3-28 右下浮「裸名 deny → 整件工具从模型身上消失」(Bash 加删除线) |
-| 3-G | p3-30..35 | **auto 分类器视野分屏**:五行记录自上而下滚入(辩解 / 工具结果 / 调用描述 / 用户消息 / 裸命令),前三行被划除,末两行金色高亮 | p3-31 五行逐条滚入;p3-32 前三行依次划除并各浮移除理由;p3-33 末两行染 `core` 高亮 + 「只看这两条」;p3-34 反例帧:一条伪装「用户早就批准了」的文本飞向视野、被 `deny` 色边界弹回;p3-35 金句压底 + 遥测角标(93%,官方 2026-03) |
+| 3-C | p3-10..12 | 第二、三道闸门工作:一个请求穿过一道被标记,停在第三道前等待;另一个请求三道全过直接抵达执行口 | p3-10/11 请求光点穿过一闸后头顶浮出「看情况」标签;p3-12 第三道闸门前画面**整体降速并压暗**,弹出「工具名 + 参数」审批卡与 y/N 光标;同句尾(`+25` 帧)另一条通路上一个光点畅通无阻直达执行口,并在轨迹上留下 `core` 尾迹(原 p3-13 已删——「弃权不等于放行」由 3-E 的 p3-24 单独承载,此处不再预支) |
+| 3-D | p3-14..15 | 拒绝表代码卡(mono,七个字符串)居中;边框降级为「示意」;金句卡改**底部叠加**(不整屏替换,为下一镜留画布) | p3-14 代码卡逐行渲染;同句 `+14` 帧表的边框由 `deny` 变 `dim` 虚线("示意"而非"边界"的视觉降级),`+30` 帧表右侧生成三条变体写法逐条**绕过**表框飞向执行口;p3-15 起 `+18` 帧金句卡 `serif` 以底部 Panel 浮出、hold 70 帧淡出 |
+| 3-D2 | p3-19a..19d | **沙箱横切面**(新镜,回答 3-D 金句抛出的问题):代码卡缩至左侧 55%,右侧画一圈圆角实心壳(`mech` 描边 4px、rx=26、辉光)罩住「跑命令 + 子进程树」;壳上下两道边界带 | p3-19a 壳自四周合拢包住进程树(spring);p3-19b 上边界带「文件:只许工作区」与下边界带「网络:只走代管口子」各闪一次;p3-19c 上一镜生成的三条变体改为**飞向壳壁被弹回**(复用 3-D 轨迹,末端 8 帧反弹),拆掉任一道墙的反例:密钥箭头飞出边界(`deny`);p3-19d 压角标「Seatbelt / bubblewrap + socat · 权限提示 −84%(官方工程博客,取数 2025-10)」 |
+| 3-E | p3-20..24 | 四个结果徽标呈四宫格:放行(`core`)、拒绝(`deny`)、问你(`mech`)、不表态(`dim` 描边) | p3-20 前三格先亮(对应三道闸门),第四格空着并有呼吸描边;p3-23 第四格亮起「不表态」;p3-24 从第四格拉出一条弧线**回连到「问你」格**,弧线上压「兜底」二字 |
+| 3-F | p3-25..29 | **官方求值顺序流水线**:拒绝 → 询问 → 放行 三站闸门横排,一枚请求光点沿轨道行进 | 三站以 spring 落下(顺序点亮,每站 7 帧);「头一个命中,就出局」横幅压上;p3-26 尾(`+30` 帧)光点在拒绝站被截获、`deny` 辉光 + 「出局」标签(原 p3-27 已删,锚点前挂);p3-28 右下浮「裸名 deny → 整件工具从模型身上消失」(Bash 加删除线) |
+| 3-G | p3-30..35 | **auto 分类器视野分屏**:五行记录自上而下滚入(辩解 / 工具结果 / 调用描述 / 用户消息 / 裸命令),前三行被划除,末两行金色高亮。右上加**成绩单卡**(两段误杀率对照) | p3-31 五行逐条滚入 + 遥测角标(93%,官方 2026-03);p3-31a 右上浮出「审判者的成绩单」Panel;p3-31b 220px 轨道上一条 `deny`→`core` 色条从 8.5% 宽收到 0.4% 宽,「快筛 / 复核」两标签随动点亮,角标追加「Stage1 FPR 8.5% → Stage2 0.4%(官方工程博客,取数 2026-03)」;p3-32 前三行依次划除并各浮移除理由,同句 `+20` 帧末两行染 `core` 高亮 +「只看这两条」(原 p3-33 已删,锚点合并);p3-34 反例帧:一条伪装「用户早就批准了」的文本飞向视野、被 `deny` 色边界弹回;p3-35 金句压底 |
## P4 挂在循环上,不写进循环里
@@ -61,9 +65,9 @@
| 4-B | p4-05..07 | 退化的循环代码卡:循环体内被塞进五行杂活 | 五行以每句 2 行的节奏插入,每插入一行代码卡**整体下移**并把最外层的 `while` 挤出可视区;p4-06 结束时代码卡边框变 `deny` 虚线;p4-07 金句卡覆盖上层 |
| 4-C | p4-08..12 | 代码卡收回,五行杂活被「拔」出循环、变成五张回调卡浮在环外;环恢复原线宽 | p4-08 五行代码逐行飞出环外并变形为卡片(0.5s 形变);p4-09/10 环外浮出「时机 → 函数列表」两列表格;p4-11/12 环与卡片之间出现四个插座图形,一张卡片「啪」地插入(`spring` 过冲 + 插入瞬间 `mech` 闪一帧) |
| 4-D | p4-13..19 | **插槽注册板**:环形循环外围四个插槽,按一整轮的时间顺序分布在环的四个方位 | 四个插槽逐句亮起并各插入对应回调卡:p4-14 入口槽(补背景信息的卡片飞入并在消息上加一小块 `mech` 补丁);p4-15/16 执行前槽——**P3 的三道闸门整体缩小、平移进这个槽**(跨幕视觉呼应),环上原本的检查标记同时消失;p4-17 执行后槽;p4-18/19 停机前槽亮 `core`,卡片返回一句话 → 一条回流箭头把它送回环的入口,环重新转起来 |
-| 4-E | p4-20..22 | 四个插槽同时点亮,环上标出四个时机点;环本身刻意**不加任何装饰** | 四点沿环依次脉冲一圈(顺时针,各 8 帧);p4-21 环上只浮出四个极简的喇叭图标("只喊一声");p4-22 金句卡;金句期间环继续匀速转动,不做任何强调动效——用"无动效"表达"故意保持无聊" |
+| 4-E | p4-20..22 | 四个插槽同时点亮,环上标出四个时机点;环本身刻意**不加任何装饰** | p4-20 四点沿环依次脉冲一圈(顺时针,各 8 帧),同句尾环上浮出四个极简的喇叭图标("只喊一声"——原 p4-21 已并入 p4-20);p4-22 金句卡;金句期间环继续匀速转动,不做任何强调动效——用"无动效"表达"故意保持无聊" |
| 4-F | p4-23..25 | **31 事件 × 三层嵌套**(官方口径):外层会话框(一次)套中层回合框(×N)套内层工具调用链(工具前→执行→工具后→整批落定);右栏异步事件网格;右下计数徽章 | 外层成形→中层边框闪两下浮 ×N→内层虚线框内一个光点沿四节点链循环跑(80 帧一圈);右栏 6 个异步事件逐条打出;计数从 0 滚到 31(官方文档事件表逐行计数) |
-| 4-G1 | p4-27..31 | **六道闸门流水线**(官方 SDK 权限判定图):hook/deny/ask/settings/mode/默认 六站横排,两端 Execute/Blocked 终点 | 轨道描出→一枚「我批了」光点出发→行至 deny 站被截入 Blocked(`deny` 辉光+冲击波);p4-31 切全放行模式重跑、光点仍在 deny 站被截 + 「全放行模式也一样:拒绝压得住」 |
+| 4-G1 | p4-27..31 | **六道闸门流水线**(官方 SDK 权限判定图):hook/deny/ask/settings/mode/默认 六站横排,两端 Execute/Blocked 终点。同一轨道上跑**三枚球**做对照(沉默 / 假拦截 / 我批了) | p4-27 起(`+6` 帧)轨道描出,消除头空转;p4-28 球 A(`dim`,标「沉默」)从 hook 站**无扰动通过**、继续走 deny/ask 站(沉默≠批准,照走正常流程);p4-28a/b 球 B(`deny`,标「我拦了」)全速穿过全部六站直达 Execute,到达瞬间标签翻成「静默失效」;p4-28c 压一行 mono 角标「唯一凭退出码单独阻断的是 exit 2;exit 1 = 非阻断错误,动作照跑」;p4-29/30 球 C(`core`,标「我批了」)行至 deny 站被截入 Blocked(`deny` 辉光+冲击波);p4-31 切全放行模式重跑、球 C 仍在 deny 站被截 + 「全放行模式也一样:拒绝压得住」 |
| 4-G2 | p4-32..34 | **印章对撞**(保留高潮母题):「我批了」印章 vs 「禁止/询问」印章 + 单向箭头 | 印章落下对撞、持续抵抗抖动;单向箭头(只允许「加限制」方向);金句卡「扩展点能加限制,不能解除限制」收口 |
| 4-H | p4-35..37 | 「别停」插槽与环的一段形成小闭环,反复自激;随后一枚标记落下把闭环打断 | p4-36 小闭环连转三圈,每圈提速一档、边缘 `deny` 色渐强(自激失控感);p4-37 一枚 `core` 色方形标记从上落下嵌入闭环,第四圈行进到标记处被**截断**,整体归于静止 |
@@ -71,14 +75,14 @@
| 镜 | 句区间 | 画面 | 动效 |
|---|---|---|---|
-| 5-A | p5-01..06 | 四层横向卡片:循环(`core` 实心)/ 分发表 / 闸门 / 插槽(后三者 `mech` 描边);卡片上方 p5-05 落下**官方四件套标尺**(循环 / 工具 / 上下文管理 / 护栏) | 四张卡逐句自下弹入并水平排开;p5-05 标尺自上淡入落位;p5-06 三根竖连线把本集三层接到对应格(循环↔循环、分发表↔工具、插口↔护栏),「上下文管理」格虚线空置 + 「空着 · 后面拆」小标;随后第一张卡**留在原地并加实底**,后三张同时上浮 8px 并转为半透明——"骨架 vs 挂件"的视觉分层 |
+| 5-A | p5-01..06 | 四层横向卡片:循环(`core` 实心)/ 分发表 / 闸门 / 插槽(后三者 `mech` 描边);卡片上方 p5-05 落下**官方四件套标尺**(循环 / 工具 / 上下文管理 / 护栏,与 0-C 四辐条同一张清单) | 四张卡逐句自下弹入并水平排开;p5-05 标尺自上淡入落位(**回指** 0-C 已立的定义,不再念「正好四件」);p5-06 三根竖连线把本集三层接到对应格(循环↔循环、分发表↔工具、插口↔护栏),「上下文管理」格虚线空置 + 「空着 · 后面拆」小标(与 0-C 灰置那根辐条首尾咬合);随后第一张卡**留在原地并加实底**,后三张同时上浮 8px 并转为半透明——"骨架 vs 挂件"的视觉分层 |
| 5-B | p5-07..10 | **四级台阶**:四段柱体高度递增(141/191/241/255),每根柱子内部有一段等高的 `core` 色块 | p5-08/09 四根柱体自左向右逐根升起(`spring`),柱顶数字滚动到位;p5-10 时四根柱子里的 `core` 色块**同时描出**——四段高度肉眼相同且色值相同,柱体其余部分降到 30% 亮度 |
-| 5-C | p5-11..14 | 环形循环回到画面中央(第五次出现,仍完全同色同线宽),执行节点位置换过四次写法的四个标签轮播 | p5-11/12 环上四个节点依次脉冲一圈;p5-13/14 执行节点上方的标签以 0.4s 间隔翻牌四次(写死命令 → 查表 → 闸门 → 插槽),**环本身一帧未变**;末尾金句卡 |
+| 5-C | p5-11..14 | 环形循环回到画面中央(第五次出现,仍完全同色同线宽),执行节点位置换过四次写法的四个标签轮播 | p5-11 环上四个节点依次脉冲一圈;p5-13/14 执行节点上方的标签以 0.4s 间隔翻牌四次(写死命令 → 查表 → 闸门 → 插槽),**环本身一帧未变**(原 p5-12 已并入 p5-13,翻牌起点改挂 p5-13);末尾金句卡 |
| 5-D | p5-15..19 | 回到 P0 的终端画面(同构图),但这次命令自己跑了起来、输出自动回流;末尾英文原句金句卡 | p5-16 P0 的灰色搬运轨迹以 `core` 色重画一次(这次是自动的);人形剪影淡出;p5-18 四层结构在终端外围合拢成一个壳;p5-19 英文原句 `serif` 与中文并列淡入 |
## P6 收尾
| 镜 | 句区间 | 画面 | 动效 |
|---|---|---|---|
-| 6-A | p6-01..04 | 壳的四层分别点亮并各配一句短标签(手脚 / 工具 / 底线 / 发挥) | 四层以每句一层的节奏点亮,点亮时对应标签 `FadeUp`;四层齐亮后整体轻微收拢一次(呼吸) |
-| 6-B | p6-05..08 | 信源卡四行(官方文档+取数日期 / 工程博客 / 源码分析已逐处标注 / 数字均为实测口径)+ 版本护栏行 → 系列身份卡 + **下期预告卡**(标题只在画面;series.json 派生硬编码,规则 8 对账) → **渐黑** | 信源卡逐行浮现(mono 小字);系列身份卡 `serif` 淡入;下期卡 `panel` 底描边框浮现(「下期 · 规划层」+ 标题);**渐黑窗口从本 beat 的总时长推导**(`beatDurationInFrames`,红线四),末 1.2s 线性压暗到全黑 |
+| 6-A | p6-01..04 | 壳的四层分别点亮并各配一句短标签(手脚 / 工具 / 底线 / 发挥) | 四层点亮锚点改为 p6-01 / p6-02 / p6-04 / p6-04+8(原 p6-03 已删,第三、四层在 p6-04 上错开 8 帧落位),点亮时对应标签 `FadeUp`;四层齐亮后整体轻微收拢一次(呼吸) |
+| 6-B | p6-06..08 | **句序已重排**(区间按 narration.json 实际顺序展开:p6-06 免责 → p6-07 免责 → p6-05 金句 → p6-08 预告)。信源卡四行(官方文档+取数日期 / 工程博客 / 源码分析已逐处标注 / 数字均为实测口径)+ 版本护栏行 → 金句压系列身份卡 → **下期预告卡**(标题只在画面;series.json 派生硬编码,规则 8 对账) → **渐黑** | p6-06/07 信源卡逐行浮现(mono 小字);p6-05 系列身份卡 `serif` 淡入并把金句压在卡上方(免责文案前移后,情绪高点与转化动作不再被隔开);p6-08 下期卡 `panel` 底描边框浮现(「下期 · 规划层」+ 标题);**渐黑窗口从本 beat 的总时长推导**(`beatDurationInFrames`,红线四),末 1.2s 线性压暗到全黑 |
diff --git a/apps/negentropy-influence/episodes/claude-code-explained-video/video/src/components/motifs.tsx b/apps/negentropy-influence/episodes/claude-code-explained-video/video/src/components/motifs.tsx
index 32f01bf8..3cc4c30e 100644
--- a/apps/negentropy-influence/episodes/claude-code-explained-video/video/src/components/motifs.tsx
+++ b/apps/negentropy-influence/episodes/claude-code-explained-video/video/src/components/motifs.tsx
@@ -42,9 +42,10 @@ export const Panel: React.FC<{
);
/** 底部角标——统一压在 bottom ≥ 150(避让字幕条,skills/06 红线二) */
-export const Footnote: React.FC<{children: React.ReactNode; delay?: number}> = ({
+export const Footnote: React.FC<{children: React.ReactNode; delay?: number; slot?: 0 | 1}> = ({
children,
delay = 0,
+ slot = 0,
}) => {
const frame = useCurrentFrame();
const o = interpolate(frame - delay, [0, 14], [0, 1], {
@@ -57,7 +58,9 @@ export const Footnote: React.FC<{children: React.ReactNode; delay?: number}> = (
position: 'absolute',
left: 0,
right: 0,
- bottom: 168,
+ // slot:同一 Sequence 里并列两个组件各带一条 Footnote 时,两条会在
+ // bottom:168 完全重印成一团糊字(2026-08 品控实拍坐实)。slot=1 抬到 206 错行。
+ bottom: slot === 1 ? 206 : 168,
textAlign: 'center',
fontFamily: theme.mono,
fontSize: 24,
@@ -86,6 +89,102 @@ export const SceneTag: React.FC<{chapter: string; tagline: string}> = ({chapter,
);
};
+/** 幕级常驻抬头带(2026-08 品控改造)——修复「顶部 1/3 全片闲置」的构图缺陷。
+ *
+ * 实测动机:五集 3×3 宫格抽样显示顶带 ink 仅 0.4–2.0%,而 SceneTag 的
+ * Sequence 覆盖率只有 7–59%。本组件由**幕**(而非镜)挂载一次,全幕常驻,
+ * 一次性补齐顶部信息层:左=幕序与幕名,右=本幕机制英文名(角标层,不进口播)。
+ *
+ * 纪律:只读底座 token 与传入的 accent;不参与句锚动画(避免与镜内动效抢注意力),
+ * 仅在幕首 12 帧淡入;高度锁死 ≤96px,不侵占镜内主体区。 */
+export const SceneHeader: React.FC<{
+ index: string;
+ title: string;
+ meta?: string;
+ accent?: string;
+ /** 本幕总帧数:给出即渲染幕内进度条(见下方 ambient 说明) */
+ durationInFrames?: number;
+}> = ({index, title, meta, accent, durationInFrames}) => {
+ const frame = useCurrentFrame();
+ const o = interpolate(frame, [0, 12], [0, 1], {extrapolateRight: 'clamp'});
+ const c = accent ?? theme.core;
+ // ambient(2026-08 品控修):实测五集随机采样点 92% 在 0.2 秒内画面**完全静止**——
+ // 镜内动效只在句锚附近发生,锚与锚之间是长时间冻帧。这条幕内进度条是全片唯一
+ // **每帧都在变**的元素,既消除「视频卡住了」的错觉,又给观众「这一幕还剩多久」的
+ // 真实信息。帧驱动、确定性,不违反禁随机数纪律。
+ const prog = durationInFrames && durationInFrames > 0 ? Math.min(1, frame / durationInFrames) : 0;
+ return (
+
+
+
+ {index}
+
+
+ {title}
+
+
+ {meta ? (
+
+ {meta}
+
+ ) : null}
+ {/* 幕内进度条:常驻底线 + 已走过的一段染主色 */}
+ {durationInFrames ? (
+
+
+ {/* 游标:进度条头部的一枚亮点 + 光晕。2px 的条在缩略图上几乎不可见,
+ 游标把「每帧都在动」放大到肉眼可辨,同时不抢镜内主体的注意力。 */}
+
+
+ ) : null}
+
+ );
+};
+
/** 数字滚动计数器(帧驱动,无随机) */
export const Counter: React.FC<{
from: number;
@@ -563,7 +662,7 @@ export const SlotRing: React.FC<{
// ─────────────────────────────────────────────────────────── 代码卡
-/** 代码卡:逐行渲染(每行 framesPerLine 帧),可高亮/压暗指定行 */
+/** 代码卡:逐行渲染(每行 framesPerLine 帧,自 startAt 帧起算),可高亮/压暗指定行 */
export const CodeCard: React.FC<{
lines: string[];
framesPerLine?: number;
@@ -573,6 +672,8 @@ export const CodeCard: React.FC<{
showLineNumbers?: boolean;
glowLineNumbersAt?: number;
accent?: string;
+ /** 逐行渲染的起始帧(默认 0)——前置动画(标签条/对照列)要先占画布时用 */
+ startAt?: number;
}> = ({
lines,
framesPerLine = 3,
@@ -582,6 +683,7 @@ export const CodeCard: React.FC<{
showLineNumbers = true,
glowLineNumbersAt,
accent,
+ startAt = 0,
}) => {
const frame = useCurrentFrame();
const glow =
@@ -594,7 +696,7 @@ export const CodeCard: React.FC<{
return (
{lines.map((ln, i) => {
- const shown = frame >= i * framesPerLine;
+ const shown = frame - startAt >= i * framesPerLine;
const hot = highlight.includes(i);
return (
= ({carryAt}) => {
@@ -74,18 +76,46 @@ const StallAndCarry: React.FC<{carryAt: number}> = ({carryAt}) => {
);
};
-/** 0-B 命名帧:搬运残影收束成外框,包住「模型」内核——外框即 Harness + 官方引文条 */
-const NamingFrame: React.FC<{officialAt: number}> = ({officialAt}) => {
+/** 0-B 命名帧:搬运残影收束成外框,包住「模型」内核——外框即 Harness。
+ * p0-03a..c 底部浮出官方赌注卡(SWE-bench 同模型换脚手架 22%→49%);
+ * 外框合拢改锚 p0-04(「名字就叫」句才落框,消除头空转);p0-05 官方引文条压上。 */
+const NamingFrame: React.FC<{
+ betAt: number;
+ barsAt: number;
+ settleAt: number;
+ frameAt: number;
+ officialAt: number;
+}> = ({betAt, barsAt, settleAt, frameAt, officialAt}) => {
const frame = useCurrentFrame();
const {fps} = useVideoConfig();
- // 外框四面合拢(spring 收拢感)
- const close = spring({frame, fps, config: {damping: 180}});
+ // 赌注卡自底浮出(p0-03a,FadeUp 语义)
+ const bet = interpolate(frame - betAt, [0, 16], [0, 1], {
+ extrapolateLeft: 'clamp',
+ extrapolateRight: 'clamp',
+ });
+ // 两条 core 分数条在 320px 轨道上增长(p0-03b:0→22% / 0→49%)
+ const bars = interpolate(frame - barsAt, [0, 30], [0, 1], {
+ extrapolateLeft: 'clamp',
+ extrapolateRight: 'clamp',
+ });
+ // 分数条定格并压「你的力气 = 它的分数」(p0-03c)
+ const settle = interpolate(frame - settleAt, [0, 12], [0, 1], {
+ extrapolateLeft: 'clamp',
+ extrapolateRight: 'clamp',
+ });
+ // 外框四面合拢改锚 p0-04(「名字就叫」句才落框——此前锚 beat 首帧,头两句空转)
+ const close = spring({frame: frame - frameAt, fps, config: {damping: 180}});
const CX = 960;
const CY = 460;
const full = 420;
- const wHalf = full / 2 + (1 - close) * 620;
- const hHalf = full / 2 + (1 - close) * 380;
- const label = interpolate(frame - 22, [0, 14], [0, 1], {
+ // 合拢起手的张开量:**必须**让展开态的外框仍落在安全区内。此前 620/380 的起手
+ // 让 close=0 时外框上沿到 y=-130(探出画面顶)、右沿 x=1790 横穿 SceneHeader 的
+ // meta 文字「the missing layer」——2026-08 帧级复查 f332/f851 实拍坐实。
+ // 上限反推:外框上沿 = CY − hHalf ≥ 150(抬头带含进度条到 y≈126),故
+ // hHalf ≤ 310;四角残影再外扩 40,故 hHalf ≤ 270。横向同理收进 72..1848。
+ const wHalf = full / 2 + (1 - close) * 420;
+ const hHalf = full / 2 + (1 - close) * 60;
+ const label = interpolate(frame - frameAt - 22, [0, 14], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
@@ -177,13 +207,17 @@ const NamingFrame: React.FC<{officialAt: number}> = ({officialAt}) => {
>
{'Harness'}
+ {/* 外框注脚:与赌注卡同为「引文条压上时让位」的一层。引文条上沿在
+ y≈718(bottom:200 + 面板高),此注脚落在 CY+hHalf+40 = 710,两者
+ 仅差 8px 而互相压字(2026-08 帧级复查 f1100 实拍坐实)——随引文条
+ 淡出即可,语义已由引文条本身承接。 */}
= ({officialAt}) => {
>
{'把动力源,套进一个可控的结构里'}
+ {/* 官方赌注卡(p0-03a..c):bottom:210 避让字幕带;p0-05 引文条压上时让位淡出。
+ 淡出必须**清零并卸载**:旧写法留 (1 - quote*0.9) = 0.1 的残影,引文条
+ (半透明 Panel)压上后两层文字互相透印成糊字(2026-08 帧级复查 f1100
+ 实拍坐实)。改为 quote ≥ 0.999 时整块卸载,之前按 1-quote 线性到 0。 */}
+ {bet > 0 && quote < 0.999 ? (
+
+
+
+ {'“同一个底层模型,换一套脚手架,成绩能差出一大截。”'}
+
+ {/* 双分数条:320px 轨道 interpolate 增长(0→22% / 0→49%) */}
+
+ {[
+ {label: '旧脚手架', v: 0.22},
+ {label: '新脚手架', v: 0.49},
+ ].map((b) => (
+
+
+ {b.label}
+
+
+
0.99 ? 1 : 0.35,
+ }}
+ >
+ {`${Math.round(b.v * Math.min(1, bars) * 100)}%`}
+
+
+ ))}
+
+ 0.99 ? 1 : 0,
+ }}
+ >
+ {'SWE-bench Verified 500 题 · 同脚手架代际 22%→49% · 官方自报 2025-01-06'}
+
+ {/* p0-03c:分数条定格 + 结论句压上 */}
+
+ {'你的力气 = 它的分数'}
+
+
+
+ ) : null}
{/* 官方引文条(p0-05 句锚) */}
{quote > 0 ? (
= ({officialAt}) => {
);
};
-/** 0-C 五辐条供给图:外框上长出五根辐条(文件/命令/权限/记忆/护栏)+ 标题卡收尾 */
+/** 0-C 四件套辐条图:外框上长出四根辐条(循环 / 工具 / 上下文管理 / 护栏),
+ * 内侧连着模型内核(与 p5-05 官方四件套标尺同一张清单——5-A 回指此处)。
+ * 「上下文管理」那根先灰(与 5-A 标尺空格、p5-06「后面拆」承诺同构)。 */
const SupplySpokes: React.FC<{titleAt: number}> = ({titleAt}) => {
const frame = useCurrentFrame();
const {fps} = useVideoConfig();
const CX = 960;
- const CY = 440;
+ // 外框由 760×500@CY440 收成 700×420@CY490(2026-08 帧级复查 f1420 实拍):
+ // 旧几何下辐点 fy = CY−250 = 190,辐条名(fy−92 = 98)正落在 SceneHeader
+ // 的 52..112 带内,「循环/工具/上下文管理/护栏」与幕标题同行印字;辐点圆
+ // (110..178)又被抬头带的进度线(y≈126)横穿。
+ // 新几何下辐条名落 y≈188、辐点圆 234..302,均在抬头带下沿之外;外框下沿
+ // 700 仍与标题卡上沿(bottom:210 → y≈736)留 36px。
+ const CY = 490;
+ const HALF_W = 350;
+ const HALF_H = 210;
const R = 300;
const spokes = [
- {label: '文件', zh: '读写项目'},
- {label: '命令', zh: '执行与回传'},
- {label: '权限', zh: '闸门与审批'},
- {label: '记忆', zh: '上下文与规则', later: true},
- {label: '护栏', zh: '拦截与兜底'},
+ {label: '循环', zh: '问模型·执行·填回'},
+ {label: '工具', zh: '查表分发'},
+ {label: '上下文管理', zh: '后面单独拆', later: true},
+ {label: '护栏', zh: '闸门·沙箱·插口'},
];
const title = spring({frame: frame - titleAt, fps, config: {damping: 200}});
const line = interpolate(frame - titleAt, [4, 40], [0, 1], {
@@ -257,10 +380,10 @@ const SupplySpokes: React.FC<{titleAt: number}> = ({titleAt}) => {
{/* 标题卡(p0-07 收尾压入) */}
{title > 0 ? (
@@ -366,13 +472,21 @@ export const P0Hook: React.FC<{scene: SceneRange}> = ({scene}) => {
const bC = w('p0-06', 'p0-07');
return (
+
-
+ {/* p0-03a 赌注卡浮出;p0-03b 双分数条增长;p0-03c 定格+结论;p0-04 外框合拢;p0-05 引文条 */}
+
-
+
diff --git a/apps/negentropy-influence/episodes/claude-code-explained-video/video/src/scenes/P1Loop.tsx b/apps/negentropy-influence/episodes/claude-code-explained-video/video/src/scenes/P1Loop.tsx
index 9dede254..1ae26c38 100644
--- a/apps/negentropy-influence/episodes/claude-code-explained-video/video/src/scenes/P1Loop.tsx
+++ b/apps/negentropy-influence/episodes/claude-code-explained-video/video/src/scenes/P1Loop.tsx
@@ -1,4 +1,4 @@
-/** P1 一个循环,就是全部(分镜 1-A…1-F)—— 开源教学素材「Agent While-Loop」可视化的概念重建
+/** P1 一个循环,就是全部(分镜 1-A…1-F)—— 开源示教素材「Agent While-Loop」可视化的概念重建
* ★ 本幕建立全片视觉锚:LoopRing 的色与线宽从此不再改变。 */
import React from 'react';
import {AbsoluteFill, interpolate, Sequence, spring, useCurrentFrame, useVideoConfig} from 'remotion';
@@ -6,17 +6,9 @@ import {theme} from '../design/theme';
import {beatWindow} from '../timing';
import type {SceneRange} from '../types';
import {QuoteCard} from '../components/cards';
-import {
- CodeCard,
- Counter,
- Footnote,
- LoopRing,
- Panel,
- SceneTag,
- useRingDot,
-} from '../components/motifs';
+import {CodeCard, Counter, Footnote, LoopRing, Panel, SceneHeader, useRingDot} from '../components/motifs';
-/** 1-A 环形循环成形 + 两个信号分支 */
+/** 1-A 环形循环成形 + 两个信号分支(原 p1-07 已删:「传送带转不转」的语义收在 p1-06 的定格收镜上) */
const RingBirth: React.FC<{yesAt: number; noAt: number}> = ({yesAt, noAt}) => {
const frame = useCurrentFrame();
const draw = interpolate(frame, [4, 40], [0, 1], {
@@ -24,7 +16,7 @@ const RingBirth: React.FC<{yesAt: number; noAt: number}> = ({yesAt, noAt}) => {
extrapolateRight: 'clamp',
});
const dot = useRingDot(2.5, 40);
- // 讲到「没有」时光点滑出到停机出口并定格
+ // 讲到「没有」时光点滑出到停机出口并定格收镜
const pull = interpolate(frame - noAt, [8, 30], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
@@ -32,8 +24,7 @@ const RingBirth: React.FC<{yesAt: number; noAt: number}> = ({yesAt, noAt}) => {
const active = frame >= yesAt && frame < noAt ? 2 : undefined;
return (
-
- 0.98 ? dot : undefined} activeNode={active} exitPull={pull} />
+ 0.98 ? dot : undefined} activeNode={active} exitPull={pull} />
{'有 tool_use → 继续 · 没有 → 退出'}
@@ -107,7 +98,11 @@ const FiveSteps: React.FC<{stepAt: number[]}> = ({stepAt}) => {
);
})}
-
+ {/* alignSelf:flex-start + marginTop:40 会把面板顶到 y≈40,整块压在
+ SceneHeader 的 meta「Agent Loop · gather / act / verify」上(2026-08
+ 帧级复查 f2473/f2554 实拍坐实)。抬头带含进度条到 y≈126,故改用
+ marginTop:170 把面板压到抬头带之下。 */}
+
{'messages[]'}
@@ -155,8 +150,16 @@ const FiveSteps: React.FC<{stepAt: number[]}> = ({stepAt}) => {
);
};
-/** 1-C 二十三行代码卡 + 分工左右分屏 */
-const TwentyThreeLines: React.FC<{countAt: number; splitAt: number}> = ({countAt, splitAt}) => {
+/** 1-C 二十三行代码卡 + 来源标签条 + 延伸列 + 分工左右分屏。
+ * p1-12a 卡眉滑入来源标签条(「开源最小实现 · 照着同一套机制搭的」,无具名信息——
+ * 示例锚,先交代这份代码是谁、再开始读);p1-12b 右侧浮出「边读边说:实际实现」
+ * 半亮列;随后代码逐行渲染 → 二十三行计数 → 左右分屏。 */
+const TwentyThreeLines: React.FC<{
+ tagAt: number;
+ compareAt: number;
+ countAt: number;
+ splitAt: number;
+}> = ({tagAt, compareAt, countAt, splitAt}) => {
const frame = useCurrentFrame();
const lines = [
'while True:',
@@ -176,15 +179,85 @@ const TwentyThreeLines: React.FC<{countAt: number; splitAt: number}> = ({countAt
' "tool_use_id": block.id, "content": output})',
' messages.append({"role": "user", "content": results})',
];
+ // 标签条自卡眉滑入(mech 描边)
+ const tag = interpolate(frame - tagAt, [0, 14], [0, 1], {
+ extrapolateLeft: 'clamp',
+ extrapolateRight: 'clamp',
+ });
+ // 右侧「对照对象」半亮列
+ const compare = interpolate(frame - compareAt, [0, 16], [0, 1], {
+ extrapolateLeft: 'clamp',
+ extrapolateRight: 'clamp',
+ });
const split = interpolate(frame - splitAt, [0, 20], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
+ // 代码逐行渲染:标签条(~14 帧)落下后即开卷——不再等对照列。
+ // 旧锚 compareAt+18 让 p1-12a 整句(164 帧 ≈ 5.5s)画布上只有一颗孤零零的
+ // 药丸标签(三密度审查 fr3195 实拍:body 仅 [82,112] 一行)。代码卡提前到
+ // 标签条就位后立刻逐行铺开,p1-12a 说「有人照着这套机制搭了一份开源的最小实现」
+ // 时代码已经在屏幕上生长。
+ const codeStart = tagAt + 16;
return (
-
+ {/* 来源标签条:卡眉上方滑入(mech 描边,无具名信息) */}
+
+
+ {'开源最小实现 · 照着同一套机制搭的'}
+
+
+
+ {/* 延伸半亮列(p1-12b):读最简实现时,右边始终立着实际实现的落点 */}
+ {compare > 0 ? (
+
+
+ {'边读边说'}
+
+ {'实际实现'}
+
+
+
+ ) : null}
= ({crossAt,
});
if (frame >= quoteAt) {
return (
-
+
);
}
return (
@@ -312,7 +385,7 @@ const UnreliableFlag: React.FC<{crossAt: number; quoteAt: number}> = ({crossAt,
) : null}
- {'stop_reason is unreliable —— 第三方的源码分析 · 开源教学版仍按停止标记判定,官方实现已改为查内容块'}
+ {'stop_reason is unreliable —— 第三方的源码分析 · 开源最简实现仍按停止标记判定,官方实现已改为查内容块'}
);
@@ -345,7 +418,11 @@ const ThreePhaseRing: React.FC<{phaseAt: number; handAt: number; drawersAt: numb
extrapolateRight: 'clamp',
});
const CX = 560;
- const CY = 500;
+ // 环心下移 +80:此前 CY=500 时 -90° 相位标签落在 y = CY − R − 96 − 10 ≈ 144,
+ // 与 SceneHeader(top 52,含进度条到 y≈126)只差 18px,「收集上下文」贴着抬头带
+ // 印字;上方的「三相互相交融」标注(y = CY − R − 148 ≈ 102)更是直接横穿幕标题。
+ // 下移后:相位标签 y≈224、交融标注 y≈182,均落在抬头带下沿之外。
+ const CY = 580;
const R = 250;
return (
@@ -426,7 +503,7 @@ const ThreePhaseRing: React.FC<{phaseAt: number; handAt: number; drawersAt: numb
}}
>
- {'真实产品随身带的状态(十样)'}
+ {'实际实现随身带的状态(十样)'}
{[
@@ -526,11 +603,11 @@ const ExitPaths: React.FC<{branchAt: number; dimAt: number}> = ({branchAt, dimAt
export const P1Loop: React.FC<{scene: SceneRange}> = ({scene}) => {
const w = (fromId: string, toId?: string) => beatWindow(scene.sentences, scene.from, fromId, toId);
- const bA = w('p1-01', 'p1-07');
+ const bA = w('p1-01', 'p1-06');
const rA = (id: string) => w(id).from - bA.from;
const bB = w('p1-08', 'p1-12');
const rB = (id: string) => w(id).from - bB.from;
- const bC = w('p1-13', 'p1-16');
+ const bC = w('p1-12a', 'p1-16');
const rC = (id: string) => w(id).from - bC.from;
const bD = w('p1-17', 'p1-24');
const rD = (id: string) => w(id).from - bD.from;
@@ -540,6 +617,7 @@ export const P1Loop: React.FC<{scene: SceneRange}> = ({scene}) => {
const rF = (id: string) => w(id).from - bF.from;
return (
+
@@ -547,7 +625,13 @@ export const P1Loop: React.FC<{scene: SceneRange}> = ({scene}) => {
-
+ {/* p1-12a 来源标签条滑入 → p1-12b 对照列浮出 → 代码逐行 → p1-14 计数 → p1-15 分屏 */}
+
diff --git a/apps/negentropy-influence/episodes/claude-code-explained-video/video/src/scenes/P2Dispatch.tsx b/apps/negentropy-influence/episodes/claude-code-explained-video/video/src/scenes/P2Dispatch.tsx
index ac2db9f4..cde96fae 100644
--- a/apps/negentropy-influence/episodes/claude-code-explained-video/video/src/scenes/P2Dispatch.tsx
+++ b/apps/negentropy-influence/episodes/claude-code-explained-video/video/src/scenes/P2Dispatch.tsx
@@ -1,4 +1,4 @@
-/** P2 加一个工具,只改一行(分镜 2-A…2-H)—— 开源教学素材「Tool Dispatch Map」的概念重建
+/** P2 加一个工具,只改一行(分镜 2-A…2-H)—— 开源示教素材「Tool Dispatch Map」的概念重建
* 重点视觉演绎:并发安全 ≠ 只读(真值表对撞)、连续块分批、读文件落盘自循环。 */
import React from 'react';
import {AbsoluteFill, interpolate, Sequence, spring, useCurrentFrame, useVideoConfig} from 'remotion';
@@ -6,25 +6,20 @@ import {theme} from '../design/theme';
import {beatWindow} from '../timing';
import type {SceneRange} from '../types';
import {QuoteCard} from '../components/cards';
-import {
- CodeCard,
- DispatchTable,
- Footnote,
- LoopRing,
- NumberedCard,
- Panel,
- SceneTag,
- Terminal,
- useRingDot,
-} from '../components/motifs';
+import {CodeCard, DispatchTable, Footnote, LoopRing, NumberedCard, Panel, SceneHeader, Terminal, useRingDot} from '../components/motifs';
-/** 环留在左上角:缩小但同色同线宽 */
+/** 环留在左上角:缩小但同色同线宽。
+ * 落位约束:SceneHeader 占 top 52..112(含 bottom:-14 的幕内进度条,实际到 y≈126),
+ * 角环必须整体落在其**下方**。此前 top:56 让环上半圈直接压在幕标题「P2 加一个工具,
+ * 只改一行」上、环线又横穿进度条(2026-08 帧级复查 f7431/f9041/f10615 实拍坐实)。
+ * size=190 的 LoopRing 内部半径 = 95−46 = 49,环上沿 = top + 95 − 49 − 3(线宽半);
+ * 取 top:150 ⇒ 环上沿 y≈193,与进度条留 67px 净空。 */
const CornerRing: React.FC<{pulse?: boolean}> = ({pulse = false}) => {
const frame = useCurrentFrame();
const dot = useRingDot(2.5);
const p = pulse ? 0.6 + 0.4 * Math.sin(frame / 4) : 1;
return (
-
+
);
@@ -37,8 +32,7 @@ const ClumsyCommands: React.FC<{typoAt: number}> = ({typoAt}) => {
const shake = bad ? Math.sin((frame - typoAt) / 1.6) * 3 : 0;
return (
-
-
+
{'模型想的是'}
@@ -72,7 +66,7 @@ const FiveTools: React.FC = () => {
const frame = useCurrentFrame();
const tools = ['跑命令', '读文件', '写文件', '改文件', '按模式找文件'];
const mono = ['bash', 'read_file', 'write_file', 'edit_file', 'glob'];
- // 官方五类工具地图(how-claude-code-works):教学五件归位官方分区,两个空槽亮虚线(Harness Engineering 改造)
+ // 官方五类工具地图(how-claude-code-works):示例五件归位官方分区,两个空槽亮虚线(Harness Engineering 改造)
const zones = ['文件操作', '文件操作', '文件操作', '文件操作', '搜索'];
return (
@@ -261,8 +255,8 @@ const QueueVsParallel: React.FC<{splitAt: number}> = ({splitAt}) => {
) : (
{[
- {t: '教学版:排队', par: false},
- {t: '真实产品:能并行的并行', par: true},
+ {t: '最简示例:排队', par: false},
+ {t: '实际实现:能并行的并行', par: true},
].map((col) => (
@@ -305,7 +299,7 @@ const ConcurrencyTable: React.FC<{
if (frame >= quoteAt) {
return (
);
@@ -563,7 +557,13 @@ const SpillLoop: React.FC<{markAt: number; loopAt: number}> = ({markAt, loopAt})
export const P2Dispatch: React.FC<{scene: SceneRange}> = ({scene}) => {
const w = (fromId: string, toId?: string) => beatWindow(scene.sentences, scene.from, fromId, toId);
- const bA = w('p2-01', 'p2-04');
+ /** 句尾锚(beat 内相对帧):分镜「pX-YY 尾(+N 帧)」的机械化落点。
+ * 句尾是本 beat 末句时,+N 会探出 Sequence 窗口(窗口只含句间 gap ≈10 帧)——
+ * 封顶到「窗口末尾 − need 帧可见」:动效仍然发生,不被窗口剪掉。 */
+ const endAt = (id: string) => w(id).from + w(id).durationInFrames;
+ const tailAt = (b: {from: number; durationInFrames: number}, id: string, plus: number, need: number) =>
+ Math.min(endAt(id) + plus - b.from, b.durationInFrames - need);
+ const bA = w('p2-01', 'p2-03');
const bC = w('p2-07', 'p2-10');
const bD = w('p2-11', 'p2-13');
const bE = w('p2-14', 'p2-16');
@@ -573,8 +573,11 @@ export const P2Dispatch: React.FC<{scene: SceneRange}> = ({scene}) => {
const rel = (b: {from: number}, id: string) => w(id).from - b.from;
return (
+
-
+ {/* p2-03 尾 +22 帧:命令里一个字符高亮成 deny 并抖动(原 p2-04 已删,语义由角标承载)。
+ p2-03 是本 beat 末句——+22 会探出窗口,封顶到末尾仍留 26 帧可见抖动+角标。 */}
+
diff --git a/apps/negentropy-influence/episodes/claude-code-explained-video/video/src/scenes/P3Gates.tsx b/apps/negentropy-influence/episodes/claude-code-explained-video/video/src/scenes/P3Gates.tsx
index 7169694d..d32407a0 100644
--- a/apps/negentropy-influence/episodes/claude-code-explained-video/video/src/scenes/P3Gates.tsx
+++ b/apps/negentropy-influence/episodes/claude-code-explained-video/video/src/scenes/P3Gates.tsx
@@ -1,4 +1,4 @@
-/** P3 执行之前,先过闸门(分镜 3-A…3-G)—— 开源教学素材「Permission Desk」的概念重建
+/** P3 执行之前,先过闸门(分镜 3-A…3-G,含新镜 3-D2 沙箱横切面)—— 开源示教素材「Permission Desk」的概念重建
* 三种结果不各占一色:allow 回 core(放行=回主干)、ask 用 mech、deny 用 danger。 */
import React from 'react';
import {AbsoluteFill, interpolate, Sequence, spring, useCurrentFrame, useVideoConfig} from 'remotion';
@@ -6,16 +6,7 @@ import {theme} from '../design/theme';
import {beatWindow} from '../timing';
import type {SceneRange} from '../types';
import {QuoteCard} from '../components/cards';
-import {
- CodeCard,
- Footnote,
- GateRouter,
- LoopRing,
- NumberedCard,
- Panel,
- SceneTag,
- useRingDot,
-} from '../components/motifs';
+import {CodeCard, Footnote, GateRouter, LoopRing, NumberedCard, Panel, SceneHeader, useRingDot} from '../components/motifs';
/** 3-A 五张工具卡,跑命令那张在「工作目录」框外 */
const UnguardedShell: React.FC<{frameAt: number; cmdAt: number; execAt: number}> = ({
@@ -34,8 +25,7 @@ const UnguardedShell: React.FC<{frameAt: number; cmdAt: number; execAt: number}>
const dim = showCmd ? 0.55 : 1;
return (
-
-
+
{/* 工作目录框:把四张 file tool 收进去 */}
};
/** 3-B 三道闸门落下,第一道把请求弹回 */
-const GatesFall: React.FC<{gateAt: number[]; bounceAt: number}> = ({gateAt, bounceAt}) => {
+const GatesFall: React.FC<{gateAt: number[]; bounceAt: number; pillAt?: number}> = ({
+ gateAt,
+ bounceAt,
+ pillAt,
+}) => {
const frame = useCurrentFrame();
const g = gateAt.map((a) =>
interpolate(frame - a, [0, 16], [0, 1], {extrapolateLeft: 'clamp', extrapolateRight: 'clamp'}),
@@ -117,10 +111,13 @@ const GatesFall: React.FC<{gateAt: number[]; bounceAt: number}> = ({gateAt, boun
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
});
+ /* 拒绝表胶囊锚 p3-08(口播点名「删根目录、提权…」那句)。闸门前移到 p3-07 后,
+ 旧式 gateAt[0]+12 会让胶囊抢在句前 ~1.3s 出现——提前落门可以,提前念清单不行。 */
+ const pillStart = pillAt ?? gateAt[0] + 12;
return (
- {g[0] > 0.8 ? (
+ {g[0] > 0.8 && frame >= pillStart ? (
{['删根目录', '提权', '关机', '格式化'].map((t, i) => (
= ({gateAt, boun
accent={theme.deny}
style={{
padding: '10px 20px',
- opacity: interpolate(frame - gateAt[0] - 12 - i * 4, [0, 10], [0, 1], {
+ opacity: interpolate(frame - pillStart - i * 4, [0, 10], [0, 1], {
extrapolateLeft: 'clamp',
extrapolateRight: 'clamp',
}),
@@ -225,7 +222,7 @@ const AskAndPass: React.FC<{askAt: number; passAt: number}> = ({askAt, passAt})
) : null}
- {/* 三判定小抄(开源教学素材 Permission Desk 的信息结构):allow / ask / deny 各带真实载荷 ——
+ {/* 三判定小抄(开源示教素材 Permission Desk 的信息结构):allow / ask / deny 各带真实载荷 ——
本幕上方只演了 ask 与 allow,这里把第三条补齐,路由器的三种出口一目了然。
落位约束:小抄出现时(t2 > 0.4)闸门已整体上移 -150,GateRouter 的闸门名
(`y - h - 18`)落在 y≈235–258、闸柱顶到 y≈272——小抄必须整体收在其上方,
@@ -235,7 +232,12 @@ const AskAndPass: React.FC<{askAt: number; passAt: number}> = ({askAt, passAt})
style={{
position: 'absolute',
left: 120,
- top: 56,
+ // top:56 会把三张卡整体压在 SceneHeader 上——幕序「P3」与幕标题
+ // 「执行之前,先过闸门」被 allow/ask 两张卡完全盖住(2026-08 帧级复查
+ // f13080 实拍坐实)。抬头带含进度条到 y≈126,故小抄下移到 150;
+ // 卡高约 108(10+30+26+23+10+边框),下沿 y≈258 仍收在闸门名
+ // (y≈235–258)之上的约束由「闸门整体上移 -150」保障。
+ top: 150,
display: 'flex',
gap: 18,
opacity: interpolate(t2, [0.4, 0.8], [0, 1], {extrapolateRight: 'clamp'}),
@@ -273,16 +275,20 @@ const AskAndPass: React.FC<{askAt: number; passAt: number}> = ({askAt, passAt})
);
};
-/** 3-D 拒绝表降级为「示意」+ 变体绕过 */
+/** 3-D 拒绝表降级为「示意」+ 变体绕过。
+ * 金句卡改**底部叠加**(不整屏替换,为 3-D2 沙箱镜留画布):p3-15 起 +18 帧以底部 Panel
+ * 浮出、hold 70 帧淡出——代码卡与变体全程留在原位(分镜 3-D 规格)。 */
const DenyListHonesty: React.FC<{degradeAt: number; bypassAt: number; quoteAt: number}> = ({
degradeAt,
bypassAt,
quoteAt,
}) => {
const frame = useCurrentFrame();
- if (frame >= quoteAt) {
- return
;
- }
+ // 金句:底部 Panel 浮出(+18 帧起),hold 70 帧后淡出
+ const quote = interpolate(frame - quoteAt, [0, 14, 70 + 14, 70 + 14 + 16], [0, 1, 1, 0], {
+ extrapolateLeft: 'clamp',
+ extrapolateRight: 'clamp',
+ });
const degraded = frame >= degradeAt;
const lines = [
'DENY_LIST = [',
@@ -323,7 +329,7 @@ const DenyListHonesty: React.FC<{degradeAt: number; bypassAt: number; quoteAt: n
textAlign: 'center',
}}
>
- {'教学示意,不是安全边界'}
+ {'示教示意,不是安全边界'}
) : null}
@@ -356,6 +362,238 @@ const DenyListHonesty: React.FC<{degradeAt: number; bypassAt: number; quoteAt: n
) : null}
+ {/* 金句卡:底部叠加(不整屏替换——下一镜 3-D2 沙箱接着用这块画布) */}
+ {quote > 0 ? (
+
+
+
+ {'它告诉你闸门装在哪儿,不是替你把门守住。'}
+
+
+
+ ) : null}
+
+ );
+};
+
+/** 3-D2 沙箱横切面(新镜,回答 3-D 金句抛出的问题):左侧缩小的拒绝表代码卡,
+ * 右侧圆角实心壳罩住「跑命令 + 子进程树」,上下两道边界带(文件/网络)。
+ * p3-19a 壳合拢;p3-19b 两道边界带各闪一次;p3-19c 三条变体飞向壳壁被弹回 +
+ * 密钥箭头飞出边界(deny)——「拆掉任一道墙」的反例;p3-19d 压官方角标。 */
+const SandboxCrossSection: React.FC<{
+ shrinkAt: number;
+ shellAt: number;
+ bandsAt: number;
+ bounceAt: number;
+ noteAt: number;
+}> = ({shrinkAt, shellAt, bandsAt, bounceAt, noteAt}) => {
+ const frame = useCurrentFrame();
+ const {fps} = useVideoConfig();
+ // 代码卡缩至左侧 55%
+ const shrink = interpolate(frame - shrinkAt, [0, 18], [0, 1], {
+ extrapolateLeft: 'clamp',
+ extrapolateRight: 'clamp',
+ });
+ // 壳自四周合拢(spring)
+ const shell = spring({frame: frame - shellAt, fps, config: {damping: 180}});
+ // 两道边界带各闪一次(p3-19b)
+ const bandOn = (i: number) =>
+ interpolate(frame - bandsAt - i * 16, [0, 6, 22], [0.55, 1, 0.9], {
+ extrapolateLeft: 'clamp',
+ extrapolateRight: 'clamp',
+ });
+ // 变体飞向壳壁被弹回(复用 3-D 轨迹,末端 8 帧反弹)
+ const fly = interpolate(frame - bounceAt, [0, 26], [0, 1], {
+ extrapolateLeft: 'clamp',
+ extrapolateRight: 'clamp',
+ });
+ const bounceBack = interpolate(frame - bounceAt - 26, [0, 8], [0, 1], {
+ extrapolateLeft: 'clamp',
+ extrapolateRight: 'clamp',
+ });
+ // 密钥箭头飞出边界(deny)——「拆掉任一道墙」的反例
+ const leak = interpolate(frame - bounceAt - 18, [0, 24], [0, 1], {
+ extrapolateLeft: 'clamp',
+ extrapolateRight: 'clamp',
+ });
+ const note = interpolate(frame - noteAt, [0, 12], [0, 1], {
+ extrapolateLeft: 'clamp',
+ extrapolateRight: 'clamp',
+ });
+ const lines = [
+ 'DENY_LIST = [',
+ ' "rm -rf /", "sudo", …',
+ ']',
+ ];
+ const variants = ['rm -rf /', 'rm -fr /', 'sudo${IFS}rm'];
+ // 壳几何:右侧画布中心。CX/CY 是**svg 内部坐标系**(svg width=760 height=620,
+ // 默认 overflow:hidden 会把界外内容整块裁掉)——旧值 1240/500 是 1920 全画布
+ // 坐标,壳 x=930..1550 全部落在 760 宽的 svg 之外,整镜 21.6s 壳体/边界带/
+ // 进程树/密钥箭头一行都没渲染出来(右侧全程空白,2026-08-29 三密度全扫
+ // f13420..f13950 逐帧实拍坐实)。取 svg 中心 (380, 310) 后,壳在画布上落于
+ // x≈750..1370、y≈320..760,左侧代码卡与右侧壳重新构成双栏构图。
+ const CX = 380;
+ const CY = 310;
+ const W = 620;
+ const H = 440;
+ const halfW = W / 2 + (1 - shell) * 560;
+ const halfH = H / 2 + (1 - shell) * 400;
+ return (
+
+
+ {/* 左:缩小的代码卡(3-D 的延续,画面 55% 区) */}
+
+
+ {/* 变体列表:飞向壳壁(p3-19c 复用 3-D 轨迹) */}
+
+ {variants.map((v, i) => {
+ if (fly <= 0) return null;
+ const goX = interpolate(fly, [0, 1], [0, 300]);
+ const backX = bounceBack * (1 - Math.min(1, bounceBack)) * -180 + bounceBack * -120;
+ return (
+
0 ? 1 - bounceBack * 0.4 : 0,
+ transform: `translateX(${goX + backX + i * 14}px)`,
+ }}
+ >
+ {v}
+
+ );
+ })}
+
+
+ {/* 右:沙箱壳(圆角实心壳罩住进程树) */}
+
+
+ {/* p3-19d 官方角标 */}
+
+ {'Seatbelt / bubblewrap + socat · 权限提示 −84%(官方工程博客,取数 2025-10)'}
+
);
};
@@ -403,10 +641,16 @@ const FourResults: React.FC<{fourthAt: number; arcAt: number}> = ({fourthAt, arc
);
})}
+ {/* 「不表态 → 问你」的回连弧走**格子外侧**(右缘之外)。
+ 旧弧 M400 210 C520 210,560 120,470 96 起点落在第四格内部,
+ 整条弧横穿「不表态」四个大字、「兜底」二字又压在格内(2026-08
+ 帧级复查 f14473 实拍坐实)。改为自第四格右缘中点出发、绕右侧
+ 外空白上行、收在第二格(拒绝)右缘——全程 x ≥ 704 = 格阵右缘,
+ 与任何文字零相交。 */}
{arc > 0 ? (
-