---
name: "convention-project-status"
type: convention
description: >-
  How a project moves through its statuses and how its health is reported,
  independent of the tracker — the lifecycle and what each status means, Hold as
  a real parked state, native project status updates as the backbone of health
  reporting rather than a parallel invented mechanism, who writes a health
  signal versus who reads it, and the operator's weekly write of its own
  vertical's health. Read before setting or changing a project's status, before
  posting or reading a project health update, and when building any sweep that
  gates on project liveness. convention-linear holds the Linear binding,
  including the write-side traps and the rule that a target date behind a Hold is
  not a commitment; convention-ticket holds the ticket-level discipline this sits
  above. Records the 2026-09-02 split: the daily report writes each started
  project's health, the vertical's operator writes its initiative headline
  weekly, and a missing health signal still means no signal, never on track.
license: CC BY-NC-SA 4.0
---

# Project status and health

The project is the deliverable layer — one level above the ticket, one below the initiative. This convention holds two things about it: how it moves through its statuses, and how its health reaches a reader. Both are arbitrary-but-shared choices where only convergence pays: a second sensible person could pick a different status set or a different place to record health, and nothing would be worse for it as long as everyone picked the same one. That is what makes this a convention rather than a standard — you diverge from it, you do not fail it.

It is written tracker-agnostically. `convention-linear` maps it onto Linear's statuses, native project updates, and the connector's read and write traps; `convention-ticket` holds the ticket-level discipline beneath it. Where this and the tool layer disagree on a Linear specific, the tool layer wins.

## The lifecycle

Six statuses, each with an underlying type. The **type** is what matters operationally — every automated gate in the estate keys off it, not off the display name.

| Status | Type | What it means |
| -- | -- | -- |
| **Backlog** | backlog | A real deliverable, not started. |
| **Planned** | planned | Committed and imminent, not started. |
| **Hold** | planned | Started, then parked deliberately. |
| **In Progress** | started | Active now. The only type that surfaces in the daily brief and in the liveness-gated sweeps. |
| **Completed** | completed | Delivered. |
| **Cancelled** | canceled | Abandoned. |

Transitions:

* **Backlog → In Progress** when the first ticket starts. There is no ceremony beyond that; a project with work underway that still reads Backlog is a stale board signal, not a considered position.
* **In Progress → Completed** on delivery.
* **→ Cancelled** when abandoned.
* **In Progress ↔ Hold** to park and resume started work.

The status is not decoration. It decides what a project-scoped sweep looks at, what the morning brief reports, and what counts toward the active-project scan. Keeping it honest is therefore a duty of whoever is running the work, not a tidiness preference — a project left in the wrong status silently changes what several skills do.

## Hold is a real parked state, not a label

Started work that stops is moved to **Hold**, never back to Backlog and never marked with a hold label.

Backlog is wrong because it asserts the work never began, which loses the fact that a half-built thing is sitting there. A label is wrong for a sharper reason: it leaves the underlying type at `started`, so the project keeps counting as active, keeps its section in the brief, and keeps its whole dormant inventory inside every liveness-gated sweep. Hold sits under the **planned** type, so it drops out of all of those automatically while still reading honestly as "started, then parked".

Two consequences worth stating:

* **A parked project's inventory is out of scope; a parked project's ticket the operator has explicitly pointed at is not.** The sweeps that gate on liveness keep an escape hatch for a directly-addressed ticket. Parking removes a project from routine attention, not from reach.
* **A target date sitting behind a Hold is not a commitment.** Parking is a decision; a deadline is a commitment, and the board must not assert one that has been suspended. The reading rule, and the connector reason it has to be a reading rule rather than a write, are in `convention-linear` (*Reading Linear safely*) — read it there rather than reconstructing it here.

Where a project's status is genuinely stale rather than deliberate — live work under a Hold, or an active build still reading Backlog — that is a hygiene item to surface, not a reason to widen a sweep back out.

**A suppression records a checkable condition, and a later run re-checks it (APP-1041).** Where a sweep suppresses an item because of a project status — a Hold voiding a target date, a park quieting a completeness gap — it writes the condition as an assertion that can be re-tested ("project X was Hold on date D"), not as a settled conclusion. Any later run re-reads the status field before honouring that suppression, and treats a change — above all Hold → started — as a material change to note on the ticket and re-open the suppressed branches for. A suppressed item is by construction the one nobody is watching, so a restart is exactly the event least likely to be noticed otherwise: two Apps bets left Hold between 2026-08-17 and 2026-08-22 with no status update, no decision-log entry and no operator ruling, while their tickets still asserted they were parked. `task-strategy-review` binds this on the strategy side and `skill-coordinate` on the delivery side (APP-1036); the detection rule is stated here once.

**Who surfaces the stale case: `skill-plan`, on its weekly cross-project pass (APP-893).** The **not-started-but-full** case — a project whose `statusType` is `backlog` or `planned` while it holds any `started` (or unstarted) ticket — is flagged there, proposing the correction to **In Progress**. The weekly pass already reads every delivery project's status header and its ticket movement, so the test costs no extra read. It **surfaces and proposes**, never auto-writes project status, because a status write reroutes every liveness-gated sweep and so is a major-level surface, not a silent correction. The mirror direction — started-but-empty — rides the same read on the same pass, so the two are authored and run together. This names a runner for the hygiene item this convention already described but left unassigned.

## Health rides on the native project update

**The health signal is the tracker's own project status update — the health flag plus progress note attached to the project.** It is not a comment convention, not a document, and not a field we invent. This reverses the earlier "no per-project updates" position, which is superseded and should not be re-derived.

The reason is roll-up, not tidiness. The native update is the atomic unit the tracker already aggregates: each project's latest update colours that project in the parent initiative's active-projects view, automatically and for free. A parallel mechanism would carry the same information and roll up to nothing, so the scannable "where should my attention go" view would stay dark however diligently it was maintained.

What rolls up and what does not, settled empirically:

* **Project health → the initiative's active-projects view: automatic.** The parent aggregates each project's latest project update. Nobody writes this; it is a consequence of the project updates existing.
* **Initiative health: authored independently.** The initiative's own health flag reads its latest *initiative* update. It is not derived from the projects beneath it. An initiative full of healthy projects still reads as no signal until somebody writes an initiative update.

So the two levels are different jobs, and conflating them is the error this section exists to prevent: posting project updates does not set initiative health, and setting initiative health says nothing about the projects.

**A missing update is no signal — never a pass.** Absence of health reads green to a careless eye and means nothing at all. Anything that consumes health treats "not set" as an unknown to surface, not as on track. (Worked case: the a1.MeirionPritchard work-section project ran In Progress from 30 May with no update ever posted and roughly three weeks past its target, and its health read "not set" throughout — no signal, on a project that plainly had one to give.)

## Who writes, who reads

| Signal | Owner | Nature |
| -- | -- | -- |
| Project health update, every started delivery project | **task-daily-report**, each morning | Mechanical — the daily signal, read off ticket and milestone movement, with an explicit health flag |
| A vertical's initiative health — the weekly headline | **the vertical's operator**, on its weekly operating loop | Authored each run in the standing operator format (`convention-comms-owner`), worst-of across its mandates |
| Initiative health, on an outcome-initiative that is not a vertical's | **Atlas** | A judgement — is this workstream moving the outcome, and should it be paused or killed |
| Surfacing that the mechanism is not being operated | **Pulse** | Ops observation, propose-only |

Two splits inside that table carry the weight.

**Project health is mechanical; initiative health is a judgement.** A project update is derivable from movement — tickets closed, milestones hit or missed, target dates approaching. That is why it belongs to the daily report and is written on a daily cadence — projects carry the daily signal (Owner decision, Aled 2026-09-02 — APP-998). Whether a workstream deserves to continue is not derivable from movement and is never inferred from a run of green projects; that is Atlas's call.

**A capability milestone's health is read from its demo, not from its date.** "Milestones hit or missed" above is the right read for a dated milestone and the wrong one for a capability. Where a project carries capability-led milestones (`convention-ticket` holds the kinds and their naming), a capability milestone is done when its demo runs — so its health is read from whether that demo runs, and from nothing else. A **missed date on a capability milestone is not by itself a health signal**: the date was a forecast of when the demo would be watchable, and a forecast moving is a thing the update reports, not a thing it flags. What does move a capability's health is the demo — not yet runnable when it was expected to be, or blocked on a gate it cannot close without.

The other two kinds keep the dated behaviour stated above and in the checklist below:

* **`Enabling · <name>`** — reported done / not done against its target date. A passed date is a real signal, because there is nothing to demo and the date is all the milestone carries.
* **`Gate · <name>`** — health reads off **the approval or release event itself**: met, or not yet met. A gate's date is a commitment rather than a forecast, so a missed gate reads straight through to project health and is the moment to re-plan or re-resource.

Two consequences for anything writing a project update on such a project. **No percentage, anywhere** — an estimate-weighted progress bar is not a capability signal; a milestone full of Done but unestimated tickets reads 0%, so the update carries each capability's status word and each gate as done / not yet. And **an undated capability is not the gap** that the no-target-date rule below names: that rule bites on dated kinds, where a blank date hides timing health that could have been assessed. A capability left undated while it is shaping is reported by its status word, and calling that a date gap manufactures a gap the format does not have.

**Scope.** Per the Owner's ruling of 2026-09-22 (APP-1658), **capability-led milestones are how every new project is set up; existing projects are unchanged** and move over only case by case, on Aled's call per project. So this section applies to a project that actually carries capability kinds. On a project still on phase or dated milestones, nothing here applies — the dated read above governs it unchanged — and nothing here is a licence to re-slice one.

**The daily report writes the daily signal; the operator writes the weekly headline.** The daily report posts one short project update per started project with an explicit health — the mechanical layer — and never writes an initiative update. Each vertical's operator reads those and writes its initiative's health once a week in the standing format. The "team-mirror initiative" the daily brief used to write to is retired as a concept: BARA, A1, Apps, Mr Pritchard and bot.Trader are simply their operators' initiatives (Owner decision, Aled 2026-09-02 — APP-998, APP-888, APP-1001). Strategic health on an outcome-initiative no operator owns stays Atlas's. Nothing writes at both altitudes, so the two never collide.

Readers, for completeness: the morning brief reads health to decide what to raise; the liveness-gated sweeps (planning, coordination, reporting) read **status** rather than health, because they need "is this live" and not "is this well"; the operator reads the rolled-up active-projects view to decide where to spend attention. Nothing reads a health flag to make an automated write.

## The operator writes its vertical's health each weekly run (APP-869)

An operator's weekly operating loop (`skill-operating-loop` Step 7) writes its vertical's health at the initiative altitude: each run it posts the status update and sets the health flag on **its vertical's own initiative** (A1, Apps, Mr Pritchard, BARA, bot.Trader), in the operator standing-report format (`convention-comms-owner`), reading the daily project signals beneath it rather than rewriting them. Where a vertical also has sub-mandate initiatives (a1.Practice, os.Claude, Luna) the operator may post there too, but the vertical headline has one home. A portfolio operator's headline goes on its portfolio initiative — Apps — with no separate surface created (Owner decision, Aled 2026-09-02 — APP-1001). This supersedes the earlier "never the team-mirror initiative" rule and the APP-867 collision it guarded against, which was a shared status stream at two altitudes; the altitudes are now split by object.

Two rules the operator report must hold:

* **Target dates and KPI baselines are the Owner's.** The operator proposes and populates them each run; the Owner confirms them in the weekly session. They are not the operator's to set unilaterally.
* **No target date → timing health is unreportable, and is flagged as a gap.** A milestone or project with no target date cannot have its timing health assessed; the report shows the date as a named gap, never leaves it blank. A blank reads green to a careless eye — the same failure as a missing update below.

## A multi-mandate roll-up takes the worst-of (APP-886)

An operator holding N independently-assessed mandates (or bets) must still publish a single health flag at the level above them, because the flag is compulsory — a status write with no explicit `health` silently defaults to `onTrack` (`convention-linear`, write-side traps). It publishes the **worst of them**, never a blend and never a net:

* 🔴 if **any** mandate is red;
* 🟠 if any is amber and none red;
* 🟢 **only** when all are green.

State in the body that it is the **worst-of, and name the driving mandate** — the one setting the colour — never an average that hides a red behind two greens. This fails in the **safe direction**: a compulsory single flag that netted would let a healthy majority mask a failing mandate, which is exactly the silent-green failure the rest of this convention exists to prevent. This is the rule the forward reference from `convention-linear`'s write-side health-default bullet points at (the worst-of roll-up for a project or operator carrying several mandates). State it once here for any rolled-up RAG rather than per operator; `convention-comms-owner`'s RAG key then needs no change.

(The one-off estate audit this raises — re-verify existing initiative health flags, since an authored green and a defaulted green are retrospectively indistinguishable — is a read-side follow-up, not part of this convention; it is spun out as its own note.)

## The cross-project mechanism has a writer: the daily report

Until 2026-09-02 nothing routinely wrote project updates outside the operator loop, so every project beyond an operator's weekly write read "not set". The daily report now writes one per started delivery project every morning (Owner decision, Aled 2026-09-02 — APP-998), which gives the roll-up something to aggregate on every live project. Two honest limits remain:

* **Non-started projects get no daily update.** A Hold or Backlog project has no signal, by design; overdue items inside one are surfaced by the daily report's health scan into `task-owner-standup`'s input, not written as a health flag.
* **`skill-plan`'s weekly pass stays the hygiene surface**, not a second health writer: it flags stale status (the not-started-but-full case above) and proposes corrections, and does not post project updates in parallel with the daily report.

Read absence honestly still: a started project with no update in the last two runs is the daily report failing, and an unknown to surface, never a project that is fine.

## Quick checklist

- [ ] Project status set from the six, and chosen by what is actually true of the work?
- [ ] Started work being parked → **Hold**, not Backlog and not a label?
- [ ] Not-started-but-full (a backlog/planned project holding started tickets) surfaced by `skill-plan`'s weekly pass and proposed to In Progress, not auto-written? (APP-893)
- [ ] Target date behind a Hold treated as suspended, not as a deadline (`convention-linear`)?
- [ ] Health recorded as a native project status update, not a comment or an invented field?
- [ ] Project health written daily by `task-daily-report` with an explicit flag; initiative health written weekly by the vertical's operator, or by Atlas on an outcome-initiative no operator owns?
- [ ] An operator rolling up N mandates publishing the **worst-of**, stated as such and naming the driving mandate — never a blend or a defaulted green? (APP-886)
- [ ] Operator's weekly headline on its vertical's own initiative (A1, Apps, Mr Pritchard, BARA, bot.Trader), never rewriting the daily project signal beneath it?
- [ ] A milestone or project with no target date shown as a named gap, not left blank?
- [ ] "Not set" read as no signal, and surfaced — never as on track?
- [ ] Anything reading cross-project health aware that only started projects carry a daily flag, and a parked project's absence is no signal?
- [ ] Linear specifics — exact status names, the write-side traps — taken from `convention-linear` rather than guessed?
