---
name: "task-daily-report"
type: task
description: Morning brief on delivery work — an agnostic status-and-progress report over active work only, posted as one short Linear project status update per live project with an explicit health flag, so projects carry the daily signal and the operators carry the weekly initiative headline. Scope resolved live from Linear, never a named list. Linear connector only; writes only project status updates; excludes Pipeline. Consumed by the five operator stand-ups and Relay's task-owner-standup. Self-contained task (no separate skill).
license: CC BY-NC-SA 4.0
---

# task-daily-report

Scheduled deployment of the morning brief. This task is **self-contained**: there is no separate `skill-daily-report`, because the behaviour is inherently the scheduled job and has no independent use. (If it ever needs to be invoked outside the schedule, extract a `skill-daily-report` and reduce this file to a loader.)

Each morning you post Aled's morning brief on his delivery work as **one short Linear project status update per live project**, each with an explicit `health`, so the briefs surface in Pulse and so the operators and Relay's consolidated stand-up can read them. This is an **agnostic status-and-progress report over active work** — it reports delivery status; it carries no operator-flavoured content and no strategy (Owner decisions 2026-08-14, APP-876; 2026-09-02, APP-998). Linear connector only. Follow `convention-linear`: calm, precise, sentence case, British spelling, no exclamation marks, no marketing language.

This run **writes** to Linear, but **only** project status updates via `save_status_update` (type: project). Change nothing else: no issue status, labels, comments, assignments, or project membership. It never writes an initiative status update — the initiative layer is the operators' weekly headline (`skill-operating-loop` step 7).

## Where to post — scope resolved live from Linear

Resolve scope from the tracker, never from a list named in prose. One `list_projects` call per delivery team (every team except Pipeline) returns each project's `statusType`; a project earns a status update **only if its `statusType` is `started`**. Post one update per started project, by project ID. There is no named list of initiatives, no team-mirror initiative, and no per-vertical special case: a new project or a new vertical is covered the morning it starts, and a parked one drops out the morning it is put on Hold. Do **not** post to the Pipeline team's projects.

**Parked areas get one line, not progress tracking.** A team with no started project gets no project update; its parked state is reported by the health scan (§ Health) through the operator's own surface, never by this task asserting a colour on it. Hold is a real parked state (`convention-project-status`), and "nothing to report" on a parked area is the correct report.

## Project gate — report only In Progress projects

Within each team, report a project **only if its `statusType` is `started` (In Progress)**. Projects in Backlog, Planned, Hold, Completed, or Cancelled are skipped entirely.

Apply the gate **before** pulling any issues: one `list_projects` call per team returns each project's status, so a non-started project is dropped without ever calling `list_issues` against it — that is the whole saving, since the per-project `list_issues` calls are the main cost. An In Progress project is by definition active, so it earns a section even in a quiet window; its update simply reads "nothing moved this window". There is no started-but-quiet special-casing and no idle roll-up line — a project either surfaces (started) or it does not.

The gate assumes project status is kept honest — it pairs with the project-status lifecycle convention (statuses, transitions, and Relay's ownership of health via the weekly plan pass). A project parked mid-flight belongs in the **Hold** status (planned type), not Backlog and not a label, so it drops out of this gate cleanly while still reading as "started then parked".

**The project gate governs the narrative only — not Health.** It decides which projects earn a section in the body; it does **not** decide the initiative's `health` field. Health runs its own scan, independent of the gate (§ Health), so a team the gate empties is never silently posted as green.

## What this report no longer carries

Two sections that lived here until 2026-09 were operator-flavoured and have moved to their carriers (Owner decision 3, APP-876). The **os.Claude canon-needs-you sweep** is the Pulse drain's consolidated *decisions-needed set* (`skill-ops-retro`, the author manifest); the Owner reads it there and in `task-owner-standup`'s 09:00 agenda, not here. The **operator scoreboard** (APP-575) is Aide's own output in `skill-attention-sweep`. The standing exception to the project gate that carried the sweep is retired with it. Neither was deleted before its carrier existed.

## Window

"Moved" / "closed" = the last 24 hours. Awaiting / your move / blocked / in flight = current state as of now. A Done issue belongs in the Done bucket only if its `completedAt` falls in the window — a recent `updatedAt` from a label change is not a move.

## Body format (Markdown — replicate the established format)

Each project update starts with `# Morning brief — D Month YYYY · <Project name>` (e.g. `# Morning brief — 24 June 2026 · mbn.website`), then one summary sentence of the project's state. Use only the `### Your move` / `### Awaiting you` / `### Done` / `### Blocked` / `### In flight` subheadings that have items. One tight line per item:

`- ID · title · priority, status/timing note — short why where useful.`

Nothing is appended for a team or an initiative; each update is one project's.

Close with a one-line `Housekeeping:` note only if something structural needs flagging; otherwise omit. No preamble, no sign-off.

Buckets:

1. **Your move** — issues whose **executor** is the Owner: the `agent`-group label reads `human`. Lane membership is read from the executor label, never from the assignee field. On this board the Owner is routinely the assignee of tickets the fleet executes, so an assignee read over-counts this bucket systematically rather than occasionally, and it over-counts in the direction that makes the Owner look blocked when he is not. A ticket assigned to Aled carrying `agent:R3-LAY`, `agent:F0-RGE` or `agent:PR-1SM` belongs to its executor's bucket, not here. A ticket assigned to Aled carrying **no** `agent`-group value at all is *unrouted* — a triage defect for Relay, reported under `Housekeeping:`, never folded into this bucket. Flag overdue and due-soon dates; most consequential first. A candidate whose gating decision ticket is already Done is **not** an open ask — carry the pointer instead of the ask (§ *A ruled decision is not an open ask — point to the deciding ticket*). (This is APP-1039's recurrence in a second artefact. That note's fix — *"read the lanes from the executor label, never the assignee"* — landed in `skill-operating-loop` step 3 only, while this artefact computes the same categorisation independently and was never touched. The lesson is to state a lane-read rule in **every artefact that derives the lane**, not once in the artefact where the friction was first observed. Worked case: APP-824 listed under *Your move* in the 2026-09-15 07:16 UTC bot.Trader-B brief, though it was promoted Backlog → Todo by the 2026-09-14 weekly operator loop precisely so Forge could pick it up. Read 2026-09-18 it carries `task`, `engineering`, `R3-LAY` — no `human` — status Todo, assignee Aled Pritchard. APP-1475, APP-1039.)
2. **Awaiting you** — In Review, especially `agent:PR-1SM` or issues carrying a QA write-up; say what awaits his call.
3. **Done** — moved to Done in the last 24h.
4. **Blocked** — Blocked state, or `agent:F0-RGE` stalled over 24h; give the why where a comment says.
5. **In flight** — In Progress under `agent:R3-LAY` / `agent:F0-RGE` / `agent:PR-1SM`; name the role.

If nothing moved and nothing needs Aled for a **started** project, still post its update with a one-line "nothing moved this window" note and its health set deliberately, matching the established daily cadence. If a whole team has no started projects, post nothing for it — the health scan still runs (§ Health) and the operator's weekly headline carries the parked state.

## Overdue under an open rescope — point to the decision, don't re-nag (APP-866)

Overdue surfacing — the *Your move* bucket's overdue flag and the health scan's *Needs a decision — overdue outside a started project* list — is pure date-arithmetic and is blind to a date already under review. Where an item's due date is the subject of an **open rescope / re-date ticket**, the useful signal is not the item's lapse but the **decision's** lapse. So replace the raw "N days overdue" line with a pointer to that rescope ticket and **its** age:

`- ID · title · rescope under APP-xxx, open N days — awaiting the re-date decision.`

This is **conditional and visible, never silence**: the item still appears, but as a decision waiting rather than a lapse re-nagged. An item whose rescope ticket is itself weeks old is a more actionable fact than the same item read as sixty days overdue for the eighth morning running — that is the `convention-linear` age-ladder principle inverted, restating without escalating. Carry the rescope pointer **once**; do not re-flag the raw lapse each day (`convention-comms-owner`, *Recurring output — read your last one before writing the next*).

This is general to any vertical whose launch or plan items age behind an open rescope, not an MRP special case. Detecting the rescope link is a read: an item is "under an open rescope" when an open (non-Done, non-Canceled) ticket exists whose subject is re-dating or rescoping it — a `blockedBy`/`relates-to` relation to a rescope ticket, or a rescope ticket naming it. Where no such ticket exists, the ordinary overdue line stands unchanged.

*Escalation-ownership — a small Owner design call, flagged for confirmation, not a settled ruling.* The recommended split: the **daily brief carries the rescope pointer once then falls silent**, and the **operator loop owns the standing recommendation** to resolve the rescope (`skill-operating-loop`, *Escalating an Owner-blocked stack*). Authored to that recommendation; confirm before it is treated as fixed.

## A ruled decision is not an open ask — point to the deciding ticket (APP-1644)

Bucket 1 reads **current ticket state only**. Nothing in it checks whether the decision that gates a candidate has already been ruled on a *different* ticket, so a settled decision reappears as an open ask for as many mornings as the dependent ticket goes un-annotated. Worked case: the 2026-09-22 app.AgentArt brief carried APP-1298 (agent-artwork-generator prototype, `human` executor) under *Your move* as needing Aled's call on config-panel scope, when he had ruled that decision in full the previous day — 2026-09-21, a `[stand-up ruling]` comment on the sibling decision ticket APP-1299, visual direction, role→shape table and animation tiers all approved, logged in `app-agentart.log-decisions`, with the follow-on refinement ticket APP-1601 already filed for the following week. APP-1298 itself had simply never been moved or annotated, so the brief read it as still open and the 09:00 stand-up agenda inherited that read. Aled, live at that stand-up: "We can't have a stand up to make decisions on things when the context and status are stale — especially confusing and wasting time when I've already made a call."

So, before a *Your move* candidate is written as an ask, **read its gating decision ticket**: a `blockedBy` relation, or a `relates-to` sibling whose title carries the `DECISION:` prefix. Where that ticket is **Done**, the decision is not open and the candidate is not an ask. Replace the ask line with a pointer naming the deciding ticket, the date it was ruled, and what actually remains:

`- ID · title · decision ruled in APP-xxx on D Month — build/hand-off work remains, no further call needed.`

This is the same move as § *Overdue under an open rescope* above: there, a raw per-ticket date signal is replaced by a pointer to the deciding ticket and **its** age; here, a raw per-ticket "needs you" signal is replaced by a pointer to the deciding ticket and **its** ruling date. Both are **conditional and visible, never silence** — the item still appears, but as work in hand rather than a question re-asked. Carry the pointer **once**; do not re-state the ruling every morning (`convention-comms-owner`, *Recurring output — read your last one before writing the next*). Where the gating ticket is still open, or where no gating ticket exists, the ordinary *Your move* line stands unchanged.

The detection is a read, and it is the same class of read this bucket already does for the lane. Bucket 1 was hardened once before to derive the lane from the executor label rather than the assignee, precisely because the categorisation is computed in this artefact and a rule stated only in `skill-operating-loop` never reached it (APP-1039, APP-1475). The same applies here: state the ruled-decision check in **every** artefact that buckets a "needs you" item, not only in the artefact where the gap was first observed. And where the write-back to the dependent ticket is the thing that is missing, report that gap under `Housekeeping:` as well, so the ticket is annotated once rather than re-read every morning (`convention-decision-log`, *The ticket write is part of the same action as the log entry*).

## Health

Set the `health` field per **project** update each run, and never omit it — an update without an explicit health is the failure this recast exists to remove (Owner, 2026-09-02). Health is **not** read off the project-gated narrative. The escalation tests below can only fire on evidence the run actually gathered, and the project gate drops every non-`started` project before any `list_issues` call — so a team with no started projects would otherwise gather nothing, fire no test, and post the default `onTrack` over an area the run never looked at. That is exactly how a dormant or deadline-blown team read green: careerOS on hold, and Mr Pritchard with five overdue high-priority items and both milestones blown, both posted `onTrack`, 2026-07-21 (APP-624). So Health runs its **own scan, independent of the project gate**.

**The health scan — runs regardless of the project gate, and active-only scope never narrows it.** For every delivery team, before setting any `health`, run two small state-scoped queries against that team (the kind § Token safety already prefers), whether or not the team has a started project — a team the gate empties is never silently read as green:

- **Overdue** — `dueDate` before today, in any state before Done (exclude Done and Canceled). Modest limit; narrow by state if the list is large.
- **Blocked and Urgent** — `state: Blocked` at priority Urgent — the "a blocked item gating an outcome" signal.

These two queries give Health its own evidence even when the project gate has dropped every project. An empty gather is never silently a pass.

**Setting the value** from the scan plus the gathered narrative:

- **`offTrack`** — a target date is at serious risk: a milestone already blown, or several long-overdue high-priority items with no movement.
- **`atRisk`** — overdue high-priority items, or a blocked item gating an outcome, short of the `offTrack` bar.
- **`atRisk` — the parked / dormant case, decided explicitly.** An area with nothing `started`, nothing overdue, and nothing committed or moving (careerOS: one project on Hold, the rest Backlog) is neither progressing nor at risk of missing a committed date. Linear's three-value health field has no `parked` value, so this rule fixes which of the three it takes: **`atRisk`, with the word "parked" in the narrative** — never `onTrack`, because green asserts a health the area does not have, and not `offTrack`, because nothing committed is actually being missed. (Vocabulary gap raised in APP-624; relates to APP-586 on Hold status and target dates. If Linear ever gains a dormant/parked value, prefer it and revisit this rule.)
- **`onTrack`** — only when the scan is clean **and** the team has at least one started project actually moving. A clean scan on a team with no started project is the parked case above, not `onTrack`.

**Surface what drove an amber or red.** When the health scan raises a team to `atRisk` or `offTrack` on overdue items that sit **outside** a started project — so the project-gated body would not otherwise name them — list those items under a `## Needs a decision — overdue outside a started project` heading in the update of the team's most relevant started project, or — where the team has none — in the run output for `task-owner-standup` to carry, one tight line each:

`- ID · title · due date · overdue by.`

Apply the rescope rule above to this list too: an item already under an open rescope ticket is shown as its decision's age, not its raw overdue count (`## Overdue under an open rescope`). This shows why the initiative is not green rather than asserting a colour with no visible cause, and it mirrors the os.Claude canon exception: an overdue item assigned to Aled is a "what needs you" item wherever it lives. It stays a **read-only** surfacing — Health never changes an item's state, priority, or assignment. For the parked case with nothing overdue, this task writes nothing: the operator's weekly headline on the vertical's initiative carries the amber (`skill-operating-loop` step 7), and `atRisk`-with-"parked" remains the value it uses.

## Who reads this

The daily stand-ups consume this report: each operator's `task-*-operator-standup` reads its own projects' updates as the morning's status input, and Relay's `task-owner-standup` reads all of them with the five stand-up documents to build the 09:00 agenda. The weekly operator loops do **not** read it — they consume `task-goal-review` and `task-strategy-review` (cycle-level progress against goal), so this report never serves two cadences (Owner decision 4, APP-876).

## Token safety

`list_issues` returns full descriptions and can overflow on large teams. The project gate above is the first defence — idle projects are never queried. Then query by state (and team) with modest limits; request only what each bucket needs; if a list is too large, **page it with its `cursor` and trim its `fields` — do not narrow the set**. Narrowing was the right instinct against overflow and the wrong one against completeness: a bucket narrowed to fit is indistinguishable from a bucket that was always that size, which is the one defect this report cannot self-detect (see **A bucket is not reported until its read is complete**, below). Paging with a compact `fields` set answers both — the full descriptions are what overflows, so `fields` is what makes a page cheap, and the `cursor` is what makes the set whole — and the overflow risk this section exists to manage is then handled by the shape of each page rather than by dropping rows out of the bucket. Narrowing by state stays correct where the narrower state **is** the bucket's own definition, and never as a way of making an oversized list fit.

Never call `list_issues` with an `assignee` filter and **no** `state` — on a large team it returns every assigned issue with full descriptions and overflows the tool result (observed 2026-06-24: ~99k characters for one team, exceeding the result limit). For the "Your move" bucket, scope to `label: human` plus `state: Todo`, per team — **not** to `assignee`. The bucket is defined by the executor label (§ *Body format*, bucket 1) and an assignee-scoped query cannot express that definition; the label filter is also the narrower of the two on this board, so it is the better overflow defence as well as the correct read. Issues carrying `human` that are Blocked or In Review already surface through their own state-scoped buckets, so a single Todo-scoped label query is enough. Run one further small query per team for the unrouted class — assigned to Aled, `state: Todo`, no `agent`-group value — and report that under `Housekeeping:`, not under *Your move*. The rule is restated here rather than left to the bucket prose because the assignee scoping lives in this recipe: a lane-read rule stated only in prose does not reach the query a run actually issues, which is how APP-1039's fix landed in `skill-operating-loop` and left this artefact untouched (APP-1475, APP-1039). The os.Claude canon sweep is likewise state-scoped (`state: Blocked` + `label: PUL-5E`), so it stays small. The health scan (§ Health) is likewise two small state-scoped queries per team — overdue by `dueDate` plus a state filter, and Blocked + Urgent — so it stays small too.

**A bucket is not reported until its read is complete (APP-1716).** Every bucket query above is paginated, and a page is not a set: `list_issues` returns `hasNextPage` and a `cursor`, and a first page that comes back under the limit reads exactly like a complete one. So **page each bucket query until `hasNextPage` is false**, or narrow it by state until one page provably holds the set, before writing any count or list into an update. A truncated bucket is the one defect this report cannot self-detect: it posts a shorter, entirely plausible list, and every consumer — the five operator stand-ups and `task-owner-standup` — inherits the undercount with nothing marking the gap. Where completeness cannot be established inside the run's budget, **report the bucket as partial under `Housekeeping:`**, naming the query, rather than posting the short list silently. That is the posture `convention-linear` already fixes for an enumerated set that disagrees with its own count: proceed and state the caveat, never narrow in silence (*Reading Linear safely*; APP-890).

**And cross-check each bucket against yesterday's own update.** These buckets are **current-state** reads with no recency filter (§ *Window*), so an item that stood in a bucket in a prior brief and is absent today **must have changed state** — and its own `state` read is the check. Read the previous update for the same project before writing the new one (`convention-comms-owner`, *Recurring output — read your last one before writing the next*, already load-bearing in the two pointer rules above), and for any item that has dropped out **without** a state change, either restore it to its bucket or name it under `Housekeeping:`. It is a cheap diff against a document the run already has reason to open, and it is what catches a silent drop. (Worked case: the 22 September Articles morning brief listed eight In Review items including MRP-28; the 24 September brief listed eight again with MRP-28 absent — while a direct `list_issues(team: "Mr Pritchard", state: "In Review")` at 07:28Z that same morning showed MRP-28 still In Review with `updatedAt` unchanged since 2026-09-14: it never left the state, was never commented, was never actioned. Board truth for the team's review queue was ten; the brief reported eight, of which MRP-53 sits in the Operations project and is correctly out of an Articles-only scope, and MRP-28 was simply dropped. Caught only because the consuming stand-up's own `list_issues` read was its primary source and the brief its cross-check, not the reverse. Same silent-undercount family as APP-1475 and APP-1039, but on the count rather than the field. APP-1716.)

## On error

If posting to one initiative fails, continue with the others and note the failure in your run output. Do not retry destructively.

## Setup

- Deployed as a **Cowork scheduled task** (cloud) via a loader config carrying the canonical loader line in `core-operating-model` (*The loader line — one form*) and nothing else. The name half is what resolves on this surface — a cloud task has no `.claude/skills/` at its working directory (APP-1101). Daily, ~7–8am.
- **Connector:** Linear only. No GitHub, no repo — this task touches no code.
- Output: one project status update per started project across the delivery teams (not a file). Runs before the five operator stand-ups (07:03–07:24), so the ~07:00 slot stands.
