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.
1How It Works & Mechanism
"Every line is a receipt": None of the text in the film is fabricated; instead, it consists of verbatim quotes extracted directly from the user's own Claude Code session history, and every line must be reviewed and approved by the user prior to rendering.
Every line on screen is a receipt. The user approves each one before anything renders.
An "ordinary day" is not hallucinated by a model, but statistically derived: harvest.py categorizes activities from tool calls in sessions (read/edit/test/build/run/install/commit/PR/deploy/fetch/render/browse/delegate), counts the occurrence rate and median position of each activity across sessions, and defines this day using "the week with the most sessions + that week's median first message time".
# the day: the weekday with the most sessions, and its usual start time (mornings preferred)
User verbatim quotes are grouped into chapters by type: CATS uses regex to categorize messages into chapter types such as building (agree/proceed), restart, knock (asking progress), notes (corrections), side-by-side (close enough), good-part (praise), frames, watching, kept (remember/next time), and sets a "maximum quotable length" for each type.
CATS = { # chapter type → pattern over the lowercased message (+ max length for short quotable lines)
The surface for reading material is strictly scoped and declared as purely local: it only reads ~/.claude/projects/*/*.jsonl, memory/*.md beneath them, and the project's CLAUDE.md, without writing, modifying, or uploading anything.
Reads (never writes, never uploads):
The privacy gate is an executable structural validation rather than a verbal honor system: new-film.mjs scans field-by-field with regex before generating story.js, raising errors on credential patterns (sk-/ghp_/github_pat_/xox?-/AKIA/BEGIN private keys), email addresses, and file paths, while only warning on URLs; furthermore, if story.approved is not set to true, it warns that the user must inspect every line first.
if (SECRET.some(r => r.test(t))) errs.push(`${at}.${k}: looks like a credential; remove it`);
The timeline is **strictly locked** to archetypes.json: Each chapter defines bars and seconds for various events, all aligned to a 96 BPM grid; both the visual scenes (scenes.js) and the score (score.py) derive timing from this single table, ensuring no drift even if chapter order changes.
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.
Visuals are rendered frame-by-frame (not DOM/CSS animations): the kit draws each frame on a hidden p5/WEBGL canvas, then composites it to visible #out; the "boil" effect = ~8 drawings/s, changing every 3 frames, with no hold exceeding 3 frames, all verified by qc.py directly measured from the rendered MP4.
/* the kit paints in a hidden p5/WEBGL canvas and composites each drawing into #out */
The musical score shares the exact timeline source with the visuals and is synthesized entirely locally: score.py reads the identical story.json + archetypes.json, composing two bars per chapter (96 BPM, D major, with the typed claude = D E F# A B A as the theme), synthesized and mastered locally via additive strings/Karplus-Strong plucking/synthetic reverb, with no network or credentials required.
No network, no credentials. Needs numpy, scipy, ffmpeg.
2Core Capabilities
3External Dependencies
| 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;不上传 |
4Recommended Governance & Evidence
- scripts/: Executes local script or command: python3 scripts/harvest.py --out <film>/receipts.json (reads local transcripts only; --root defaults to ~/.c
- 网络端点: Declares external network access: registry.npmjs.org: setup runs npm ci to install locked p5/p5.brush/puppeteer, plus npx --ye on every run
- 本地工作目录: File I/O and artifact outputs: within film directory: receipts.json, story.json, story.js, index.html (rewrites data-duration), mus
5Second-Pass Verification
- [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』一致。
6Verdict & Scenarios
94c054dd0a6d242e…