tasks: phases — 48 the model, 49 the runner, 50 the UI, 51 the action
A phase is a card with Type: Phase and a ## Cards section listing its members in run order; members carry Depends on, which guards rather than orders. It runs on its own branch, each member branched from the phase tip so card two sees card one's work, merged back on green, one PR into main at the end — the human gate moves from every card to the phase boundary, and 'the board never merges' stays true of main.
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
# 48 — A phase is a card that lists its cards
|
||||
|
||||
**Status:** Backlog
|
||||
**Priority:** High — everything else about phases reads this; get the
|
||||
shape wrong and three cards inherit it
|
||||
**Type:** Feature
|
||||
|
||||
A phase is a group of related tasks meant to run one after another. Rather
|
||||
than a new directory, a new stage or a registry, a phase is **a task card
|
||||
like any other** — `**Type:** Phase`, with a `## Cards` section naming its
|
||||
members in the order they run. This card is the model and its rendering:
|
||||
after it, the board knows what a phase is and shows it. Nothing runs yet.
|
||||
|
||||
## Context
|
||||
|
||||
- `tasks/` holds only state and has to keep working as a plain folder
|
||||
kanban even if `manager/` is deleted. A phase that lived anywhere else
|
||||
would be a second source of truth; as a card it is diffable,
|
||||
greppable, reviewable and syncs like everything else.
|
||||
- **`Type` already exists and is already read.** `TYPE_RE`
|
||||
(`manager/core/taskfiles.py:27`) matches any value and `read_task`
|
||||
surfaces it (`:70`), so `**Type:** Phase` needs no parser change to be
|
||||
stored — only somewhere to mean something.
|
||||
- **`Depends on` is documented but parsed nowhere.** `AGENTS.md`
|
||||
describes it as informational — "the board does not enforce it; it
|
||||
informs whoever picks the next card" — and a grep of `manager/core/`
|
||||
finds no reader. This card gives it one.
|
||||
- Ordering *within* a lane is task `../backlog/25-order-within-a-lane.md`
|
||||
and is not needed here: a phase's order comes from its own list, not
|
||||
from where its cards happen to sit.
|
||||
|
||||
**Affected areas:** `manager/core/taskfiles.py` (reading the section and
|
||||
the dependency line), and `manager/core/board.html` (the chip on a member
|
||||
card).
|
||||
|
||||
## What to build
|
||||
|
||||
- **`**Type:** Phase`** marks a card as a phase. The name of the phase is
|
||||
the card itself — its number and title — so nothing new has to be
|
||||
named, spelled twice or kept in step.
|
||||
- **A `## Cards` section** listing members in run order, one per line,
|
||||
each beginning with a task number:
|
||||
|
||||
```markdown
|
||||
## Cards
|
||||
|
||||
- 31 — Stand up site/ and its build
|
||||
- 32 — Serve it from a Cloudflare Worker
|
||||
- 33 — The landing page
|
||||
```
|
||||
|
||||
Document order is run order. The number is what is parsed; the title
|
||||
after it is for the reader and is never matched against anything.
|
||||
- **One direction only.** The phase card lists its members; member cards
|
||||
say nothing about phases. Membership therefore cannot disagree with
|
||||
itself, and there is exactly one place to edit when it changes.
|
||||
- **`Depends on` is parsed at last, and it guards rather than orders.**
|
||||
The list says what runs next; a member's dependencies say whether it
|
||||
*may* — a card whose dependency is not finished is not startable even
|
||||
if the list reached it. Expose it on the task; do not act on it here.
|
||||
- **Membership is derived for the member card.** The board reads every
|
||||
task file already, so a member's phase and its position are computed
|
||||
from the phase cards rather than stored twice.
|
||||
- **A `⟶ <phase> 3/5` chip** in the member card's footer row, in the dim
|
||||
register beside `CI` and `PR ↗` — a destination, not a status — opening
|
||||
the phase card.
|
||||
- **Drift is flagged, not swallowed**, in the same spirit as `status
|
||||
drift`: a listed number with no card, the same card listed by two
|
||||
phases, a card listed twice by one phase. Each is an authoring mistake
|
||||
that would otherwise surface much later as a runner behaving oddly.
|
||||
|
||||
**Out of scope** — tempting neighbours left alone:
|
||||
|
||||
- Running anything. No branch, no launches, no advancing — card 49.
|
||||
- The header chip and the run/hold actions — card 50.
|
||||
- The board action that adds a card to a phase — card 51.
|
||||
- Nesting. A phase card is not a member of another phase.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- [ ] Given a card with `**Type:** Phase` and a `## Cards` section, when
|
||||
the board reads it, then its members and their order are available
|
||||
in the order the section lists them.
|
||||
- [ ] A member card shows `⟶ <phase> <n>/<total>` in its footer row, and
|
||||
clicking it opens the phase card.
|
||||
- [ ] A card in no phase shows no chip and is unchanged in every way.
|
||||
- [ ] Given a `## Cards` line naming a number no card has, the board
|
||||
flags it rather than silently skipping it.
|
||||
- [ ] Given the same card listed by two phase cards, both are flagged.
|
||||
- [ ] `Depends on` is parsed into a list of numbers and exposed on the
|
||||
task; nothing yet acts on it.
|
||||
- [ ] Edge case: a `## Cards` section that is empty, or a phase card with
|
||||
no such section, reads as a phase with no members rather than
|
||||
failing.
|
||||
- [ ] Edge case: a number in the list written as `#31` or `31 —` or bare
|
||||
`31` all resolve to the same card.
|
||||
|
||||
## Notes
|
||||
|
||||
The phase card earns its keep beyond membership: it is where the phase's
|
||||
own brief lives (what this group of work is for), it is what the board
|
||||
will later hang a branch and a PR on, and it is what makes the whole
|
||||
feature reviewable — a phase arrives in the repo as a file you can read
|
||||
in a diff before anything runs.
|
||||
|
||||
Most phase cards will be written whole, with their members already
|
||||
listed and each member's `Depends on` already filled in, rather than
|
||||
assembled on the board. The authoring path (51) is the convenience, not
|
||||
the primary one — which is a good reason for the file format to be
|
||||
pleasant to write by hand.
|
||||
@@ -0,0 +1,146 @@
|
||||
# 49 — A phase runs itself, on a branch of its own
|
||||
|
||||
**Status:** Backlog
|
||||
**Priority:** High — the point of the whole thing: work that continues
|
||||
without you until something is genuinely wrong
|
||||
**Type:** Feature
|
||||
**Depends on:** 48 — the board has to know what a phase is first
|
||||
|
||||
Give a phase its own integration branch and work the list into it: each
|
||||
card branched from the phase's tip, run headless, merged back when its
|
||||
checks are green, and the next one started. One PR at the end, into
|
||||
`main`, for a human. The runner is a beat on the board, not an agent —
|
||||
everything it decides is already structured state, and an agent paid to
|
||||
poll would be the wrong tool at the wrong price.
|
||||
|
||||
## Context
|
||||
|
||||
The problem this exists to solve, precisely:
|
||||
|
||||
- `_fresh_branch_point()` (`manager/core/agents.py:167`) branches every
|
||||
new task worktree from `origin/main`. For *related* tasks — the whole
|
||||
premise of a phase — card two branched from main cannot see card one's
|
||||
work while card one sits unmerged in `review/`. It will conflict, or
|
||||
quietly build the same thing twice.
|
||||
- Gating on a merge into `main` would fix the branch point and destroy
|
||||
the point: `main` is merged by a person, so the phase would stall on
|
||||
every card.
|
||||
|
||||
So the phase gets **its own branch**, and the human gate moves from every
|
||||
card to the phase boundary. The promise bench makes — *nothing merges
|
||||
without you* — is about `main`, and it survives intact: the board merges
|
||||
into a branch it created, inside a scope you opened, and `main` still
|
||||
waits for your click.
|
||||
|
||||
What already exists and should be used rather than rebuilt:
|
||||
|
||||
- The board reacts to state without an agent: a card entering `review/`
|
||||
opens a PR "mechanically, by the board", and the PR poller is a plain
|
||||
thread checking reviews, CI and mergeable state every 60s.
|
||||
- `github.public_state()` (`:378`) exposes `{verdict, ci, copilot,
|
||||
conflicts, url}` per card — the advance condition is readable, not
|
||||
judged.
|
||||
- `watch.py` answers *am I the actor?*, and `AGENTS.md` says every future
|
||||
automation hung off a stage transition inherits it.
|
||||
- Every failure mode a card can have is already a state it wears:
|
||||
`NOT READY`, `run failed`, a clean exit that committed nothing.
|
||||
- `complete_task()` (`manager/core/github.py:387`) is the model for a
|
||||
careful multi-step git operation that narrates and aborts cleanly.
|
||||
|
||||
**Affected areas:** a new `manager/core/phases.py` — right of `agents` in
|
||||
the module map, since it needs `taskfiles`, `github` and `agents` — plus
|
||||
the branch-point change in `agents.py` and a beat wired in `board.py`.
|
||||
|
||||
## What to build
|
||||
|
||||
- **A phase branch.** Starting a phase cuts `phase/<task-stem>` from the
|
||||
newest `origin/main` it can see, by the same rule and the same timeout
|
||||
a task branch uses.
|
||||
- **Members branch from the phase tip**, not from main. This is the one
|
||||
change inside `agents.py`: where a launch is part of a phase, the
|
||||
branch point is the phase branch rather than `origin/main`, and the
|
||||
ticker names it as it already names an unusual branch point.
|
||||
- **A stateless beat.** On each pass, recompute: which members are
|
||||
finished, which is first unfinished, what does it need. Hold no
|
||||
registry — a board restart then resumes a phase by looking, and the
|
||||
same logic answers "what now?" whether the last event was a launch, a
|
||||
merge or a crash.
|
||||
- **Advance on green.** A member is finished when its card is in
|
||||
`review/` and its CI has passed. Then merge its branch into the phase
|
||||
branch — additively, never rebasing, never force-pushing — and start
|
||||
the next member whose `Depends on` are all finished.
|
||||
- **Halt, never skip.** The five conditions, each already a visible state
|
||||
on the card: `NOT READY`; a non-zero exit; a clean exit with no
|
||||
commits; CI red; a merge into the phase branch that is not mechanical.
|
||||
A phase that steps over a failed card builds the rest on a foundation
|
||||
that never landed.
|
||||
- **Keep the phase branch fresh.** Merge `main` into it on the beat,
|
||||
additively, so a phase that runs for hours does not drift into one
|
||||
enormous conflict at the end. A conflict there halts the phase like any
|
||||
other.
|
||||
- **Finish into a PR.** When every member is finished, push the phase
|
||||
branch, open one PR into `main` with the member list as its body, write
|
||||
the `**PR:**` line into the phase card, and move the phase card to
|
||||
`review/`. From there the existing apparatus applies unchanged — CI
|
||||
chip, `◔ review PR`, `⚑ copilot`, and drag-to-`done/` for **merge &
|
||||
clean up**.
|
||||
- **One board runs it.** The actor rule decides; a replica renders the
|
||||
phase and advances nothing.
|
||||
|
||||
**Out of scope** — tempting neighbours left alone:
|
||||
|
||||
- Running members in parallel where dependencies allow. Sequential
|
||||
first; parallelism is a second card once the sequencing is trusted.
|
||||
- Any UI. The header chip, the run and hold actions and the narration
|
||||
are card 50 — this card is reachable through the API and the ticker.
|
||||
- Auto-merging anything into `main`, under any condition.
|
||||
- Nested phases, and a member belonging to two phases at once (48 flags
|
||||
that as drift; here it simply must not run twice).
|
||||
|
||||
## Acceptance
|
||||
|
||||
- [ ] Given a phase whose members are unstarted, when it is run, then
|
||||
`phase/<stem>` exists at the newest `origin/main`, and the first
|
||||
member's worktree is branched from it.
|
||||
- [ ] Given member one is in `review/` with CI green, when the beat runs,
|
||||
then its branch is merged into the phase branch and member two is
|
||||
launched from the new tip — and member two's worktree contains
|
||||
member one's work.
|
||||
- [ ] Given a member whose `Depends on` names an unfinished card, it is
|
||||
not launched even when the list reaches it.
|
||||
- [ ] Given a member that exits `NOT READY`, the phase halts, the card
|
||||
walks back as it does today, and no further member starts.
|
||||
- [ ] Given a member whose run fails, or exits clean with no commits, or
|
||||
whose CI is red, the phase halts in each case.
|
||||
- [ ] When every member is finished, one PR is opened from the phase
|
||||
branch into `main`, the phase card carries its `**PR:**` line, and
|
||||
the card is in `review/`.
|
||||
- [ ] Restarting the board mid-phase resumes it without repeating a
|
||||
launch or a merge.
|
||||
- [ ] With `BOARD_SYNC` on, a second board shows the phase advancing and
|
||||
launches nothing itself.
|
||||
- [ ] Edge case: a phase whose list is empty goes straight to `review/`
|
||||
with nothing to merge, or refuses to start — either, but not a
|
||||
branch left behind and a card stuck in `in-progress/`.
|
||||
- [ ] Edge case: a member card moved by hand while the phase is running
|
||||
does not cause a second launch on the same task.
|
||||
|
||||
## Notes
|
||||
|
||||
**`AGENTS.md` will need a sentence it does not have.** "Merging remains
|
||||
yours — the board never merges" is quoted as an absolute. After this it
|
||||
needs to say what it always meant: the board never merges into `main`. A
|
||||
phase branch is the board's own, and merging into it is bookkeeping in
|
||||
the same family as committing a move.
|
||||
|
||||
A lever worth considering once this works: run `◔ review PR` on each
|
||||
member and require `APPROVE` as well as CI green, with one automatic
|
||||
`↻ act on PR` round before halting. That would put an agent's review
|
||||
inside the phase and keep the human's for the boundary — more autonomy,
|
||||
more tokens, and a bigger change than this card should carry.
|
||||
|
||||
**Risks** — this is the third place in the codebase that merges git
|
||||
branches, after `complete_task` and `↻ act on PR`'s conflict resolution.
|
||||
It must behave like both: abort cleanly, leave no half-merged branch, and
|
||||
narrate every step. A phase branch that ends up in a broken state is
|
||||
worse than a phase that refuses to start.
|
||||
@@ -0,0 +1,105 @@
|
||||
# 50 — The board shows a phase running, and shows it stopping
|
||||
|
||||
**Status:** Backlog
|
||||
**Priority:** High — a phase that runs unattended is only trustworthy if
|
||||
its halt is impossible to miss
|
||||
**Type:** Feature
|
||||
**Depends on:** 49 — there must be something to watch
|
||||
|
||||
Give the phase the three pieces of interface it does not already inherit:
|
||||
an action to start and stop it, a header chip while it runs, and the same
|
||||
chip in `--alarm` when it halts. Everything else — the PR chip, CI, the
|
||||
review actions, the merge-and-clean-up sheet — the phase card already
|
||||
gets by being a card.
|
||||
|
||||
## Context
|
||||
|
||||
- The phase card moves through the stages like any other card, and each
|
||||
stage already means the right thing: `to-do/` queued, `in-progress/`
|
||||
running, `review/` all members landed and a PR open, `done/` merged.
|
||||
- Card actions live in the status pill's slot on hover, at most two per
|
||||
state, and anything costing tokens or stopping work arms on the first
|
||||
click and fires on the second (`board.html:1191` onwards).
|
||||
`▸ start work` sits in that slot on an in-progress card, and `‖ hold`
|
||||
is already the word for stopping while an agent runs.
|
||||
- The header carries what is happening *across* the board: the live
|
||||
agents chip, and the sync chip which appears only when sync stops
|
||||
converging and then holds until a human settles it. A running phase is
|
||||
the same kind of fact.
|
||||
- The design system's colour law: `--accent` an agent alive, `--alarm`
|
||||
blocked or failed, and one looping animation ("breathe") meaning work
|
||||
is happening.
|
||||
- A failed run is deliberately told three times at three altitudes — a
|
||||
state the card wears, a toast, and a line in the ticker — because it is
|
||||
the outcome a person must not miss.
|
||||
|
||||
**Affected areas:** `manager/core/board.html` only, plus whatever
|
||||
`/api/state` must carry to describe a running phase.
|
||||
|
||||
## What to build
|
||||
|
||||
- **`▸ run phase`** on an `in-progress/` phase card, in the slot
|
||||
`▸ start work` occupies on an ordinary one, arming and firing like
|
||||
every other launch. Moving the card to `in-progress/` stays the
|
||||
commitment; this is the second half of it.
|
||||
- **`‖ hold`** while it runs, meaning what it means everywhere else:
|
||||
stop, without unwinding what has already landed on the phase branch.
|
||||
- **A header chip while a phase runs**, beside the agents chip, breathing
|
||||
in `--accent`: the phase, its progress and the card it is on —
|
||||
`⟶ auth-rework · 3/5 · on #33`. Present only while a phase is running,
|
||||
the way the sync chip is present only when there is something to say.
|
||||
- **The halted state, in `--alarm`, holding.** `⟶ auth-rework · halted at
|
||||
#35 — not ready`, staying until the phase is resumed or stopped rather
|
||||
than scrolling away. With a toast, because a halt is rare and
|
||||
actionable, and a ticker line that survives in the log. The member card
|
||||
wears its own failure independently — that duplication is the point.
|
||||
- **Narration of each advance** in the ticker: the member that finished,
|
||||
the merge into the phase branch, the member starting next. A phase that
|
||||
advances silently is a phase nobody can debug afterwards.
|
||||
- **The phase card's sheet lists its members in order** with each one's
|
||||
current stage, so the card answers "where is this up to" without
|
||||
hunting across five columns.
|
||||
|
||||
**Out of scope** — tempting neighbours left alone:
|
||||
|
||||
- Filtering the board to a phase's cards, or dimming everything else.
|
||||
Useful, bigger, and better judged once the chip exists.
|
||||
- A Focus view for a phase. Focus is a heads-up display for one session;
|
||||
one for a phase is a real idea and a separate build.
|
||||
- Anything that changes what the runner does. This card watches.
|
||||
- The `⟶` member chip, which is card 48's.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- [ ] Given a phase card in `in-progress/`, hovering it offers
|
||||
**▸ run phase**, which arms on the first click and starts on the
|
||||
second.
|
||||
- [ ] While a phase runs, the header carries a breathing chip naming the
|
||||
phase, its progress and the member in flight.
|
||||
- [ ] Given a member that fails, declines, commits nothing or goes red,
|
||||
the chip turns `--alarm` and names the reason and the card, a toast
|
||||
fires, and the chip holds until the phase is resumed or stopped.
|
||||
- [ ] **‖ hold** stops the phase and leaves the phase branch and every
|
||||
landed member exactly as they were.
|
||||
- [ ] With no phase running, the header is exactly as it is today — no
|
||||
empty chip, no placeholder.
|
||||
- [ ] The ticker names every advance: what finished, what merged, what
|
||||
started.
|
||||
- [ ] Opening a phase card lists its members in run order with each
|
||||
one's stage.
|
||||
- [ ] Edge case: two phases could in principle run on one board — either
|
||||
the chip handles more than one, or starting a second is refused
|
||||
with a reason. Not a chip that silently shows one of them.
|
||||
|
||||
## Notes
|
||||
|
||||
The reason the halted state gets this much attention is that the feature
|
||||
is asking for trust: you start a phase and stop watching. Everything in
|
||||
bench that runs unattended already pays that back the same way — the
|
||||
failed run wears its state, sync says when it stalls — and a phase that
|
||||
halts quietly at 2am would undo the whole argument for having one.
|
||||
|
||||
Worth checking while building: the header is getting crowded. Agents
|
||||
chip, sync chip, phase chip, theme, refresh. If they collide, the phase
|
||||
chip is the one that can fold into the agents chip — both answer "what
|
||||
is happening without me" — rather than shrinking the ones already there.
|
||||
@@ -0,0 +1,101 @@
|
||||
# 51 — Add a card to a phase without opening the file
|
||||
|
||||
**Status:** Backlog
|
||||
**Priority:** Low — the convenience path; phase cards mostly arrive with
|
||||
their lists already written
|
||||
**Type:** Feature
|
||||
**Depends on:** 48 — the list this appends to has to exist and be read
|
||||
|
||||
A `⟶ phase` action on a backlog or to-do card, offering the phase cards
|
||||
currently in `to-do/` and appending the card to the one you pick. It is
|
||||
the small path: most phases are written whole, members and dependencies
|
||||
already in place, before anything reaches the board. This is for the card
|
||||
you decide belongs after all.
|
||||
|
||||
## Context
|
||||
|
||||
- The phase card's `## Cards` section is the single place membership
|
||||
lives (48), so adding a card means appending one line to *that* file —
|
||||
not editing the card being added.
|
||||
- The board already writes into task files where the file must stay the
|
||||
source of truth: the `**PR:**` line goes in through `commit_edit()`
|
||||
(`manager/core/taskfiles.py:242`), which commits under the same gate a
|
||||
move does and, in team mode, reaches the other boards.
|
||||
- A choice between several options has a pattern too: the drag-to-`done/`
|
||||
sheet (`completeSheet`, `board.html:1362`) puts a short list of named
|
||||
outcomes in front of you rather than guessing.
|
||||
- Card actions arm before they fire, and the status pill's slot holds at
|
||||
most two per state. On backlog and to-do cards that slot is close to
|
||||
free — `◔ still true?` is often the only one there.
|
||||
- Task `../backlog/47-an-archive-button-on-the-card.md` puts a chip in
|
||||
the footer row for a similar reason; the two should not fight for the
|
||||
same corner.
|
||||
|
||||
**Affected areas:** `manager/core/board.html` (the action and its sheet),
|
||||
`manager/core/httpd.py` (a route), `manager/core/taskfiles.py` (the
|
||||
append).
|
||||
|
||||
## What to build
|
||||
|
||||
- **A `⟶ phase` action** on cards in `backlog/` and `to-do/` that are not
|
||||
already in a phase, and not themselves phase cards.
|
||||
- **A sheet listing the phase cards in `to-do/`**, each with its number
|
||||
and title and how many cards it already holds. Picking one appends the
|
||||
card to the end of that phase's `## Cards` section and closes the
|
||||
sheet.
|
||||
- **`to-do/` only.** A phase in `in-progress/` is running: its branch
|
||||
exists, its members are being worked in the order the list had when it
|
||||
started, and appending mid-flight is a different feature with different
|
||||
questions. Offer it and someone will find out the hard way.
|
||||
- **The append goes through the board's own write path**, so it commits
|
||||
itself under `BOARD_COMMIT_MOVES` and syncs like every other board-made
|
||||
change to a task file. An addition that never leaves one working tree
|
||||
is not an addition the phase will run.
|
||||
- **Written the way a person writes it** — `- 33 — <title>` — so the
|
||||
section stays something you would have typed. A machine-shaped line in
|
||||
a file people author by hand is how a format stops being pleasant.
|
||||
- **Nothing else moves.** The card stays in its stage; joining a phase is
|
||||
not a commitment to start it, and the phase decides when it runs.
|
||||
- **No phases in `to-do/` → the action is absent**, not present and
|
||||
empty. The board offers what it can do.
|
||||
|
||||
**Out of scope** — tempting neighbours left alone:
|
||||
|
||||
- Removing a card from a phase, and reordering the list. Both are file
|
||||
edits for now; both are better judged once phases have run a few
|
||||
times.
|
||||
- Adding to a running phase.
|
||||
- Creating a phase card from the board. Phase cards are written, and
|
||||
writing them is where their brief comes from.
|
||||
- Multi-select — adding several cards in one gesture — which needs a
|
||||
selection model the board does not have.
|
||||
|
||||
## Acceptance
|
||||
|
||||
- [ ] Given a card in `backlog/` or `to-do/` and at least one phase card
|
||||
in `to-do/`, hovering offers `⟶ phase`; choosing a phase appends
|
||||
the card to that phase's `## Cards` and the member chip from 48
|
||||
appears on it.
|
||||
- [ ] The appended line names the card's number and title, at the end of
|
||||
the section, and the rest of the phase card is untouched.
|
||||
- [ ] With `BOARD_COMMIT_MOVES` on, the append is committed on its own,
|
||||
messaged like the board's other bookkeeping; with it off, nothing
|
||||
is committed and the file is simply edited.
|
||||
- [ ] Phase cards in `in-progress/`, `review/` or `done/` are not
|
||||
offered.
|
||||
- [ ] A card already in a phase does not offer the action.
|
||||
- [ ] With no phase in `to-do/`, the action does not appear.
|
||||
- [ ] Edge case: a phase card with no `## Cards` section yet gets one,
|
||||
rather than the line being appended to the end of the file.
|
||||
- [ ] Edge case: two boards adding to the same phase produce two lines,
|
||||
not a lost one — the second append reads the file as it is on disk
|
||||
rather than as it was rendered.
|
||||
|
||||
## Notes
|
||||
|
||||
The reason this is Low and not Medium: the intended way a phase arrives
|
||||
is fully formed — a card written with its members listed and each
|
||||
member's `Depends on` filled in — because that is a thing you can read in
|
||||
a diff and reason about before any of it runs. This action is for the
|
||||
afterthought, and it should stay small enough not to become the way
|
||||
phases are assembled.
|
||||
Reference in New Issue
Block a user