--- name: build-plan description: "Turn a build spec into a complete, ready Bernstein build. Use /build-plan with an idea, a plan directory, or nothing whenever a build needs a human-owned spec, an autonomously derived plan, witnesses in code, generated DAG artifacts, an isolated workspace, and readiness validation before an unattended paid run." --- # build-plan The plan stage is the only stage that needs a human. It ends with everything `/build-run` needs to execute the whole DAG with no human input: a signed-off spec, a derived plan, acceptance tests and contracts in code, briefs an executor can start from, a workspace, and a readiness verdict. Human time is spent on the spec and on one checkpoint written in spec terms, never on implementation detail. Everything before the first artifact write is read-only and may run from anywhere in the repo; the first write triggers WORKSPACE (section 3), and from then on the session lives in the workspace and every artifact commits on its branch - the primary stays free for other sessions. The driver writes every spec, plan, facts, report, contract document and brief. Research and criticism use subagents. Code is written only by the planning executor in WITNESS, never by the driver. Use absolute paths in commands. Use `fd`, not `find`, and `rg` without `-r`. Author ASCII only with a single `-`, never an em dash. The moment a skill instruction proves wrong, ambiguous, or is deviated from - or the user has to intervene where the skill should have sufficed - append `- workflow: ` to `/ledger.md`. These lines are the retro's input for improving the workflow after the run. Every commit anywhere in this workflow - driver, executor, judge, fix - uses Conventional Commits: `type(scope): description` with type in feat, fix, chore, refactor, docs, test, ci, perf. Branch names follow the same shape: `type/short-description`. No attribution lines or trailers. ## Two layers, one directory A plan is a directory `/-/` with at most four files: spec.md human-owned. Frozen by sign-off, hashed. Never edited by a critic, executor, judge or the driver after sign-off; a change is a new sign-off and re-derives plan.md. plan.md machine-owned, regenerable from spec.md and the repo at a sha. facts.md repo facts as `claim | path:line | needle`, verified by script. report.md the checkpoint, in spec terms; regenerated by /build-close. Ownership is by file, so a diff can enforce it. Nothing else goes in the directory: contracts and acceptance tests live in the code tree, briefs under `.agents/build/plans//`, evidence under ` = .agents/build/runs//`. Do not add a template file; the sections below are the template. Citation tokens, the only forms readiness resolves: `SPEC ` for spec.md, `PLAN ` or `PLAN ` for plan.md, `DESIGN ` for the product spec (the repo's own design document, commonly `DESIGN.md`; a repo may have none). "Spec" alone means spec.md in this skill; say "product spec" for the other. ### spec.md sections, numbered `## . ` 1. Problem and intent: what is wrong or missing, for whom, why now. 2. Outcomes: observable acceptance criteria, each one testable. This is the run's definition of done; every outcome gets a witness test in WITNESS. 3. Approaches: the approach chosen for each outcome, rejected alternatives, why. 4. Scope: must, defer, won't. 5. Constraints: invariants that must hold, the production-ready definition (gate, tests, docs), risk appetite, anything the run must not touch. 6. Delegation: decisions the machine may make alone, decisions reserved for the human. A decision that changes an outcome, a public contract, cost, or a named risk is reserved unless listed here as delegated. ### plan.md sections, numbered `## <n>. <title>` 7. Tier and its evidence (see Tiers). 8. Decisions made under delegation, each tagged `derives from SPEC <n>` with the alternative rejected. Freeze a section number once a brief cites it. 9. Witnesses: the acceptance test per SPEC 2 outcome and the contract files landed in WITNESS, by path. Empty for tiers S and M. 10. Phases: a table whose every row names an owner, a tranche-free DAG, allowlist globs, the symbols it changes, its gate. Use an executor role for code-in-worktree work and `user` for live credentials, infra ops or manual verification; never cut `owner: user` rows into the DAG, hand them back at close. Below the table, a fenced `surface` block, one line per phase: `<phase> :: <allowlist globs> :: <rg regex of symbols it changes>`. 11. Spec amendments: exactly which product-spec sections this build adds or amends, and which phase performs each inside its allowlist. Omit when the repo has no product spec. 12. Escalation register: every finding routed to the human, its answer, and which SPEC section it changed. 13. Optional Release requirements, for `/build-close`. ## Tiers The tier is decided in DISCOVER from measured signals, printed with its evidence in PLAN 7, and overridable by the user at either checkpoint. | tier | trigger | what runs | target wall | |---|---|---|---| | S | one phase, one package, no signature, schema or contract change, no product-spec amendment | driver reads the code itself; spec.md; one brief; plan-lint; no critics; one phase and its detached judge; readiness | 15 min | | M | up to four phases, no public contract or schema change | one research agent; spec.md, plan.md, facts.md; surface check; one round of two parallel critics only when a schema changes; cut; per-brief critics in one batch; readiness | 40 min | | L | anything else | full pipeline below, including WITNESS and probes | 90 min | ## Escalation rule Every finding from discovery, critics, probes and readiness lands in one bin: | bin | test | action | |---|---|---| | brief defect | wrong path, allowlist gap, missing fence, unresolvable citation, stale fact | driver fixes, one ledger line | | delegated decision | SPEC 6 allows it, or the choice changes no SPEC 2 outcome and no public contract | driver decides, records in PLAN 8; shown at checkpoint 2, the user can veto | | spec conflict | a SPEC 2 outcome cannot be witnessed, two SPEC clauses contradict against the repo, or the choice changes what the user would see as delivered, a public contract, cost, or a named risk | stop only the affected phases, keep deriving everything else, batch into PLAN 12, present with a recommended answer each | The test for the third bin is one question: would the answer change what the user considers delivered? Escalations are batched and numbered Q1..Qn; never one at a time. Nothing is escalated after `/build-run` starts. ## 1. INTAKE Discover repo facts before designing. Check CLAUDE.md (or the equivalent project instructions) for the product spec document, the plan-documents directory (default `docs/plans/`), and the whole-tree check command (commonly `just check`, `pnpm check`, `cargo clippy`, `uv run ruff check`). If the check command is not documented, ask. Starting from an idea: do quick recon yourself (no subagents), then interview the user on the spec only, three to eight questions that change outcomes, approach, scope or delegation. Draft spec.md; the user signs off. Pick a short lowercase title slug, one word or two joined by `-`. The interview and recon are read-only. At the moment the plan directory is about to be created - the first write of any kind - ask one intent-level question with no implementation vocabulary: "Build this in an isolated workspace so the main checkout stays free? (yes)". Default yes. On yes, run WORKSPACE now with the chosen slug and continue from inside it; on no (draft planning only), continue without committing. Never ask again for this plan. A linked workspace is required before sign-off, machine readiness or execution; if the user declined one, finish the draft and report that unmet prerequisite instead of claiming READY. Starting from an existing single-file plan document: create the directory, move intent-level content into spec.md, discard the rest (plan.md is regenerated), and ask for sign-off on spec.md. Starting from an existing plan directory: skip INTAKE when spec.md has no commit after the sign-off sha recorded in PLAN 7 or the user says it is current. With no argument, continue the current directory when it is clear; otherwise ask. Sign-off is a commit on the WORKSPACE branch containing only the plan directory with spec.md; record its sha in PLAN 7 as `signed-off: <commit sha>`. The same sha is ALSO recorded in the machine sidecar at CUT as `defaults.signoff`: readiness compares the working spec.md against `git show <sha>:<spec path>` and FAILS when they differ, so a spec edited after sign-off cannot reach an unattended run. spec.md is current when `git log --format=%H -- spec.md` shows no commit after that sha. The primary branch is never committed to by the plan; the plan directory reaches it when /build-close merges the branch. ## 2. DISCOVER Fan out research subagents on `claude-opus-5` (in Claude Code: `model: opus` on the Agent call), reports only, never a file. Tier S: none, the driver reads. Tier M: one. Tier L: two to four, split by area. Each returns: - facts with `path:line` and a literal needle on that line, for facts.md; - a verdict per SPEC 2 outcome and per SPEC 5 constraint: feasible, feasible with a decision (named), or infeasible as written (with why); - uncertainties: questions whose answer changes a phase, each with the files that would answer it; - the tier signals: packages touched, whether any signature, schema or public contract changes, whether the product spec needs an amendment, phase count of the obvious decomposition. Write facts.md with `pinned: <sha>` on the first line and run the facts verifier (READY preflight lists the command); a research fact that fails verification is dropped, not folded. Decide the tier. Route every "infeasible as written" and every reserved decision through the escalation rule. If anything lands in the spec-conflict bin, present checkpoint 1 now: the numbered questions, a recommended answer each, and nothing else. Fold answers into spec.md as a new sign-off. If nothing does, there is no checkpoint 1. ## 3. WORKSPACE Make this phase idempotent. Resolve checkout identity first with `git rev-parse --git-dir`, `git rev-parse --git-common-dir`, and `git rev-parse --show-superproject-working-tree`. Refuse a submodule. When the git dir differs from the common dir and the superproject result is empty, already inside a linked worktree: continue. In the primary checkout, if `<run>/workspace.json` exists, verify its five fields and re-enter its recorded absolute `path` with the harness's native worktree tool (EnterWorktree in Claude Code, path mode). Never create a second worktree. Otherwise capture the primary HEAD sha and branch (the workspace branches from whatever the primary is at when the first write arrives). Ask once for branch type `feat|fix|chore|refactor`, default `feat`. If `.claude/worktrees/` is not gitignored, add that one line to the PRIMARY's working tree UNCOMMITTED (it keeps the worktree path untracked there; the workspace branched before the edit, so the committed copy is made in the workspace below). Create: git worktree add -b <type>/<slug> <primary>/.claude/worktrees/<slug> <primary HEAD sha> Use absolute paths in the real command. Bootstrap: anything the repo tracks arrives via the branch. Copy into the workspace, never committing, only the gitignored local state the templates need. With user-scope skills nothing needs copying; for a project-scope install, copy `.agents/skills/` and `skills-lock.json` when untracked. Run this copy ONCE, at workspace creation, and never again: a primary-to-workspace copy after artifacts were edited in the workspace reverts those edits (a near-miss on 2026-09-03 was stopped only by a failed `cd`). The primary may move under an active build; never resync from it, the workspace branch is the truth. The workspace session must inherit the primary checkout's tool permissions; how depends on the harness. In Claude Code: settings are read from `.claude/settings*.json` at the MAIN checkout's root for every worktree (a worktree's own settings file is ignored), so copy this skill's `templates/claude-settings.local.json` to `<primary>/.claude/settings.local.json`, merging allow lists by hand if the file exists, and confirm the primary gitignores it; config reloads on the worktree switch, no session restart needed. A harness without file-based permissions skips this step. Copy this skill's own `templates/bernstein.yaml` when the repo has none and set `quality_gates.base_ref: <type>/<slug>`. If the repo tracks one, retain its project settings and reconcile the mandatory scorer pipeline, cache, repair, evolution and follow-up switches with the template; set `base_ref` to this workspace branch. Write `.agents/build/plans/ACTIVE` as one line containing `<slug>.yaml`. Write `<run>/workspace.json` with EXACTLY these five fields: `path` (absolute workspace path), `branch` (`<type>/<slug>`), `base` (the primary HEAD sha the worktree branched from), `base_branch` (the primary's branch name, commonly `main`), `primary` (absolute primary checkout path). Capture and disable shared Git hooks before the first workspace commit: python3 <this skill>/scripts/workspace-hooks.py disable --root <workspace> --run <workspace>/<run> The immutable hooks.json records the previous local configuration. A different build already owning the shared setting blocks this change. In the WORKSPACE, repeat the `.gitignore` line when it was missing, then make one seed commit containing ONLY `bernstein.yaml`, that `.gitignore` edit, and `ACTIVE`. Keep copied local state untracked. Enter the workspace with the harness's native worktree tool (EnterWorktree in Claude Code, path mode). A harness without one: tell the user to start a session in the absolute workspace path and stop. The captured empty hooksPath stays active through planning and execution; linked worktrees share hooks. ## 4. WITNESS (tier L only) The spec becomes code before the plan does. Spawn ONE planning executor on `claude-opus-5` (seam and contract work is Claude's per the 2026-08-31 executor evals) in the workspace, on the workspace branch, with a brief the driver writes. It lands, in two commits: 1. `test(<scope>): witness SPEC 2 outcomes` - one failing acceptance test per SPEC 2 outcome, derived from spec.md alone, before any contract exists, so the tests witness the spec and not the contracts. Name each test after its outcome number. A test that cannot be written from SPEC 2 is a spec conflict: the executor reports it, the driver escalates. 2. `feat(<scope>): contracts and stubs for <slug>` - interfaces, types, migrations, schema or API diffs, and stub implementations that return a not-implemented error. The tree compiles and lints; every pre-existing test still passes; every witness test fails for the not-implemented reason and no other. No behavior. The executor's validation is exactly: compile, lint, pre-existing tests green, witness tests red for the right reason. The driver reviews the diff against spec.md, sends defects back to a fresh executor, never edits code, and records the witness paths in PLAN 9. Everything after this is cut against interfaces that exist, so the compiler, not a critic, verifies every seam. The bend of the driver rule is exact: the plan stage lands tests, interfaces and stubs that compile; never behavior. ## 5. DERIVE Write plan.md sections 7 to 13. Every decision cites the SPEC clause it derives from; every phase names the witness tests it turns green; every allowlist comes from `rg` over the real tree, never from memory. Run `scripts/plan-check.py` (the facts verifier and the surface check) before any critic; a surface FAIL is a brief defect, fix it first. Critics are fresh subagents on your own model, read-only, "write no files", launched in ONE message, in parallel: - one narrow critic per uncertainty from DISCOVER, given the question, the files that answer it, and spec.md; it returns an answer with `path:line` evidence and, if the answer contradicts plan.md, a BLOCKER; - spec conformance: SPEC 2 outcomes to PLAN 9 witnesses to PLAN 10 phases traceability, undefined terms, invariants with no check, seams with no storage home, contradictions with the product spec; - cut shape: uncut dependencies, role and title collisions, owner rows, amendment-table consistency. Tier M runs only the two lens critics, and only when a schema changes. Give each critic the plan directory plus the product-spec sections plan.md cites, not the whole product spec. Ask for BLOCKER (a gate would fail, two sections contradict, an outcome has no witness) or NIT (wording, labels, list completeness). Fold once. Stop on severity, not silence: rerun only for a blocker, and then a diff-scoped critic that gets `git diff` of plan.md since the last round plus the sections it touches, with everything outside the diff declared verified. A round returning only nits ends after the fold. Never run a generalist full-document round twice. Commit the plan directory on the workspace branch. ## 6. CUT `<slug>` is the run directory name, machine plan `name:`, and machine plan file name. Use the title-slug rule for every `<T>`: lowercase, replace each non-alphanumeric run with `-`, trim, and cut to 48 characters. Produce exactly: .agents/build/plans/<slug>.yaml .agents/build/plans/<slug>.steps.yaml .agents/build/plans/<slug>/<step>.md .agents/build/runs/<slug>/contracts/<seam>.md # only for a seam WITNESS did not land in code; # move any executor-required contract to a TRACKED path Keep only generated machine artifacts plus `ACTIVE` under `.agents/build/plans/`. Keep prose in the plan directory. Briefs are tracked, not run files; only tracked files enter executor worktrees. Point each plan `description:` at its tracked brief. Every sidecar brief, report and document path resolves from the workspace root. No run-directory-relative fallback exists. A brief edit is a commit. Copy templates from this skill's own `templates/` directory (under the base directory the harness reports for this skill; for older installs, `.agents/skills/build-plan/templates` in the project or `~/.agents/skills/build-plan/templates`). Write the machine plan from `build.yaml`, the sidecar from `build.steps.yaml`, executor briefs from `brief.md`, judge briefs from `judge-brief.md` plus `judge-prompt.md`, and fixes from `fix-brief.md`. Replace every placeholder and remove unused sibling stanzas. Every phase needs a complete conditional fix specification and a tracked judge brief, including tier S. No judge belongs to the machine task inventory. Pin discovery in the sidecar: defaults.doc: <plan dir>/plan.md defaults.spec: <plan dir>/spec.md defaults.design: <product spec path> # omit when none defaults.signoff: <sign-off commit sha from PLAN 7> defaults.gate_cmd: <whole-tree check command> Decompose by dependency graph, not file list. Target 15-30 minutes of executor wall per step. With witnesses landed, shared files already exist, so siblings split naturally by package; write a driver-owned interface contract only for a seam WITNESS did not cover. Split more than about eight files or two independent packages into disjoint `phase-Na`, `phase-Nb` siblings. Give shared files to one sibling or a small predecessor. Readiness compares every pair of executor allowlists within a phase using a conservative overlap check; it still cannot prove true glob disjointness, so guarantee sibling disjointness yourself. Use one step per stage and make stage name equal step name. Carry the DAG in `depends_on`. Bernstein batches concurrently open tasks with the same role into one session. Give parallel siblings different roles that resolve to different dispatch policies. Never write per-step `cli:`; Bernstein loses it on retry. Use only the persona-free roles: `resolver` and `ci-fixer` for Codex, `analyst` for Claude. Judges have no engine role; the sidecar pins their ACP transport: `claude` (default, the pinned adapter with budget, model and turn limits bound through session metadata and measured USD cost) or `acp` (any other ACP agent, model passed to acpx; a subscription agent reports no cost, so its ceremony is recorded unmetered). Assign seam and investigation work to `analyst`; assign transfer, exact-line, and fix work to `resolver` or `ci-fixer`. Set every step's `scope:` because it controls the adapter watchdog: `small` 15m, `medium` 30m, `large` 60m, and `large` plus `complexity: high` 120m. Default is medium. Use large above about 20 minutes and for every multi-file judge. Gates while the tree is red by design (tier L, witness tests failing until their phase lands): a phase gate is compile plus whole-tree lint plus the phase's own witness tests plus every test that was green at the base. Never let a phase's gate include a witness test another phase owns. Add a final `regress` phase after all prior phases have been accepted, with `defaults.gate_cmd` as its gate; it is the only step whose gate requires every witness green. Tiers S and M use `defaults.gate_cmd` on every step. Define `phases` and `bounds` in the sidecar using build.steps.yaml. Each phase names its executor titles, one complete conditional fix title, and a tracked judge brief with a pinned ACP adapter, model, turn, time and spend limits, which every transport requires. All tasks belong exactly once to an executor phase or conditional fix. No judge or no-op fix task exists. The final phase declares `final_regression: true` and runs the whole-tree command; its repair uses that same command. A dependency meaning "verified and merged before this starts" MUST cross phases. Native DONE releases a dependent before gate/merge; no in-phase edge is a verified barrier. Within a phase use distinct roles, disjoint files and only dependencies that tolerate that native behavior. Future phases are not posted until the prior run is quiescent, delivery is proven, and its detached judge accepts. Judge scope is cumulative from one frozen build base to the current tip. Earlier changes cannot be excluded using the synthetic staging commit's ancestry. Pin fix scope wide enough for the intended cumulative repairs, or explicitly accept that an earlier-phase defect outside it parks for a new plan. Never widen a fix at runtime. Fix input arrives as immutable review bytes in its task description. Every step must declare working completion_signals; file_contains uses exactly `{type: file_contains, value: "relative/path :: literal needle"}`. One of them names the step's own report and a literal that report must contain, and that literal must be one the brief asks for: the scorer archives the report and the judge reads it, so a step signalled only on its code passes with the report missing and the absence surfaces later as a finding no pinned fix can repair. The driver POSTs and verifies them directly because the native bootstrap drops this field. Derive whole-build wall, spend and attempt bounds from measured medians and a finite repair allowance. Native runs and judge ceremonies each count as attempts. For every executor brief: - Cite spec.md as `SPEC <n>`, plan.md as `PLAN <n>` or `PLAN <n.m>`, the product spec as `DESIGN <n>` and only when one exists. - Name the witness tests the step must turn green and the contract files by path; a brief that restates a signature in prose is a defect. - Include `## Items`, `## Validation` with a fenced command block, and `## Report` naming Deviations. - Give each item a done criterion that Validation can decide. - List exact allowlisted paths and exact validation commands. - Keep it under 16k. - Never tell an executor to run a setup recipe such as `just setup`, `make bootstrap`, or `lefthook install`; linked worktrees share hooks. - Choose a writable, committed report path and set sidecar `brief:` and `report:` explicitly. If `.agents/` is refused, use a tracked allowed path. Set seed `quality_gates.base_ref` to the `<type>/<slug>` workspace branch. The driver freezes a separate write-once base ref at admission. The scorer measures the command even where its Go and TypeScript heuristics do not apply. Record hashes in `<run>/ledger.md` and commit `.agents/build/plans` on the workspace branch. Re-cut only changed artifacts when re-verifying an old plan. ## 7. READY Use the Python interpreter from the installed Bernstein uv tool environment (`uv tool dir` then `bernstein/bin/python`). It must import bernstein_operator and the pinned patched source engine. Follow the repository installation guide when absent; do not silently substitute PyPI or weaken required gates. Claude ACP/acpx must be installed and authenticated before paid execution. Preflight, before the first readiness pass (each item here otherwise costs a full readiness rerun): - Confirm `core.hooksPath` points at the empty repo-local directory. - Run `scripts/plan-lint.sh <briefs dir>` from this skill and fix every FAIL: the 16,000-char brief cap, the fenced `## Validation` block in executor and fix briefs, and the hooks state. - Run `scripts/plan-check.py <plan dir>`: the facts verifier (every facts.md row still holds at its `path:line`) and the surface check (every symbol a phase changes is matched only inside that phase's allowlist). - Environment for an unattended run: at least 40 GB free on the volume that holds the workspace; `go mod download <module>` (or the repo's equivalent) for every dependency a brief names that is not yet in the module cache (find them with `rg -o` over the briefs); the whole-tree check run once in the workspace so the build cache is warm for the base sha; network policy known. For a Go repo, `export GOFLAGS := "-trimpath"` in the Justfile makes the build cache shared across worktree paths (measured 2026-09-03: a cold `just check` in a fresh worktree 63s without, 22s with); without it every executor worktree and every readiness replay is a full rebuild. A run that dies on disk or a missing module is a plan defect. Then from the workspace root run: <Bernstein python> <this skill>/scripts/plan-check.py <plan dir> --repo <workspace> --machine <machine.yaml> Read the structured readiness receipt and every facts/surface result. Notes are advisory: a missing allowlisted path may be a new file and a missing report requirement weakens evidence. Treat unexpected RED as a brief error unless the brief names that red window. Read every printed gate command and require the discovered whole-tree command (there is no fallback). Keep step-specific gates scoped. Preserve the dispatch guards: every codex role's declared effort must equal the machine's `model_reasoning_effort` (absent means `default`), because the adapter passes only `-m`; every role needs a `role_model_policy`, fast-path titles must be reworded, parallel tasks must not share a role, phase judge fields must be complete with an `acp` transport's adapter resolvable on PATH, and every final regression fix uses the whole-tree gate. Probes (tier L): one fresh executor subagent on `claude-opus-5` per executor brief, in parallel batches, each in a throwaway detached worktree of the workspace branch outside the repo. The probe has ten minutes to locate every allowlisted file, resolve every SPEC, PLAN and DESIGN citation, read the witness tests it owns, run the step's gate as-is, and report every ambiguity it hit; it writes nothing and stops. A probe that cannot start is a brief defect or a spec conflict; route it. Remove probe worktrees afterwards. Per-brief critics (tier M, and tier L only for briefs a probe flagged): one fresh critic per brief on your own model, read-only, launched in parallel batches. Give each the brief, the plan directory, the optional product spec and named files. Ask for BLOCKER or NIT only: underspecification, contradictions, validation gaps, allowlist gaps, overlapping claims. Edit briefs yourself, rerun ready so it re-pins, and rerun critics only on the briefs you edited. A round returning only nits ends after the fold. Readiness also writes `<run>/readiness/receipt.json` (engine source provenance, installed code hashes, CLI versions, Codex config hash and role_model_policy) so the retro can tell what the run actually executed with. Record the readiness rounds and final pins in `<run>/ledger.md`. ## 8. REPORT Write report.md in the plan directory, in spec terms only, no code: ## Traceability SPEC 2 outcome -> witness tests -> phases ## Decisions PLAN 8, each stated as behavior the user will observe ## Escalations PLAN 12, answered and open ## Restatement each SPEC 2 outcome in the machine's own words ## Run tier, phase count, DAG critical path, executor roles, owner:user rows handed back at close An outcome with no witness goes back to DERIVE, never to the user. Restatement exists to expose a consistent misread, where witnesses and contracts are both wrong in the same way and every check passes. Commit the plan directory, then rerun this skill's plan-check.py with --machine so the final readiness receipt names this exact committed tip (report generation advances HEAD). Present report.md as checkpoint 2. End STOPPED. Print exactly: use /build-run <plan dir> to start plan execution Never auto-chain. Starting the paid run is the user's explicit decision, and after it starts nothing asks the user anything until the DAG ends.