O invariante 3 (Lição 16) diz que uma run é content-addressed e replayável. Esta lição é o mecanismo: o id de uma run é um hash do seu spec, então o mesmo spec sempre cai no mesmo diretório; esse diretório é um log de eventos append-only mais um checkpoint, então uma run que crashou retoma; e a VM de plano proíbe as três funções que quebrariam tudo isso — Date.now(), new Date() e Math.random(). Onde tempo real e aleatoriedade real são genuinamente necessários, eles entram por uma costura injetada — um Clock e uma fábrica de ids — nunca um global. Essa única disciplina é o que torna "fazer replay desta run exata" possível.
A identidade de uma run não é um timestamp nem um UUID — é o hash de conteúdo do seu spec. runIdFor(spec) faz o hash da especificação da run, então mudar qualquer campo do spec gera um novo id e, portanto, um novo diretório:
// packages/swarm/src/orchestrator.ts:168 — o id da run é o hash do seu spec const runId = runIdFor(spec); // hash de conteúdo estilo SHA-256 do spec // muda qualquer campo de `spec` ⇒ um runId diferente ⇒ um diretório de run diferente // (packages/swarm/src/types.ts:247-257)
O retorno: identidade é conteúdo. Duas runs do mesmo goal+plan+contract compartilham um diretório e podem retomar uma à outra; um spec ajustado é inequivocamente uma run diferente. Não há id derivado do clock que tornaria "a mesma run" não-encontrável amanhã.
Cada run vive num caminho fixo e previsível com um log append-only e um checkpoint — o substrato para retomar e replay (Lição 16):
// packages/etl/src/run-directory.ts:52-59 — o layout <baseDir>/runs/<runId>/ ├── events.jsonl // append-only; todo evento em ordem ├── checkpoint.json // último estado retomável └── meta.json // fingerprint de goal/plan/contract (validado no --resume)
E os stores content-addressed reforçam: resultados são gravados por SHA-256 sobre JSON canônico (chaves ordenadas), então "re-anexar conteúdo idêntico é um no-op e re-runs convergem" (etl/stores.ts:79-97). A idempotência é estrutural — rodar duas vezes não pode duplicar, e uma run retomada pega exatamente onde o log parou.
Date.now(), new Date(), Math.random() num planoUm módulo de plano (alembic.plan.ts) é a descrição determinística do que rodar. Se um plano pudesse ler o relógio de parede ou jogar dados, duas avaliações do mesmo plano divergiriam — e o replay seria uma mentira. Então a VM de plano rejeita as três na avaliação (complete-map §7.8; CLAUDE.md "Determinism"):
// alembic.plan.ts — estas lançam na avaliação da VM de plano: const id = Date.now(); // ✗ rejeitado — relógio de parede const t = new Date(); // ✗ rejeitado — relógio de parede const r = Math.random(); // ✗ rejeitado — não-determinismo
O erro é nomeado no guia de troubleshooting: "Non-determinism error — remova Date.now(), new Date(), Math.random() do módulo de plano". A VM não confia que você lembre; ela impõe. (Nota: essa proibição é no módulo de plano, não no código de aplicação — um comando de CLI pode ler o relógio à vontade; o plano descrevendo uma run não pode.)
Sistemas reais precisam de tempo real (um curador decidindo "obsoleto após 30 dias") e ids únicos (uma pergunta clarify precisa de um handle). A resposta não é proibi-los — é torná-los uma costura injetada, para que a produção passe o real e um teste passe um fake. Você já viu ambos:
Clock — o curador recebe tempo como um Clock injetado (epoch ms), "nunca Date.now()/new Date() — a VM de plano do motor os proíbe e eles quebram replay determinístico" (curator/types.ts:26-29). Um teste passa um clock fixo e as transições active→stale→archived ficam determinísticas (Lição 9).monotonicIdFactory em vez de chamar um global. Um teste passa um contador; a produção passa o monotônico. Mesma forma, determinística no teste (Lição 10).Tempo e aleatoriedade são efeitos colaterais, exatamente como o filesystem. O segundo invariante do motor — kernel puro, efeitos colaterais injetados — se aplica a eles também. Proibir os globais na VM de plano e passar um Clock/fábrica de ids em todo o resto é uma ideia usando dois chapéus: o único não-determinismo numa run entra por uma costura que você controla. Controle a costura e você controla o replay. Os subsistemas curator e clarify guardam thresholds em milissegundos precisamente para que o Clock injetado seja a única fonte de tempo.
Junte tudo. IDs content-addressed significam que "a mesma run" é encontrável. O log append-only + checkpoint significam que uma run pode ser relida e retomada. A proibição da VM de plano significa que re-avaliar o plano gera a estrutura idêntica. O Clock/fábrica de ids injetados significam que o não-determinismo residual é capturado e replayável. Remova qualquer um e o replay quebra: um id derivado do clock não-encontraria a run; um ramo aleatório no plano re-rodaria diferente; um Date.now() global no curador faria "obsoleto" depender de quando você fez o replay. A disciplina é holística — que é por que a VM impõe a parte fácil de esquecer automaticamente.
runIdFor(spec) content-addressa a run. Um id de timestamp tornaria "a mesma run" não-encontrável amanhã e mudaria a cada invocação; um hash de conteúdo faz re-runs convergirem ao mesmo diretório e faz um spec ajustado uma run distinta.Date.now(), new Date() e Math.random() num alembic.plan.ts. A razão central é:Clock — nunca um global — então a mesma telemetria + o mesmo clock sempre geram a mesma decisão de ciclo (Lição 9).Clock injetado — totalmente usável, só controlável. Um comando de CLI pode ler o clock; um plano descrevendo uma run não pode.