day-in-my-life
> A 60 to 75 s hand-drawn ink film of the agent's day with its own user ("a day in my life as <name>'s agent"): one average session from the user's local Claude Code history, beginning to end. The story follows what they actually do (coding, tests, docs, shipping, videos, whatever the history shows), told with their real words (first message, check-ins, corrections, praise, memory titles). It is drawn in two-tone p5.brush strokes that boil at 8 fps and scored for strings composed note-for-note on the film's clock (the typed "claude" is the theme). Trigger on: "day in my life as my agent", "a film about me and my agent", "what is it like to be my agent", "animate your day with me", "make a video of our sessions". Covers harvesting the routine and receipts, the privacy gate, the chapter toolbox, the ink kit, the score, render and QC.
1实现原理 · 为什么它能做到
「每一行都是收据」:片中所有文字不是创作出来的,而是从用户自己的 Claude Code 会话史里抽出的逐字原话,并且在渲染前必须由用户逐行过目批准。
Every line on screen is a receipt. The user approves each one before anything renders.
「普通的一天」不是模型想象的,而是统计出来的:harvest.py 从会话里的工具调用归类活动(读/改/测/构建/跑/装/提交/PR/部署/取数/渲染/上网/委派),统计各活动在会话中的出现率与中位位置,再用「会话最多的那个星期 + 该星期中位首条消息时间」定义这一天。
# the day: the weekday with the most sessions, and its usual start time (mornings preferred)
用户原话按类型分章:CATS 用正则把消息分成 building(同意/放行)、restart、knock(问进度)、notes(纠正)、side-by-side(差一点)、good-part(夸)、frames、watching、kept(记住/下次)等章节类型,并为每类设了「可引用的最大长度」。
CATS = { # chapter type → pattern over the lowercased message (+ max length for short quotable lines)
素材读取面被限定且声明为纯本地:只读 ~/.claude/projects/*/*.jsonl、其下 memory/*.md 与相关项目的 CLAUDE.md,不写不改不上传。
Reads (never writes, never uploads):
隐私闸门是可执行的结构校验,而不是口头约定:new-film.mjs 在生成 story.js 前逐字段正则扫描,命中凭证形状(sk-/ghp_/github_pat_/xox?-/AKIA/BEGIN 私钥)、邮箱、文件路径即报错,命中 URL 只警告;并且 story.approved 未置 true 时会提醒必须先给用户看每一行。
if (SECRET.some(r => r.test(t))) errs.push(`${at}.${k}: looks like a credential; remove it`);
时间轴**锁死**在 archetypes.json:每章有 bars 与若干事件秒数,且全部落在 96 BPM 网格上;画面(scenes.js)与配乐(score.py)都从这一张表取时间,因此换章节顺序也不会跑偏。
Single source of truth for the film clock. scenes.js draws every event at these times and scripts/score.py composes on them, so picture and music cannot drift.
画面是逐帧绘制的(不是 DOM/CSS 动画):kit 在一个隐藏的 p5/WEBGL canvas 上画每一张,再合成到可见的 #out;「boil」= 约 8 张/秒、每 3 帧换一张、任何停顿不超过 3 帧,这几条由 qc.py 直接从渲染出的 MP4 量出来验证。
/* the kit paints in a hidden p5/WEBGL canvas and composites each drawing into #out */
配乐与画面同源且纯本地合成:score.py 读同一份 story.json + archetypes.json,按章写作两小节(96 BPM、D 大调,主题是键入的 claude = D E F# A B A),本地用加法弦乐/Karplus-Strong 拨弦/合成厅渲染并 master,无网络无凭证。
No network, no credentials. Needs numpy, scipy, ffmpeg.
2核心能力
3外部依赖
| Kind | Dependency / Endpoint |
|---|---|
| cli | HyperFrames CLI(钉死 0.8.70,经 npx 从 npm 取;check 与 render) |
| cli | ffmpeg(配乐母带、QC 解码与响度量测、渲染链路) |
| package | p5 2.3.2 (LGPL-2.1) / p5.brush 2.2.1 (MIT) / puppeteer 25.9.0(含其钉死的 Chrome for Testing),由 lockfile 钉住 |
| package | Python3 + numpy + scipy(配乐合成与母带);harvest.py 只用标准库 |
| network | Google Fonts:合成页在预览/渲染时加载手写字体 Gochi Hand(SIL OFL) |
| network | 素材来源(唯一):用户本机的 Claude Code 转录、memory 文件与相关项目的 CLAUDE.md;不上传 |
4管控建议与判定依据
- scripts/: 执行本地脚本或命令: python3 scripts/harvest.py --out <film>/receipts.json(只读本地转录;--root 默认 ~/.c
- 网络端点: 声明外部网络访问: registry.npmjs.org:setup 的 npm ci 安装锁定的 p5/p5.brush/puppeteer;以及每次 npx --ye
- 本地工作目录: 文件读写与工件输出: film 目录内:receipts.json、story.json、story.js、index.html(改写 data-duration)、mus
5第二遍独立确认
- [ok] 脚本是否读凭证(黄/橙分界的关键) — 全目录 grep process.env / os.environ 零命中;harvest.py 走 argparse 参数(--root/--tz/--project),score.py/qc.py 无环境变量入口。new-film.mjs 的 SECRET 常量是用于**拒绝**凭证形状的正则,不是读取。因此橙档第一条不成立。
- [ok] 『Nothing is uploaded』声明是否真的成立 — harvest.py(读转录)import 仅 argparse/collections/datetime/glob/json/os/re/statistics,无任何网络模块;score.py 只 subprocess 调 ffmpeg;shoot.mjs 只 page.goto('file://…');qc.py 只跑 ffmpeg 解码。唯一出网行为是 setup 的 npm ci、每次 npx 取 hyperframes 与页面加载 Google Fonts。
- [ok] Use 门槛(至少 10 个本地会话、跨 5 个活跃日)是否真在代码里 — harvest.py:256 'enough': len(sessions) >= 10 and len(days) >= 5,并在运行摘要里打印 'enough material: {out["enough"]}';与 SKILL.md 的 Use 条件一致,且 SKILL.md 要求 enough 为 false 时停下。
- [ok] 隐私闸(凭证/邮箱/路径/URL)是否真有拦截面 — new-film.mjs 的 validate() 对每章每个字符串字段:SECRET 命中 → errs('looks like a credential; remove it');EMAIL 命中 → errs;PATH 命中 → errs;URL 命中 → warns。errs 非空即 exit 1,故是硬拦截。
- [discrepancy] 『故事必须经用户批准』是否被强制 — SKILL.md 的 Rules 写 'Approval before render.',但代码层面 story.approved 未置 true 时只产生一条 warn('story.approved is not true: show the user every on-screen line …'),并不阻断 --sync 或渲染。也就是说这是一道人工程序纪律,不是技术门禁;SKILL.md 的措辞(`story.approved` is set only by the user's yes)容易被读成强制。该出入对 pin commit 有效。
- [discrepancy] 『8 到 13 章』是否被校验 — SKILL.md Workflow 第 3 步写 '8 to 13 chapters.',但 new-film.mjs 的 validate() 只校验:非空、首章必须是 intro、末章必须是 tag、kept 必须紧邻 tag 之前、各章型必需字段;没有章数上下限检查。故 8–13 是写作纪律而非校验规则。
- [ok] 单一时间轴声明是否成立(画面与配乐同源) — archetypes.json 的 about 明写 scenes.js 与 scripts/score.py『both read it』;score.py 文档行也声明读 story.json + archetypes.json('the same table scenes.js draws from')。两处一致,未见第二处时钟。
- [ok] 没有本地转录时的行为 — harvest.py:137 'if not sessions: raise SystemExit(f\'no sessions under {a.root}: nothing to harvest\')',且没有任何兜底造数路径;与 SKILL.md『this skill never invents receipts』一致。
6结论
94c054dd0a6d242e…