Dayward AI
Week 1 · D3About 4 hours

The Initializer Agent: init.sh, a Progress File and the First Commit

The first minute of an unattended run decides how efficient the next several hours are. Today initialization becomes a phase of its own: a script that gets the environment running, a progress file written for whoever comes next, and a clean initial commit, so that any fresh window is productive within three minutes.

Today's goals 0/3

Sign in to tick these off and save your progress.

Today's Goals

  1. Can name the three artifacts the initialization phase must produce, and explain which failure mode each one kills
  2. Can explain why initialization is a separate phase instead of something the main loop does on its first pass, and name two concrete problems that follow from mixing them
  3. Can judge whether a workspace is good enough against a checkable standard: can any fresh window get it running and know where the work stands, inside three minutes

D2 moved progress out of the context window. Today moves a second kind of knowledge that the window boundary erases just as thoroughly: knowledge about the environment. When you finish, scroll back up and tick off the three goals.

Plain-Language Walkthrough

A new hire's first day: where the docs are, how to run it, where the last person left off

Think back to your first day at a company. Suppose nobody prepared anything for you, so you have to work out three things by yourself: where the code is, how to get it running, and where the last person left off.

On a good day those three things cost you half a day. On a bad day, when the README is two years old, the start command lives in somebody's chat history, and the last person has already left the company, they cost you two days.

Now change one thing about the scene: every new hire stays for three hours, and then the next one walks in.

That is the real shape of an unattended run. Eight hours overnight at one hour per context window is eight separate first days. If each new hire burns half an hour finding their footing, half your compute for the night went into working out the same thing over and over, and every time the answer came out identical.

The industry solved this a long time ago, in the ordinary world: an onboarding doc, one command that actually runs, and a handover note. Today's job is to make the harness produce all three before any work starts.

Four failure modes, four mechanisms

Time to look back at that table from D1. The Anthropic engineering blog groups the failure modes of long-horizon agents into four classes, and pairs each one with a mechanism. Today lands two and a half of the four.

Failure modeMechanismLands on
Burning time working out how to start the applicationinit.sh, read at the top of every sessionD3 (today)
Leaving defects in the environment with no documentationAn initial git repo plus a progress note, committed at every wrap-upD3 (today)
Declaring victory too earlyA feature list file, where a line only flips to passing after it is verifiedD4
A feature marked complete that was never actually testedOnly an end-to-end check may change that line's statusD5

Note how the table is meant to be used: the failure mode and the mechanism have to be read as a pair. On its own, "write an init.sh" is a rule with no reason attached. Say instead "without it, every fresh window rediscovers the start command from scratch", and it becomes a design decision with a cause behind it.

For every harness practice you meet from here on, you can turn the question around and ask: which failure is this blocking? A practice nobody can answer that for was usually copied from somewhere else.

The three artifacts: three things someone else can open

What the initialization phase delivers is not an explanation. It is three things another party can open.

First, init.sh. It answers "how do I run this". Why does it have to be an executable script rather than a paragraph in a README? Because a paragraph can only be read, while a script can be verified. The harness can actually run it once, and if it does not run, the harness knows the workspace is not ready. There is no way to do that with a paragraph.

Second, the progress note. It answers "how far did this get, and what comes next". It shares its source of truth with the context summary from D2, but it has a different reader: the summary is written for the model inside the next window, so it puts conclusions first and skips connective prose, while the note has to read from top to bottom for a person who just walked in.

Third, a commit. It puts the first two under version control. The full value of this one is not visible today, because what it really provides is reliable ground for every rollback later on. D4 makes that argument properly.

Why initialization has to be a phase of its own

This is the one structural decision that matters today, and the reason is a single sentence:

Mixed into the main loop, its output lands in the context window. Made into a phase of its own, its output lands on disk.

Anything that lands in the window does not survive the window boundary. So the next window probes again, and the one after that probes again, and every probe returns exactly the same answer. This is the D2 lesson replayed over a different class of information: it is not that nobody knew. It is that nobody wrote it down outside the window.

In the lab that difference is measurable. Same code, same offline model, one variable: where initialization happens.

TextText
completed items         6 -> 9
environment probed      3 times -> 1 time
onboarding artifacts    none -> init.sh and PROGRESS.md, both under version control
commit history          1 -> 5

Mixing them has a second cost that is subtler than waste: initialization and ordinary work do not fail the same way. An initialization failure means the premise of the whole run is false, and the correct response is to stop and raise an alarm. A single feature failing means that one item did not land, and the correct response is to record it and pick the next one. Put both inside the same loop and the two failures travel down the same error path, so "the environment never came up at all" gets handled as "this feature is a bit hard", and the harness cheerfully moves on to the second item. In the morning you have forty attempts and a service that never started once.

The three-minute standard

"The workspace should be friendly to fresh windows" is a correct statement that does no work. To make it useful, it has to be checkable.

The standard this course uses is: any fresh session is productive inside three minutes. It unpacks into three concrete actions:

  1. One command tells you how to run the project, with no source reading and no guessing
  2. One file tells you where the work stands and what comes next
  3. One glance at the commit history tells you what just happened

And the way to accept it is to actually do it, not to look at the artifacts and nod. In the lab self-test, that standard takes this shape: spawn a real process running bash init.sh start, poll the port, and send a real HTTP request.

Source Reading

One new module today, src/init/initializer.ts. Three places are worth slowing down for.

Position one: what the artifact looks like. The generated init.sh has exactly two subcommands, and both are deliberately thin.

render-init.js
// The generated init.sh looks roughly like this. What the template embeds are
// probed values, not hard-coded constants
const script = `#!/usr/bin/env bash
# notekeeper onboarding script, generated by the nightrun initializer. Do not hand-edit.
#
#   bash init.sh check   syntax self-check, should pass at any time
#   bash init.sh start   start the service, PORT overrides the default ${probe.defaultPort}
set -euo pipefail
cd "$(dirname "$0")"
 
case "\${1:-help}" in
  check) node --check ${probe.entry} ;;
  start)
    export PORT="\${PORT:-${probe.defaultPort}}"
    exec ${probe.startCommand}
    ;;
esac
`
writeFileSync(scriptPath, script, 'utf8')
// The executable bit is part of the artifact. Without it, the first thing a
// fresh session hits is permission denied
chmodSync(scriptPath, 0o755)

Two details deserve attention. The check subcommand looks useless until you see what it buys: it separates two classes of failure. If the syntax check passes, the code itself is intact and the service failing to start means the feature is not built yet. If the syntax check fails, somebody broke a file. Without that dividing line, two completely different problems arrive as the same error message.

The other one is cd "$(dirname "$0")", which lets the script work no matter which directory it is invoked from. And the script never calls git. It gets invoked from arbitrary directories, so putting a git command inside it means depending on an inherited working directory, and that is a red line in this course. Every git call the harness makes goes through runGit(workdir, args).

Position two: the two points where initialization attaches to the main loop.

init-hooks.js
// Attachment one: outside the main loop. It happens once for the whole run
const initResult = initializeWorkspace(repoDir, state, features)
 
for (let w = 1; w <= config.windows; w += 1) {
  transcript.length = 0
 
  // Attachment two: at the opening of every window, read the onboarding
  // commands back out of init.sh on disk. The order is invariant first,
  // variable second: onboarding holds all night, progress changes every window
  const initSummary = readInitSummary(repoDir)
  const withInit = initSummary === null ? base : `${base}\n\n${initSummary}`
  const opening = rebuildContext(withInit, loadState(stateDir), features)
 
  // ...run this window's steps...
 
  // Attachment three: at wrap-up, rewrite the progress note and commit.
  // That is why git log by itself reads as a progress line
  commitProgress(repoDir, state, features, probe, `chore: window ${w} wrap-up`)
}

Notice that readInitSummary reads the file on disk, not an in-memory copy of the probe result. The reason is the same one as in D2: only a disk read earns this boundary the right to become a process restart in D6. One self-test assertion moves init.sh aside and confirms the function returns empty on the spot. An implementation that read the in-memory copy would stay green on every other assertion, which is precisely why that one exists: it discriminates on where the data came from.

Position three: how one sentence of requirements becomes dozens of checkable items. The forty-line feature list the target ships with is the product of that expansion, and the way it is organized is worth a look: twenty functional items, twelve validation items, eight error-handling items, and the dependencies are written into the wording of the descriptions ("depends on the listing from F02", "must come before the F10 pagination"), rather than living in a separate dependency field.

That is not laziness. The description is the only specification the model ever sees, so a dependency has to be something it can read. Put it in a field that only the harness understands, and the model is blind to it. The same trade-off comes up again in D4, where features.json becomes the driver of task selection.

Hands-On Lab

🧪 Day 3 lab: the initializer, turning onboarding into artifacts on disk

Code location: labs/agent-harness-7days/day-03-initializer

There are five exercises, all in src/init/initializer.ts: generate init.sh, generate PROGRESS.md, run the initialization phase, commit at window wrap-up, and produce the onboarding block the opening reads. The frozen files and the D2 state layer come across verbatim; not one character changes.

  1. Read the three attachment points in src/core/loop.ts first, and see clearly which one happens outside the main loop.
  2. Fill in renderInitScript and renderProgress. The usage lines must start with a hash and three spaces, and the progress note needs exactly five second-level headings.
  3. Fill in initializeWorkspace. Do not forget the executable bit, and route every git call through runGit.
  4. Fill in commitProgress and readInitSummary. The first must not commit when nothing changed, and the second must read from disk.
  5. Run MOCK=1 pnpm selftest until all 35 assertions are green, then run MOCK=1 pnpm start and compare the two modes.

When it finishes, look at the commit history in work/phase/repo: five commits, being the empty shell, the initialization, and three window wrap-ups. That log is itself a progress line you can read, and it is the setup for the reversal in D4.

Today's mutation check targets a silent failure. In renderInitScript, change the prefix on the two usage lines from a hash and three spaces to a hash, a space and a hyphen, and change nothing else. Run again, and the onboarding block in the window opening comes out like this. The lab prints it in Chinese, because both editions of this course share one codebase; the shape is what matters:

TextText
| -- Onboarding, read from init.sh in the repo rather than recalled --
| These commands hold all night, so there is no need to work them out again:
| The progress note is in PROGRESS.md, the commit history is in git log.

Both commands are gone. The block is still there, the heading is still there, the init.sh file is still intact, and only the part that was actually useful quietly disappeared. No exception, no warning.

Interview Questions

Today's four questions circle the division of responsibility around the initialization phase, reader-awareness in a progress file, and the skill of turning "good enough" into a checkable standard.

The fourth one is worth extra practice. For a question like "how would you judge whether a workspace is friendly enough for an agent", answering "good documentation, clear structure" is close to not answering at all. What the interviewer is waiting for is a procedure somebody else could run. The same habit of thought applies in evaluation, in acceptance testing, and in writing an SLO.

Checklist and Tomorrow

  • Can name the three artifacts the initialization phase must produce, and explain which failure mode each one kills
  • Can explain why initialization is a separate phase instead of something the main loop does on its first pass, and name two concrete problems that follow from mixing them
  • Can judge whether a workspace is good enough against a checkable standard: can any fresh window get it running and know where the work stands, inside three minutes
  • Can say why the progress note is regenerated rather than maintained incrementally, and why a stale progress file is worse than none
  • Got all 44 self-test assertions green with MOCK=1 pnpm selftest, and saw the commit history difference between the two modes with your own eyes
  • Ran the usage-line prefix mutation check once, and can describe the difference between an assertion on the container and one on the contents
  • Can answer at least three of the four interview questions without looking at the key points

Tomorrow is D4, One Thing at a Time: The Feature List and Git Discipline. It opens on a reversal. If you took the course on hand-building a Coding Agent, you will remember an explicit ruling there: do not commit with git, because polluting the user's repository history is not yours to do. Tomorrow's conclusion is the exact opposite: a git commit is the authoritative record of progress, and the agent commits by itself. Both are right, because the repository being operated on is not the same repository. That course touches the user's repository; this one touches the agent's own workspace. Once that premise is clear, you can see why the same question has two opposite correct answers in two settings.

Interview questions

  • Before letting an agent develop a project unattended overnight, what do you have it do first?让一个 Agent 无人值守地开发一个项目,开工前你会先让它做哪几件事?
    Common in ChinaCommon overseasIntermediate#initialization#failure-modes#long-horizon

    How to reason about it · think before answering

    1. This tests whether you treat pre-flight as a design object at all. Most people answer with a better prompt or a fuller toolset, which is the answer to an interactive-agent question: when a human is sitting there, environment gaps get patched on the spot. Run unattended overnight and every unpatched gap gets rediscovered by every window that follows.
    2. The right approach is to work backwards from failure modes rather than listing habits. The primary source groups long-horizon failures into four classes, two of which can be eliminated in the first minute: time wasted figuring out how to run the app, and defects left in the environment with no documentation. Each gets its own mechanism. The other two - declaring victory early, and marking work done without really testing it - belong to the checklist and the end-to-end gate later, not to initialization.
    3. Against the first failure the mechanism is an `init.sh`. State the trade-off explicitly: why must it be an executable script rather than a paragraph in the README? Because a paragraph can only be read, while a script can be verified - the harness can actually run it once, and if it fails you know the workspace is not ready. You cannot do that to prose, and unattended there is nobody to read the prose anyway.
    4. Against the second failure the mechanism is a progress note plus an initial commit, two halves of one thing. The note answers where things stand and what comes next. The commit puts both artifacts under version control, and its value is not fully visible on day one - it gives every later rollback a trustworthy floor. Without that floor, rolling back to the last good point has nowhere to land.
    5. So the answer is three artifacts: `init.sh`, a progress note, a commit. What is worth saying out loud is what they share - the deliverable of initialization is not an explanation, it is three things somebody else can open. Offer a portable test alongside it: for any harness practice, ask which failure it blocks. A practice with no answer was usually copied from somewhere.
    6. Expected follow-up: is three too thin - should it also run the tests first, or read the whole codebase? Quantity is not the point; verifiability is. Add ten more artifacts and if nobody has run them their credibility is still zero. Volunteer the discipline here: initialization artifacts are generated by an agent, and agents produce scripts that look entirely correct and do not run. So the harness must execute what was generated. The model saying it works does not count.

    分析过程 · 先想清楚再作答

    1. 这题考的是有没有把「开工前」当成一个独立的设计对象。多数人会答「写个好提示词」「把工具配齐」——那是在答交互式 Agent 的问题:人在旁边时,环境有什么坑你当场就补上了。人不在场跑一整夜,没补上的每一个坑都会被后面每个窗口重新踩一遍。
    2. 正确的拆法是**从失败模式倒推**,而不是凭经验列清单。一手资料把长时程 Agent 的失败归成四类,其中两类在开工第一分钟就能被消灭:**浪费时间摸索怎么把应用跑起来**,以及**在环境里留下缺陷且没有文档**。这两类各配一个机关,其余两类(过早宣布胜利、标记完成但没真测)要靠后面的清单与端到端闸门,不属于初始化。
    3. 对着第一类失败,机关是一个 `init.sh`。这里有个必须讲清楚的取舍:**为什么必须是可执行脚本,而不是 README 里的一段话?** 因为一段话只能被读懂,脚本可以被**验证**——harness 能真的去跑它一次,跑不起来就知道工作区还没准备好。这件事在一段文字上做不到,而无人值守场景里没有人会替你读那段文字。
    4. 对着第二类失败,机关是**进度笔记加一个初始 commit**,两件事是一半一半。笔记回答「跑到哪了、下一步做什么」;commit 把前两样放进版本库,它的意义在第一天还看不全——它是给后面所有回滚一个**可靠的地面**,没有这个地面,「回到上一个可用点」就无处可回。
    5. 所以结论是三件套:`init.sh`、进度笔记、一个 commit。而更值得说出口的是它们的共性——**初始化的交付物不是一段说明,是三件能被别人打开的东西**。顺带给出一条可以带走的反问式判据:遇到任何一条 harness 的做法,问一句「它挡的是哪种失败」,答不上来的做法通常是抄来的。
    6. 可预期的追问是「三件套是不是太单薄了,要不要再让它先跑一遍测试、先读一遍全部源码」。数量不是重点,**能不能被验证**才是:再加十件产物,只要没人跑过它们,它们的可信度都是零。这里有一条纪律必须主动说——初始化产物是**由 agent 生成的**,而 agent 会写出看起来完全正确却跑不起来的脚本,所以 harness 必须自己跑一遍生成物,模型说写好了不算数。

    Key points

    • Three artifacts: init.sh, a progress note, an initial commit - all things someone else can open.
    • Derive them from failure modes: init.sh blocks time wasted figuring out how to run the app.
    • The progress note plus the initial commit block defects left with no documentation.
    • It must be an executable script, not prose: prose can only be read, a script can be verified.
    • The initial commit gives every later rollback a trustworthy floor to land on.
    • Portable test: every harness practice must name the failure mode it blocks.
    • The artifacts are agent-generated, so the harness runs them itself. The model's word does not count.

    答题要点

    • 三件套:init.sh、进度笔记、一个初始 commit,三件都是能被别人打开的东西。
    • 按失败模式倒推:init.sh 挡「浪费时间摸索怎么把应用跑起来」。
    • 进度笔记加初始 commit 挡「在环境里留下缺陷且没有文档」。
    • 必须是可执行脚本而不是一段话:一段话只能被读懂,脚本可以被验证。
    • 初始 commit 的意义是给后面所有回滚一个可靠的地面。
    • 通用判据:任何一条 harness 做法都要答得出它挡的是哪种失败。
    • 产物是 agent 生成的,harness 必须自己跑一遍——模型说写好了不算数。
  • What belongs in a progress file, and how does a human-readable log differ from a summary written for the next window?进度文件应该写什么?写成给人看的日志和给下一个窗口看的摘要有什么区别?
    Common in ChinaCommon overseasIntermediate#progress-file#state-management#long-horizon

    How to reason about it · think before answering

    1. This tests audience awareness. Answering 'record what was done' merely describes a log and earns nothing. The interviewer wants to hear that the file has two kinds of reader, and that the same facts get organized differently for each - not different wording, but a different position for the conclusion and a different way of referring to work.
    2. Lay out the content first; it is short. Five questions, five answers: how to run it, how many items are complete, which item is next, what counts as complete, and which pitfalls are already known. The last two get skipped most often. 'What counts as complete' defines the acceptance standard - without it the next window declares success on its own terms. 'Known pitfalls' carries the lessons that only mean anything across windows, such as an item already attempted three times without success, so stop retrying it unchanged.
    3. Now the reader difference. The summary for the next window puts conclusions first, refers to work by feature id rather than description (ids are stable, descriptions drift with requirements), and states outright that earlier conversation no longer exists - omit that and the model assumes it missed something and goes hunting for history that is not there. The human log has to be readable start to finish by someone just picking the work up, so it can afford transitions and background. Same numbers, different register - that sentence alone is a good answer.
    4. The real discriminator is the next conclusion: the progress file must be regenerated every time, never maintained incrementally. A hand-maintained file will drift - nobody remembers to update it every time, and when they do nobody checks it against reality. Regeneration means it cannot lie, because every number in it comes from the same authoritative state.
    5. Pin down why that matters with one line: a progress file with wrong content is worse than no progress file at all. With no file, the next window knows it has to go look. With a stale file, it acts on what it reads. The first wastes minutes; the second produces a night of wrong work, and nothing anywhere raises an error.
    6. Give a verifiable implementation: rewrite the whole file at every window close, then write one assertion - hand-edit a line, trigger another close, and that line must be gone. The assertion pins the regeneration property itself rather than judging whether one particular output reads nicely.
    7. Expected follow-up: does a full rewrite throw away history? History lives in the commit log, which is append-only and does not drift. Making one repeatedly rewritten text carry both current state and historical record is exactly where it starts lying. Split the two responsibilities across two media and each one can be correct.

    分析过程 · 先想清楚再作答

    1. 这题考的是**读者意识**。答「记录做了什么」只是在描述日志,拿不到区分度。面试官想听的是:你知道这份文件有两类读者,而同一批事实要按读者换一种组织方式——不是换一套文案,是换结论的位置和引用的方式。
    2. 先把内容摆出来,它其实很短,五问五答:怎么把它跑起来、现在完成了几条、下一步做哪一条、怎么才算一条做完了、已知的坑有哪些。注意最后两条容易被漏掉:「怎么算做完」定义的是验收口径,没有它,下一个窗口会按自己的理解宣布通过;「已知的坑」承载的是跨窗口才有意义的教训,比如某条已经试过三次仍然没成,别再原样重试。
    3. 然后是读者的差别。**给下一个窗口看的摘要**:结论前置、用 feature 编号而不是描述文本(编号稳定、描述会随需求改)、并且要明说更早的对话已经不存在——不说这句,模型会以为自己漏读了什么而去翻不存在的历史。**给人看的日志**:要能被一个刚接手的人从头读到尾,可以有过渡句、可以解释背景。**两者数字同源、口吻不同**,这是一句可以直接答出去的总结。
    4. 真正拉开差距的是下一条结论:**进度文件必须每次重新生成,不能增量维护。** 手工增量维护的文件一定会漂移——没人记得每次都改,改了也没人核对它和真实进度是否一致。重新生成意味着它不可能说谎,因为它的每个数字都来自同一份权威状态。
    5. 为什么这么严重,要用一句话钉死:**一份内容错误的进度文件比没有进度文件更糟。** 没有文件时,下一个窗口知道自己得去查;有一份过期文件时,它会照着做。前者浪费几分钟,后者产出一整夜的错误工作,而且没有任何东西会报错。
    6. 落到实现上给一条可验收的做法:每次窗口收尾把这份文件整个重写,然后配一条自检——手改其中一行,再触发一次收尾,那一行必须被覆盖掉。这条断言的好处是它直接钉住「重新生成」这个性质本身,而不是去评判某一次输出写得好不好看;输出的措辞可以随时改,性质不能丢。
    7. 可预期的追问是「整个重写不就把历史丢了吗」。历史在提交历史里,而且那份历史是 append-only、不会漂移的。让一份会被反复重写的文本同时承担「当前状态」和「历史记录」两个职责,正是它开始说谎的起点——两个职责分给两个介质,各自都能做对。

    Key points

    • Five questions: how to run it, how many done, what is next, what counts as done, known pitfalls.
    • Summary for the next window: conclusions first, feature ids, and say earlier conversation is gone.
    • Human log: readable end to end, transitions allowed. Same numbers, different register.
    • The file must be regenerated each time; incremental maintenance always drifts.
    • A wrong progress file is worse than none: with none you go look, with a stale one you act on it.
    • Verifiable assertion: hand-edit a line, close a window, and that line must be overwritten.
    • Rewriting loses nothing: history belongs to the commit log, one text should not carry both jobs.

    答题要点

    • 五问五答:怎么跑起来、完成几条、下一条做什么、怎么算做完、已知的坑。
    • 给下一个窗口的摘要:结论前置、用 feature 编号、明说更早的对话已不存在。
    • 给人看的日志:能从头读到尾,可以有过渡与背景。两者数字同源、口吻不同。
    • 进度文件必须每次重新生成,增量维护一定漂移。
    • 一份内容错误的进度文件比没有更糟:没有时会去查,过期时会照着做。
    • 可验收的断言:手改一行再触发收尾,那一行必须被覆盖掉。
    • 整个重写不丢历史:历史归提交历史,一份文本别兼两个职责。
  • Why make initialization its own phase instead of letting the first pass of the main loop handle it?为什么初始化要独立成一个阶段,而不是让主循环第一轮顺手做掉?
    Common in ChinaCommon overseasDeep dive#architecture#initialization#failure-handling

    How to reason about it · think before answering

    1. This probes structural judgment, not recall, so 'cleaner' and 'more modular' are non-answers - they hold for any split whatsoever. You need the consequence specific to this split, ideally backed by a measurable difference.
    2. The reason compresses to one sentence worth delivering verbatim: mixed into the main loop, its output lands in the window; as its own phase, its output lands on disk. What lands in a window does not survive the window boundary, so the next window probes again, and the one after that probes again, and every probe returns exactly the same answer.
    3. The first concrete problem is repeated probing, and it is measurable. Same code, same offline model, the only difference being where initialization lives: probe count drops from three to one, completed items rise from six to nine. Note where that gap comes from - both runs discovered identical facts; only the destination differed. Eight windows a night, each spending half an hour re-establishing the same thing, means half your compute went into repetition.
    4. The second problem is subtler and is where this question actually separates people: initialization failure and feature failure do not mean the same thing. Initialization failing means the premise of the whole run does not hold, and the correct response is to stop and raise an alarm. One feature failing means just that one did not land, and the correct response is to record it and move to the next. Share one loop and both take the same error path, so 'the environment never came up' gets treated as 'this feature is a bit hard' and the harness cheerfully starts the second one. In the morning you have forty attempted items and a service that never started.
    5. The third benefit comes free but yields a transferable test: initialization runs exactly once and is idempotent, so it can be retried, verified and cached on its own. A step inside the main loop can do none of those three. Inverted: anything that runs once and must be redone wholesale on failure usually deserves to be its own phase - the same call holds in CI, in data pipelines, in deployment.
    6. Expected follow-up: is reading init.sh at every window opening not also repetition? Not the same kind. Reading a file on disk is one deterministic constant-cost action; probing is open-ended trial and error whose cost and conclusion both vary. Volunteer one implementation discipline here too - that opening section must be read from disk, not assembled from an in-memory copy of the probe result, or the boundary loses its right to become a real process restart later.
    7. Another follow-up: what actually happens when initialization fails? The semantics above answer it - halt and alarm, never enter the main loop. That is where the split pays for itself: only a separate phase is allowed separate error handling. Merged, you do not even have a place to express the distinction.

    分析过程 · 先想清楚再作答

    1. 这题考的是结构判断力,不是知识点,所以答「更清晰」「更模块化」等于没答——那两句对任何拆分都成立,换成把日志抽出来、把配置抽出来同样说得通。要给出的是**这个拆分特有的后果**:不这么拆会具体坏在哪里,而且最好能用一个可测量的差把它顶起来。
    2. 理由可以压成一句话,值得原样答出去:**混在主循环里,它的产物会落在窗口里;独立成阶段,它的产物落在磁盘上。** 落在窗口里的东西活不过窗口边界,于是下一个窗口再探一次、再下一个窗口再探一次,而每次探出来的答案完全一样。
    3. 第一个具体问题是**重复摸索**,而且是可测的。同一份代码、同一个离线模型,唯一的差是初始化放在哪:探测次数 3 降到 1,完成条数 6 升到 9。注意这个差的来源——两趟探出来的结论一模一样,差别只在它被写到了哪里。一夜八个窗口,每个窗口花半小时重新搞清楚同一件事,就是一半算力花在了重复上。
    4. 第二个问题更隐蔽,也是这题真正的分水岭:**初始化失败与 feature 失败的语义根本不同。** 初始化失败意味着整个运行的前提不成立,正确处理是停下来报警;某一条 feature 失败只是这一条没做成,正确处理是记下来换下一条。混在同一个循环里,两者会走同一条错误处理路径——于是「环境根本没起来」被当成「这条 feature 有点难」,harness 兴高采烈地接着做第二条。早上你会拿到四十条「已尝试」和一个从来没启动过的服务。
    5. 第三个好处是顺带的,但它给了一条能迁移的判据:初始化**只跑一次而且幂等**,所以它可以被单独重试、单独验证、单独缓存;主循环里的一步这三件事一件都做不到。反过来说,一个东西只要满足「只跑一次、失败了要整个重来」,它通常就该是一个独立阶段——这条在 CI、在数据管道、在部署流程里同样成立。
    6. 可预期的追问是「每个窗口开场都去读一次 init.sh,不也是重复吗」。不是同一种重复:读一个磁盘上的文件是一次确定的、O(1) 的动作,摸索是不确定的多轮试错,代价和结论都不稳定。这里还有一条实现纪律值得主动提——开场那段上手信息必须**从磁盘读**,不能拼内存里的探测结果副本,否则这条边界在后面就没资格变成一次真正的进程重启。
    7. 另一个追问是「初始化失败了到底怎么办」。按上面的语义就有答案:停机报警,不进主循环。这正是把它独立出来的收益兑现的地方——**独立的阶段才允许有独立的错误处理**,混在一起时你连表达这个区别的位置都没有。

    Key points

    • One-line reason: mixed in, output lands in the window; as a phase, output lands on disk.
    • Problem one is repeated probing, measurable: probes three to one, completions six to nine.
    • Both runs discover identical facts; only the destination differs.
    • Problem two is failure semantics: init failure means halt and alarm, feature failure means move on.
    • On one error path, a dead environment reads as a hard feature - forty attempts and no running service.
    • Third benefit: it runs once and is idempotent, so it can be retried, verified and cached alone.
    • Transferable test: run-once, redo-wholesale work usually belongs in its own phase.

    答题要点

    • 一句话理由:混在主循环里产物落在窗口里,独立成阶段产物落在磁盘上。
    • 问题一是重复摸索,可测:探测次数 3 降到 1,完成条数 6 升到 9。
    • 两趟探出来的结论完全一样,差别只在它被写到了哪里。
    • 问题二是失败语义不同:初始化失败该停机报警,feature 失败只换下一条。
    • 混在一条错误处理路径上,环境没起来会被当成这条有点难,早上拿到四十条已尝试。
    • 第三个好处:只跑一次且幂等,所以能被单独重试、验证、缓存。
    • 可迁移判据:只跑一次、失败要整个重来的事,通常就该独立成阶段。
  • How do you judge whether a workspace is friendly enough for a newly arrived agent? Give an executable test.怎么判断一个工作区对新来的 Agent 足够友好?给一个可执行的检验方法。
    Common in ChinaCommon overseasDeep dive#acceptance-criteria#verification#developer-experience

    How to reason about it · think before answering

    1. This asks whether you can turn a correct platitude into a criterion. 'Good docs, clear structure' is effectively a non-answer - two adjectives, neither of which anyone can go run. The interviewer wants a test someone else can follow, with an unambiguous result at the end. The same habit applies to evaluation, to acceptance, to writing SLOs.
    2. State the criterion: any new session can get going within three minutes. Then immediately decompose it into three executable actions, or it stays a slogan. One, run a single command and know how to start the thing, without guessing from source. Two, read a single file and know where the work stands and what is next. Three, glance at the commit history and know what just happened. Each maps to one artifact, so a failure tells you which artifact is missing.
    3. Now the real point of the question: acceptance means actually doing it, not nodding at the artifacts. Concretely: spawn a real process running the startup script, poll the port, send a real request, and require a response before it passes. The summary line is answerable as is - an artifact existing does not count, an artifact running does.
    4. Explain why you need to be this strict or it sounds like fastidiousness: these artifacts were generated by an agent, and agents write scripts that look entirely correct and do not run. Reading them finds nothing, because at the text level there is nothing to find. The same holds anywhere a model generates configuration, scripts or migrations - the harness must execute the output itself. The model's claim that it works does not count.
    5. Give a concrete pitfall proving that reading cannot replace running, which is where field experience shows: the executable bit. Invoke the startup script through the shell explicitly and it needs no execute permission; invoke it as a path and it does. So forgetting chmod may well be untestable on your own machine, because you happen to use the first form. That class of defect surfaces only when you really run it, and run it the way a new session actually will.
    6. One more layer worth volunteering: the criterion itself can be a tautology. This course has a ready example - the first assertion checked whether the window opening contained the getting-started section. Change the prefix on the usage lines and both commands vanish, yet the assertion stays green, because the section is still there and merely empty. Rewritten to check that each of the two commands is actually in the context, the same mutation turns exactly one item red. Ask yourself while writing assertions: am I asserting the consequence, or the packaging?
    7. Expected follow-up: where does three minutes come from? Answer honestly - the number itself does not matter and no experiment fixes a universal three minutes. Its job is to force the standard into a sequence of actions you can time. The real criterion is those three actions, not the number. Swap in five minutes and not one action changes, which is precisely the proof that the criterion rests on the actions.

    分析过程 · 先想清楚再作答

    1. 这题问的是**把一句正确的废话变成判据**的能力。「文档齐全、结构清晰」基本等于没答——两个形容词,没有一个能被别人拿去跑一遍。面试官等的是一条**别人能照着做、做完有明确结果**的检验方法。这个思路在评估、在验收、在写 SLO 时是同一套。
    2. 先给判据本身:**任何新会话三分钟之内能上手。** 然后立刻把它拆成三个可执行的动作,否则它还是一句口号。一,跑一条命令就知道怎么把它跑起来,不用读源码猜;二,读一个文件就知道做到哪了、下一步做什么;三,看一眼提交历史就知道刚才发生了什么。三条各对一件产物,缺哪一条就知道该补哪件。
    3. 接着是这题真正的重点:**验收要真的做一遍,不是看着产物点头。** 具体做法是 spawn 一个真进程跑起手脚本、poll 端口、发一个真实请求,拿到响应才算过。一句话总结可以直接答出去:**产物存在不算数,产物跑得起来才算数。**
    4. 为什么非得这么狠,理由要说清楚,否则听起来像洁癖:**这些产物是 agent 生成的**,而 agent 会写出看起来完全正确却跑不起来的脚本。你读它读不出问题,因为它在文本层面确实没问题。同理,任何「让模型生成配置、脚本、迁移」的设计里,harness 都必须自己跑一遍生成物——模型说写好了不算数。
    5. 给一个具体的坑来证明「读」代替不了「跑」,这一步最能体现实战经验:**可执行位**。起手脚本如果用 `bash init.sh` 调,不需要执行位;用 `./init.sh` 调才需要。于是漏掉 chmod 这件事在你自己的机器上**很可能测不出来**——你恰好一直用前一种调法。这类缺陷只有真跑、并且按新会话真实的调用方式跑,才会暴露。
    6. 还有一层值得主动提:**判据本身也可能是恒真的。** 本课有个现成的例子——最初那条断言查的是「窗口开场里有没有上手这个段落」,把用法行的前缀改掉之后两条命令一条都不剩,而断言照样全绿,因为段落确实还在,只是里面空了。改成逐条查那两条命令真的在上下文里,注入同样的变异才恰好一项转红。写断言时问自己一句:我断言的是那件事的**后果**,还是那件事的**包装**?
    7. 可预期的追问是「三分钟这个数字怎么定出来的」。老实答:数字本身不重要,也没有实验能定出一个普适的三分钟。它的作用是**逼你把标准翻译成一串能计时的动作**——真正的判据是那三个动作,不是那个数。换成五分钟,三个动作一条都不用改,这恰好说明判据落在动作上而不落在数字上。

    Key points

    • Criterion: any new session gets going within three minutes, decomposed into three executable actions.
    • The three: one command to start it, one file for where things stand, one glance at history for what just happened.
    • Acceptance means really doing it: spawn the script, poll the port, send a real request.
    • An artifact existing does not count, an artifact running does - because an agent generated it.
    • The executable bit is the ready example: shell invocation needs none, path invocation does, so a missing chmod hides locally.
    • The criterion can be a tautology too: asserting the section heading stayed green after the commands vanished.
    • Three minutes is not the point; it forces the standard into actions you can time.

    答题要点

    • 判据:任何新会话三分钟之内能上手,必须拆成三个可执行动作才有用。
    • 三个动作:跑一条命令知道怎么起、读一个文件知道做到哪、看一眼提交历史知道刚发生了什么。
    • 验收要真做一遍:spawn 真进程跑起手脚本、poll 端口、发真实请求。
    • 产物存在不算数,产物跑得起来才算数——因为产物是 agent 生成的。
    • 可执行位是现成的例子:用 bash 调不需要执行位,用路径调才需要,漏掉 chmod 本机测不出来。
    • 判据本身也可能恒真:断言盯了段落标题而不是那两条命令,变异之后照样全绿。
    • 三分钟这个数不重要,它的作用是逼你把标准翻译成能计时的动作。

Comments