Nighty websiteNighty 官网

BEHIND NIGHTY

Behind Nighty: notes on how it was madeNighty 幕后:做这个 App 时的思考与调优记录

Nighty is a box of slow, dim pixel games for the last ten minutes before sleep, built because every game I tried was designed to keep me up.

Nighty 是一盒节奏慢、画面暗的像素小游戏,给睡前最后十分钟玩。做它,是因为我试过的游戏都在想办法让我别睡。

I made the product calls and designed the workflow; AI agents wrote most of the code inside it. This page is about that split: where I put the human checkpoints, what the agents got wrong, and the rules that came out of it.

产品上的决定和工作流程由我来定,大部分代码是 AI agent 在这套流程里写的。这页讲的就是这个分工:人工检查点放在哪里,agent 在哪些地方出过错,以及从中定下的规矩。

374 commits · 559 tests · 19 specs · 22 plans · EN / ES / 简体中文374 个 commit · 559 个测试 · 19 份规格 · 22 份计划 · 英 / 西 / 简体中文
Status: submitted to the App Store.状态:已提交 App Store 审核。

How I organize the AI我怎么组织 AI 干活

I own the two ends of every round: what gets built, and whether it is good enough to ship. The usual loop: I notice a problem and give a direction; the AI proposes options; I pick one or push back, and settle the approach and the parameters; agents implement and review; I accept it on the phone.

每一轮的两头在我手里:做什么,以及做到什么程度可以上线。通常的节奏是:我发现问题、给出方向;AI 拿出几种方案;我判断、提别的想法,定下方案和参数;agent 实现并审查;最后我在真机上验收。

All of it ran in Claude Code, following a brainstorm → spec → plan → subagent pipeline. I used my own workflow kit, which drew on the Superpowers pipeline, with my own pieces on top: the model split below, the CLAUDE.md constraints, the rules in 04 and the phone gate. Each feature starts as a conversation that ends in a written spec, then a step-by-step plan. Each task in the plan goes to a fresh subagent that has no memory of earlier tasks: Haiku when the plan already contains the code to write, Sonnet when the task needs judgment or Haiku gets stuck. A Sonnet reviewer checks every task against the spec, and for code quality, before the next task starts. At the end, Opus reviews the whole branch. Cheap models do the volume; the strongest model goes where a miss costs the most.

全程在 Claude Code 里完成,按头脑风暴 → 规格 → 计划 → 子代理这条线做。用的是我自己的工作流 kit,它参考了 Superpowers 的流水线,并加了我自己的部分:下面说的模型分工、CLAUDE.md 约束、04 里的规矩和真机闸门。每个功能先聊清楚、写成规格,再拆成逐步的计划。计划里每个任务交给一个全新的子代理,它不记得之前的任务:计划里已经写好代码的用 Haiku,需要判断或者 Haiku 卡住了用 Sonnet。每个任务做完,由 Sonnet 审查者对照规格检查、再看代码质量,通过了才做下一个。最后由 Opus 审查整个分支。量大的活交给便宜模型,最强的模型放在漏掉代价最大的地方。

After Nighty, I folded what it taught me back into that kit and open-sourced it as the Claude Code workflow kit (v2): risk-tiered specs, plan-first, TDD, a verify gate enforced by hooks, and a lesson-capture loop. Some of it is newer than Nighty: the hook-enforced gate came in v2, and the rules in 04 were added by hand at the time. The kit's lesson-capture skill writes that habit down: a failure becomes a rule once it has happened more than once and fits in one sentence.

Nighty 做完后,我把这次的经验补回这套 kit,优化后开源成了 Claude Code workflow kit(v2):按风险分级的规格、先写计划、TDD、用 hooks 强制执行的验证闸门,以及从失误里总结规矩的闭环。其中有些是 Nighty 之后才有的:hooks 强制执行的验证闸门是 v2 加的,04 里的规矩当时是手动加进来的。kit 里的 lesson-capture skill 把这个习惯写成了流程:一个失误重复出现、并且能用一句话说清,才变成规矩。

Decisions I made我做的判断

01Throwing away the first look推翻第一版外观

The first version looked like a monochrome LCD handheld. It worked, but I didn't want to play it myself, and an app I wouldn't open was not going to win anyone else over, so I asked for a redesign. Round one compared three directions (night light, quiet ink, cartridge) and I picked cartridge. Round two still showed single-color blocks once you were inside a game, so I rejected it. Round three gave every game its own small world and art style, and that became the app. My only change request on round three: English and Spanish game titles must fit on one line.

第一版是单色液晶掌机的样子。能用,但我自己都不想玩,我自己都不想打开的 App 也打动不了别人,于是要求重做。第一轮比了三个方向(夜灯、静墨、卡带),我选了卡带;第二轮进游戏后还是单色方块,我否掉了;第三轮给每款游戏一个自己的小世界和画风,就是现在的 App。第三轮我只提了一处返修:英文、西语的游戏名必须单行。

Round 2 draft第二轮原稿 · Round 3 draft第三轮原稿

02Measuring the maze before fixing it迷宫先量化再改

The mazes felt like one corridor with stubs. Tuning by feel would have left me unable to tell whether a change helped, so I asked for numbers first: on the old generator, "always walk toward the exit" was within a few percent of the shortest path. We built a browser test bench, I tuned the parameters there by playing, and a golden test checks the Swift version against it cell for cell.

迷宫玩起来像一条走廊加几截短岔路。凭感觉改,就没法判断一次改动到底有没有用,所以我先要数据:旧算法下「一直朝出口走」只比最短路多走几个百分点。然后做了网页试验台,我在上面边玩边调参,最后用对照测试核对 Swift 版本与试验台逐格一致。

Full case →完整案例 → · Test bench试验台

03A ball that never reached the middle打砖块:球永远打不到中间

On the phone, the Brick Breaker ball sometimes circled forever without touching the bricks in the middle. The cause: the ball only ever moved at 45°, and on a grid with reflecting walls that allows closed loops around a centered pattern. It was reproduced with a deterministic probe that prints every frame as text: 4,000 steps, zero hits, locked into a loop from step 38. The fix gives the ball variable slopes depending on where it hits the paddle. Because the probe is deterministic, the bug reproduces on every run, so the same setup became a regression test: across 8 seeds, the ball now hits the pattern and clears the level within a bounded number of steps.

真机上打砖块的球有时会一直绕圈,碰不到中间的砖。原因是球只会走 45°,在会反弹的网格里,这能形成绕开中间图案的闭合轨道。复现用的是确定性探针:每一帧打印成文字,跑 4000 步零击中,第 38 步起进入闭环。修复是让球根据打在挡板上的位置有不同斜率。探针是确定性的,每次运行都能复现,所以同一套设置直接变成了回归测试:8 个种子下,球都能在有限步数内打中图案并过关。

04Rules that came out of mistakes从失误里来的规矩

  • First level first. For any multi-level game, level one is built as a tunable prototype and accepted on a real device before more levels are produced. For endless difficulty curves, check at least three points (start, middle, near the cap), because level one only tells you where the curve starts.
  • 第一关先行。多关卡游戏先把第一关做成可调参数的原型,真机验收通过才铺开后续关卡。程序化难度曲线至少看三个点(起始、中段、接近上限),因为第一关只能告诉你起点。
  • A regression test must fail before the fix. In one round, three tests passed without actually exercising the bug they were written for. Since then, every bug-fix task includes reverting the fix and watching the test go red.
  • 回归测试必须先确认会红。有一轮里三次出现测试是绿的、但根本没测到要测的 bug。从那以后,每个修 bug 的任务都要先把修复回退,看着测试变红。
  • The phone is the gate. A round only merges after I've gone through its checklist on a real phone, because green tests had already missed real bugs.
  • 真机是闸门。每一轮都要我在真机上逐条过完验收清单才合并,因为测试全绿已经漏过真实的 bug。

05Product calls产品取舍

  • The bedtime reminder never forces you out. Tapping "OK, time to sleep" fades the screen slowly.
  • 睡前提醒不强制退出,点「好,休息了」后屏幕慢慢变暗。
  • Brightness can go below the system minimum, because the app is meant for a dark room.
  • 亮度可以压到比系统最低档还暗,因为它就是在黑屋子里用的。
  • Version 1.0 leaves out the China mainland storefront instead of waiting weeks for an ICP filing.
  • 1.0 先不上中国大陆,不为等 ICP 备案拖几周。

06Being talked out of an idea被说服放弃一个想法

I suggested labeling the new serve button "S". The AI argued against it: the other two buttons are A and B, so a letter would read as a third action button; serving only works while the ball is waiting, so a mistap costs nothing and the button doesn't need a distinct look to prevent mistakes. I agreed. It became a round ▶ button the same size as A and B, with a small caption underneath.

我提议把新加的发球键标成「S」。AI 不同意:另外两个键叫 A 和 B,再加一个字母会被读成第三个动作键;发球只在球待发时有效,误按没有代价,所以不需要靠外形区分来防误触。我被说服了,最后是一个和 A/B 同样大小的圆形 ▶ 键,下面带一行小字说明。

What I learned building with AI agents和 AI agent 一起做东西学到的

Engineering notes工程结构

Together, the seeded logic, the maze golden test, the frame-by-frame probe and the pixel-diff tool work as an eval suite: every claim about behavior can be reproduced and checked.

种子注入的逻辑、迷宫对照测试、逐帧探针和逐像素比对工具合在一起,就是一套评估体系:每一条关于行为的说法都能复现、能核对。

public protocol PixelGame {
    static var info: GameInfo { get }
    init(seed: UInt64)
    mutating func handle(_ input: GameInput)
    mutating func tick(_ deltaTime: TimeInterval)
    var frame: PixelFrame { get }
    var state: GamePlayState { get }
}

What I'd validate after launch上线后想验证的

The app collects no data by design, so these can't come from in-app analytics.

App 刻意不收集任何数据,所以这些问题没法靠 App 内的统计来回答。

Archived drafts原稿存档

These are the pages I used to make decisions at the time, unedited apart from a note at the top. They are in Chinese.

这些是当时用来做决定的页面,除了顶部加一条说明,没有改动。

Cartridge redesign, round 2卡带改版 · 第二轮Rejected: games still looked monochrome否掉:进游戏仍是单色
Cartridge redesign, round 3卡带改版 · 第三轮Approved: an art style per game定稿:每款一个画风
Push Boxes level-clear screen推箱子过关画面Picked C, the full-moon badge选 C 满月徽章
Sound audition, round 1音色试听 · 第一轮Harp and soft xylophone竖琴 + 轻木琴
Sound audition, round 2音色试听 · 第二轮Buttons told apart by pitch按键靠音高区分
Icon, opening scene, settings图标、开场、设置页Icon E + opening ④图标 E + 开场④
Website redesign官网改版Option A, the opening story方案 A 开场故事
Maze test bench (EN / 中文)迷宫试验台(中 / EN)Parameters tuned here参数在这里调定