---
title: Tasks (markdown outliner, in development) (part 6)
---
# Tasks (markdown outliner, in development) (part 6)

## What it is

This is part 6 of the [Tasks (markdown outliner, in development)](tasks-v2.md) page. It carries the next stretch of the material on that page, moved here because a single page is capped at 40,000 characters.

## Where to find it

Reach this part through [Tasks (markdown outliner, in development)](tasks-v2.md) — it lists every part and explains where the feature lives in the product. Everything below is reached from the same place.

## How it behaves

Everything below is the behaviour, detail and edge cases that belong to this stretch of the Tasks (markdown outliner, in development) page.

### Today — your daily plate (a flat, editable list)

A dedicated **Today** surface holds the day's true priorities across every project. It is a **non-destructive overlay**: a task on Today keeps its home project and `list_id` — Today just flags it (`daily_plate_date` = the local day). Nothing is moved or copied; at day's end a task simply drops off Today and is still sitting in its project.

**It behaves like any other project — fully editable.** Today renders your plate as one **flat, editable list** using the SAME rows the "All Projects" view uses: edit a task inline, open its Task details (due / priority / context), launch an agent on it, use the ⋯ menu, check it off — all right here, without bouncing to the project. Editing a task on Today edits the **real task in its home project** (it's the same row shown in two places — nothing is copied). Done tasks collect in a **Done** section below. (The older read-only "Do · best first" / "Keep · must land today" bands were dropped in favor of this editable list; deadlines now show as the normal due chip on each row, and snoozed tasks stay hidden until they wake.)

**Where it lives.** A **Today** tab (calendar icon) sits **directly below "All Projects"** at the top of the projects rail — a project-style row rendered above your projects (and above New project / Archived) — AND a **Today** section is **pinned at the top** of the "All Projects" combined view, styled as a project section (calendar glyph + bold heading), staying fixed above your project groups as they scroll (a long day's plate scrolls inside its own capped height, so it never swallows the screen). That Today section is **always shown** — when nothing is on today's plate it reads a quiet "Nothing scheduled for today yet" in place of the list, followed by the same **"Plan my day with AI"** button described below, so an empty Today in the combined view can kick off planning in one click (it previously had no button). "Today" is always the first thing at the top of the combined view. So the day's plate is the first thing you see in the combined view, while the rail keeps a Today tab right under "All Projects". Today is mutually exclusive with All Projects and any single-project selection.

**Plan my day with AI (the prominent button).** Today carries a prominent **"Plan my day with AI"** button — in the header, and the SAME button also appears in the "All Projects" view's Today section when that's empty. When the **Today tab** itself is empty it shows a **grid of action cards** instead of a bare message — **Plan my day**, **Catch me up** (with a count of caught items), an adaptive **Suggest** card (a next-best-task pick for power users; a start-or-continue-your-AI-coaching card for newer ones) and **Brain dump** — so an empty day teaches by doing in one click. Once everything on today's plate is done, a two-card version (Suggest and Brain dump) shows above the finished list. It launches a **real Claude session** (a full agent, not the cheap prioritizer) steered by a **Daily Focus Planner** prompt and handed **today's plate, your whole open board, and your strategic context** — so it starts already knowing what's on your plate and reasons from it (leading with a point of view) instead of asking you to list everything from scratch. It talks through your day with you and — once you approve — captures what to focus on.

- It's a **paid session** (real tokens per turn) — it launches straight from the labelled button (no confirm pop-up), and it can never be triggered by a background agent (app-only).
- **Your open board** = every open task across all your projects that isn't already on today's plate, grouped by project and bounded — so the agent starts oriented over what's really open and never has to go fetch it.
- **One "Context loaded for Planning AI" divider** shows the loaded planning context (today's plate + your open board + your projects) at the top of the chat, collapsed, so you can see exactly what it started with. The app's login token still reaches the agent but is never shown in that divider.
- **Strategic context** = your **AI Coaching "About you" profile** (your goals, standing priorities, how you like to work). It's folded into the session **privately** — used only to steer the AI, addressed to you in the second person, never shown back in the chat (not even in that divider), in the session title, or written into your tasks. It appears only when **AI Coaching** is enabled; without it, the session still works from your plate + board alone.

**How tasks get onto Today.**

- Add a task straight to Today (you pick which project it belongs to).
- **Create one inline, from a row you're already on** — **Add subtask** (⋯ menu / right-click), **Enter** for a new task below, **Shift+Enter** for one above. Anything created this way lands on today's plate as a **user pin**, so it stays put through a re-arrange. Because Today is a flat list it appears as its own row rather than indented under its parent — it is still a real sub-task back in its home project.
- **From the Move-to-project menu** — on any existing task (its ⋯ menu, right-click, or the `V` key) pick **Add to Today** to put it on the plate, or **Remove from Today** to take it off. It's the same menu you use to file a task under a project, so moving things on and off Today works "just like any other project"; being an overlay it keeps the task's home project, and a hand-added task is a **user pin** (`plate_source = null`) that a re-arrange never disturbs.
- The **"Plan my day with AI"** session can add tasks to today's plate for you (only after you approve).
- Or accept the daily **check-in** interview: the AI reviews what is due and slipping and proposes a focus; on accept, Today is **arranged** — the focus tasks + any due-soon deadlines are placed on the plate. Arrange is **deterministic and free** (no AI, no spend): the interview does the thinking; arranging just materializes its result plus your deadlines. A one-tap **Re-arrange** redoes it anytime.
- Each placed task carries a provenance mark: `plate_source = 'ai'` (arrange placed it) vs a user pin (`null` — you added it by hand). A re-arrange never touches a task you pinned yourself.

**The daily reset.**

- Past items drop off Today on their own (still in their projects).
- **Roll unfinished → tomorrow** (with undo) carries anything you did not finish forward to tomorrow's plate; done rows stay put and provenance is preserved.

**For AI readers — the wiring.**

- **Data:** two additive `tasks_v2` columns — `daily_plate_date` (LOCAL YYYY-MM-DD, nullable) and `plate_source` (`'ai'` | `null`/user). A partial index (`daily_plate_date IS NOT NULL AND is_deleted = 0`) keeps the "on today's plate" filter cheap. Migration `20260707211837-tasks-v2-daily-plate-columns` (PRAGMA-guarded, additive).
- **Editable surface:** `TaskV2TodayView` renders the plate with the shared editable `TaskV2Row` + `TaskV2RowActions` + `useMasterViewKeyboardActions` + `useTaskV2CommandKeys` (mirrors the master view; MasterView is NOT refactored). Flat (no bands), snoozed pre-filtered, done tasks in a separate section; every create on this surface threads BOTH the row's `listId` (the plate has no active list) **and `dailyPlateDate: today`** — the plate renders only `daily_plate_date === today`, so a create that omits the stamp is filtered straight back out and reads as a dead button (contract **I6a**; `plateSource` stays unset so the row is a user pin). That covers the header quick-add _and_ the row-level creates (`useTaskV2RowCreation`'s `handleAddSubtask`/`handleCreateBelow` via their optional `dailyPlateDate` param, plus `useTodayRowActions`' own `handleCreateAbove`), and `commitDraft` must carry the field too or the row vanishes on save. Outline numbers omitted; reorder is within-project siblings only (cross-project = the explicit Move-to-project).
- **Plan-my-day agent:** the prominent button → `TASKS_V2_LAUNCH_PLATE_AGENT` (`tasks-v2:launch-plate-agent`, **IPC-only — no CLI route**, `deferred-no-route`) → `buildPlateAgentPrompt(db, plateDate, { settings })` (`src/main/services/tasks/plate-agent-prompt.ts`) → `createSessionWithPrompt` bound to the Tasks workspace project. **Steering = the user's verbatim "Compass" Daily Focus Planner prompt** (`PLATE_AGENT_PROMPT`, kept in its own module `plate-agent-compass-prompt.ts` because it embeds markdown fences) + an Omniscio `PLATE_AGENT_BRIDGE` that overrides Compass's blank-slate opening (start from the loaded plate/board, open with a point of view, question only calendar/energy/capacity). Hidden/visible split: the steering + the strategic context (`buildCoachingBrief`, gated by `ai-coaching`, fail-open, ≤2000 chars, second-person **"Background about you"** heading) + the day's plate (bounded `MAX_PLATE_TASKS = 40`) + the **open board** (`renderOpenBoard` — open tasks NOT on today's plate, grouped by project, bounded `MAX_BOARD_TASKS_PER_LIST = 12` / `MAX_BOARD_TASKS_TOTAL = 120` / per-item `MAX_BOARD_TASK_CHARS = 120`) + the **project roster** + the shared **CLI route cheat-sheet** all ride HIDDEN; the user sees only `PLATE_AGENT_VISIBLE_FRAMING`. `prompt` always contains `displayText` (visible-subset); the steering + strategic context NEVER reach `displayText` (steering-hidden). **The loaded-context divider:** the builder also returns `contextDisplay` (plate + board + roster — the actionable context only, never the steering/coaching/footer), passed to `createSessionWithPrompt` as `seedContext: { label: 'Planning' (PLATE_AGENT_SEED_LABEL), content }` so ONE "Context loaded for Planning AI" divider shows it; the Bearer token still folds into the prompt but is never shown in the divider. The IPC handler and its verbatim CLI-action dispatch mirror (`tasks-v2-launch-plate-agent-handler.ts`) both pass it. Launched directly from the button (no confirm pop-up) + an in-flight guard keyed by the plate date; store `launchPlateAgent(plateDate?)` → `setDetailSessionId`.
- **Deadlines (load-bearing):** v2's `due_at` is a FULL ISO datetime and v2 has NO `ai_due_at`, so the due-soon math normalizes the instant to the user's LOCAL date first — `src/shared/tasks-v2-deadline.ts` (`isDueSoonV2` / `effectiveDeadlineV2`), NOT the generation-agnostic `src/shared/tasks-deadline.ts` helper (which assumes a date-only string and reads NaN off an ISO time portion).
- **Arrange:** `arrangeTodayPlate(db, plateDate)` (deterministic, DB-only) — target = the day's check-in focus (still-open) ∪ due-soon deadlines; claims each as `plate_source='ai'` unless already there or user-pinned; drops an ai-owned pick no longer selected; never touches a user pin. Idempotent.
- **Channels + routes:** `TASKS_V2_ARRANGE` (`tasks-v2:arrange`, `POST /tasks-v2/arrange`) + `TASKS_V2_ROLL_PLATE` (`tasks-v2:roll-plate`, `POST /tasks-v2/roll-plate`) + `TASKS_V2_LAUNCH_PLATE_AGENT` (`tasks-v2:launch-plate-agent`, IPC-only). Accepting the check-in (`TASKS_V2_CHECKIN_ACCEPT` / `POST /tasks-v2/checkin/accept`) also arranges the plate, in ONE transaction (atomic — never "accepted but not arranged").
- **Contract:** [`.claude/memory/contracts/tasks-v2-contract.md`](/.claude/memory/contracts/tasks-v2-contract.md) → `today-plate`, `today-editable`, `today-plate-agent`.

### Projects & master context

> **Naming note.** The UI calls these **"projects"**, but everything persisted or wired still uses the original **"list"** spelling — the `tasks_v2_lists` table, the `list_id` column, the `TASKS_V2_LISTS_*` channels, the `/tasks-v2-lists/*` CLI routes, and the `tasks-v2.md` mirror's `## {list}` headings are byte-stable. The relabel changed only user-visible text (the same convention Omniscio uses for KMS). So the table and channel names below keep saying "list" — that's correct.

Each project is a row in the **`tasks_v2_lists`** table (migration `src/main/db/migrations/20260602002635-tasks-v2-lists.ts`):

| Column                      | Type    | Notes                                                                                                                                                                                                                    |
| --------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `id`                        | TEXT PK | `randomUUID()`                                                                                                                                                                                                           |
| `name`                      | TEXT    | list name (1–120 chars, Zod-enforced)                                                                                                                                                                                    |
| `color`                     | TEXT    | optional project color, nullable — **default none ⇒ NO dot**                                                                                                                                                             |
| `icon`                      | TEXT    | optional `emoji:<char>` \| `lucide:<name>`, nullable (migration `20260620173500-…`); shows in place of the color dot, edited from the project ⋯ menu — the dot is the no-icon fallback, painted only when a color is set |
| `master_context`            | TEXT    | the shared markdown note, nullable (≤ 50 000 chars)                                                                                                                                                                      |
| `default_project_id`        | TEXT    | reserved for a later chunk, nullable                                                                                                                                                                                     |
| `sort_order`                | INTEGER | 1024-spaced, controls rail + mirror order                                                                                                                                                                                |
| `created_at` / `updated_at` | TEXT    | ISO timestamps                                                                                                                                                                                                           |
| `is_deleted`                | INTEGER | 0/1 soft-delete                                                                                                                                                                                                          |

The migration also adds `list_id` to `tasks_v2`, seeds one **Inbox** list, and backfills every list-less task into it. The **only** writer of `tasks_v2_lists` is `src/main/db/queries-tasks-v2-lists.ts` (`listTasksV2Lists`, `getTaskV2ListById`, `getDefaultInboxListId`, `createTaskV2List`, `updateTaskV2List`, `reorderTaskV2List`, `softDeleteTaskV2ListCascade`). Deleting a list cascades the soft-delete to its tasks (reversible — links preserved).

List CRUD has its own gated IPC channels (in `src/shared/ipc-channels/productivity.ts`, handlers in `src/main/ipc/tasks-v2-lists-handlers.ts`):

| Constant                 | Channel string           | Purpose                                                   |
| ------------------------ | ------------------------ | --------------------------------------------------------- |
| `TASKS_V2_LISTS_LIST`    | `tasks-v2-lists:list`    | all lists, by `sort_order`, + per-list open-task `counts` |
| `TASKS_V2_LISTS_CREATE`  | `tasks-v2-lists:create`  | append a list; returns it                                 |
| `TASKS_V2_LISTS_UPDATE`  | `tasks-v2-lists:update`  | patch name / color / icon / master context                |
| `TASKS_V2_LISTS_DELETE`  | `tasks-v2-lists:delete`  | cascade soft-delete the list + its tasks                  |
| `TASKS_V2_LISTS_REORDER` | `tasks-v2-lists:reorder` | reposition a list (fractional `sort_order`); IPC-only     |
| `TASKS_V2_LISTS_CHANGED` | `tasks-v2-lists:changed` | push — cache-invalidate, strict empty payload             |

**Every list mutation (create / update / delete / reorder) schedules a mirror write** — the `## {list}` headings, their order, and the master-context blockquotes all live in `tasks-v2.md`. (This is the deliberate divergence from _task_ reorder, which does not write the mirror.)

In the renderer, `useTasksV2Store` tracks `lists`, `activeListId`, and `taskCountByList` (the backend counts) and files new tasks into the active project (or, in the giant view, the picked one). The UI adds a project rail (`TaskV2ListRail.tsx`) and a pinned master-context card (`TaskV2MasterContext.tsx`). **The rail is a persistent FILTER, not just a switcher:** an **always-present "All Projects" entry** (the giant default view — valid even with a single project) plus one entry per project that filters the view down to it. The rail supports:

- **Drag-to-reorder** the projects (the whole tab is the drag surface; a plain tap still switches the filter).
- **Clean rows — icon + name + a right-aligned count, no per-row buttons.** The old per-row quick-add **"+"** was removed (add a task by switching to the project and using its always-present **"Add a task"** row, or from a master-view project header's **"+"** — see [Per-project "+" add](#per-project--add)); with no trailing buttons the count sits flush-right in one column and the name takes the freed width. A project with **no icon and no color** also drops its blank **leading** slot (`ProjectIcon collapseWhenEmpty`), so its name starts flush-left and reclaims that width too; a project with an emoji/icon or a color still shows it, and the fixed **All Projects / New project / Archived** rows keep theirs.
- **Creating a project opens it and drops you into a first task.** The fixed **"New project"** row reveals an inline name input (Enter submits, Escape cancels, blank ignored — wired to the store's `createList`). On submit, the rail uses the new project's id to switch the view INTO it (`setActiveList`) and arm a one-shot signal (`autoStartFirstTaskListId`); the per-project outliner consumes it once and auto-opens a blank task **in edit mode**, so you start typing the first task immediately — no "Press Enter" step. **Desktop only** (an unprompted edit box would pop the mobile soft keyboard, the same reason focus-on-entry is desktop-only — mobile still opens the project, just without the auto task box). The signal self-clears on use and if you navigate away first, so it can't fire later on an unrelated empty list. Locked by the contract's `create-opens-project` invariant (`TaskV2ListRail.test.tsx` + `TaskV2Outliner.test.tsx`). (The giant **All Projects** view also has its **own** bottom **"New project"** box — that one deliberately **stays** on the giant view instead of switching in; see [The giant default view](#the-giant-default-view--all-your-tasks-in-one-list).) You can also spin up a project **from a task's Move-to-project menu** — its **"＋ New project…"** row creates the project and moves the task(s) straight into it, then opens it.
- A **per-project options menu** (`TaskV2ListMenu.tsx`) — rename, recolor (the shared `ColorPicker`), **set an icon** (the `IconPicker` — an emoji or a Lucide glyph; see [Per-project color & icon](#per-project-color--icon)), and delete (a danger `ConfirmDialog`; Delete is disabled when only one project remains; **Ctrl/Cmd+Z** undoes a deleted project). The rail row has **no ⋯ button** — open the menu by **right-clicking** (desktop), **long-pressing** (mobile), or **Shift+F10 / Menu** (keyboard) on the row. The **master-view project header keeps its ⋯ button** for this same rename/color/icon/delete menu (the rail and the header ⋯ both drive one shared `useListOptionsMenu` hook, so a project is customizable wherever its name appears); **right-clicking the header now opens the broader [project actions menu](#select-a-project--act-on-the-whole-thing)** (which offers a direct **Delete project…** row, and reaches rename/color/icon via its "Rename, color & icon…" row), not this menu directly.
- A **per-project open-task count badge** that is accurate for _every_ project (backend-computed), not just the active one.
- A **resizable width that respects the sidebar.** The rail starts matched to your sessions-sidebar width — the slot it stands in for, since Tasks `panelOwnsLayout` suppresses that sidebar — instead of a fixed column. Drag the handle on its right edge to resize it in place (double-click resets); your width persists to a **dedicated `tasksV2RailWidth` setting** (a separate key from `sessionsSidebarWidth`, so resizing the rail never makes the real sidebar "jump"). It reuses the shared, freeze-hardened `useResizeSidebar` + `ResizeHandle` — see the contract's invariant 13.

**On mobile the rail is not a side column.** A side rail beside the content leaves a phone almost no room (the content column collapses to a one-word-per-line sliver), so on `useIsMobile()` `TasksV2View.tsx` drops the side rail entirely — the content column goes full-width — and moves the **same `TaskV2ListRail`** into a `DrawerShell` slide-up sheet. A header **switcher button** (`data-testid="tv2-mobile-list-switcher"`, showing the active list's name + a chevron) opens it; the sheet auto-closes when the active list / master view changes, so a tap-to-switch dismisses it. The header action buttons (Check-in, Send to inbox, Export JSON, Suggest breakdowns) collapse to **icon-only** on mobile (the text label stays in the a11y tree as `sr-only`) and the header `flex-wrap`s so nothing spills off-screen. Because the sheet is a transformed/overflow container, `TaskV2ListMenu.tsx` portals to `document.body` so its `fixed` backdrop/box is positioned against the viewport (never clipped inside the sheet) — identical on desktop, where its coords already come from the trigger's `getBoundingClientRect()`. Desktop is unchanged. Locked by the `TasksV2View — mobile layout` describe in `tests/unit/features/tasks-v2/TasksV2View.test.tsx` (see the contract's invariant 13).

Tasks can be **moved between projects** from the outliner: a focused row shows a "Move to project" trigger (`TaskV2MoveToListMenu.tsx`) that re-files the row and its whole subtree into the chosen project. (Moving to another project is its own explicit action — it is NOT a reorder.) The markdown mirror groups tasks under a `## {list name}` heading (in `sort_order`) with the master context as a `> ` blockquote beneath it; tasks with no known project fall under Inbox (or an `## (Unfiled)` section) so none are lost.

### Per-project color & icon

Each project carries an optional **color** (default: **none**) and an optional **icon** — an **emoji** or a curated **Lucide glyph** — chosen from the project's ⋯ options menu (the `IconPicker` sits beside the existing `ColorPicker`). The icon is stored on `tasks_v2_lists.icon` as a self-describing string (`emoji:<char>` | `lucide:<name>`; null / empty / unknown ⇒ no icon) and rendered by the shared `ProjectIcon` wherever a project name appears (the rail row and every master-view header). **`ProjectIcon` falls back to a color dot** when there is no icon or the string is malformed/unknown — but the dot is painted **only when the project has a color**. Projects **default to no color**, so by default a project shows **no dot**: **in the rail** a no-icon, no-color project drops its leading slot entirely so its name reclaims the width (`ProjectIcon collapseWhenEmpty`), while **master-view project headers** keep the empty same-size slot so their names stay aligned. A colored project shows its dot, and a bad icon value never crashes a render. The dot is the shared `ListColorDot` (color set ⇒ a `rounded-full` dot; no color ⇒ nothing); a no-color **Lucide** icon tints neutral (inherits the row text color), never an accent. The pure parse/format lives in `src/shared/tasks-v2-icon.ts`; the curated Lucide allow-list in `src/renderer/src/features/tasks-v2/project-icon-catalog.ts`. Icon is **update-only** (creating a project never sets one). The options menu that hosts both pickers opens from the **master-view project header's ⋯ button** and from a **rail row via right-click / long-press** (one shared `useListOptionsMenu` hook), clamped on-screen via `containToViewport`.

The **built-in views — Today and Caught** — support the **same context menu** (right-click / Shift+F10) as regular projects: rename, recolor, set an icon. Because these are view-mode toggles (not DB-backed `TaskV2List` rows), their customizations persist to `AppSettings` (`tasksV2TodayColor` / `tasksV2TodayIcon` / `tasksV2TodayName`, and the `Caught` equivalents) instead of the `tasks_v2_lists` table. The menu reuses the same `TaskV2ListMenu` component with **Delete disabled** (built-in views cannot be removed) and **Move up/down omitted** (their positions are fixed). When no customization is applied, the default Lucide icons (CalendarDays for Today, Sparkles for Caught) and the i18n-derived labels render unchanged.

### Per-project "+" add

Each **master-view project header** carries a **"+" (add task)** button. Clicking it drops a new empty top-level task into **that** project and opens it for editing immediately (the top-level twin of "Add subtask"); a **collapsed** project is expanded first so the new row is visible. (The **rail** row no longer carries a "+" — its rows are clean icon + name + a flush-right count; to add from the rail, switch to the project and use its always-present **"Add a task"** row.) With the old top "Add a task" box removed, this per-project **"+"** and the outliner's **"Add a task"** row are the primary ways to add a task.

### Inline "Add a task" row at the bottom

Beneath the tasks there's an **always-present blank entry row** you can click and start typing into right away (Notion / Apple-Reminders style) — no "+" button, no separate box. It's there in **both** editable views: at the **bottom of a single-project list**, and at the **bottom of each project's section** in the giant default view (a collapsed project shows none). Type a line and press **Enter** and it becomes a real task in **that** project, and your cursor **stays right in the add row** — cleared and ready for the next one — so capturing many tasks is just type, Enter, type, Enter…. It does **not** re-open the task you just typed for editing (you already named it in the row) — that's the rapid-entry behavior, locked by the `add-row-rapid-entry` invariant in the contract. The **"+"** button still drops into editing because it starts a _blank_ task; the empty-list first-task field does too **only on a bare Enter** (also blank) — but a first task you actually **type in full** now settles to the highlighted row border, the same rapid-entry as this add row (no longer re-opened in the green editing state). **On a phone, a small tappable "Add" button appears at the end of the row once you've typed something** — because a phone's on-screen keyboard can't be relied on to commit (autocorrect / predictive text can swallow its Enter), the button is the deterministic way to save; tap it and the task is created exactly as Enter does — same rapid-entry, the cursor stays in the add row for the next one. Desktop keeps Enter only — no button. Visually, the inline row shows a **muted "+" marker** sitting in the **outline-number column** — lined up with the task numbers (`1.` / `2.`) above it, not off under the checkboxes — and hovering the row gives it a subtle **button-like highlight** (the "+" darkens with it), so it reads as a place to add without being a separate click-to-add button (that's the per-project header **"+"** above). The checkbox spacer follows your **Show checkboxes** setting, so the "+" stays aligned whether checkboxes are on or off.

It only turns into a task **once you've typed something and pressed Enter** — so it never leaves empty junk tasks behind (Tasks deliberately deletes blank tasks, so a permanent _real_ empty task would fight that; this row is just a typing slot until you commit it). It's **plain text only** — typing "call vendor due friday" creates a task literally named that; the smart date/tag/dial parsing stays on the **`:` command line** (see [Natural-language capture](#natural-language-capture-the-command-line)), because Tasks never silently guesses a date. The row is its own keyboard context (a typed letter like "x" or "d" types into it, never firing a row shortcut), guards against a double-Enter creating two tasks, and won't submit mid-IME-composition. An **empty project now shows its heading + this add row** in the giant view (empty projects are no longer hidden — see [The giant default view](#the-giant-default-view--all-your-tasks-in-one-list)), so you can add the first task right there. One honest limit: the synthetic **"(Unfiled)"** group — where orphan tasks land when they've lost their project — has no add row, since there's no real project to file a new task into. Lives in `TaskV2NewTaskRow.tsx`; created via the store's `optimisticCreate`.

### Empty list opens ready to type

When a list has **no tasks**, it doesn't make you press Enter first — it shows a **focused "Add your first task…" field with the cursor already in it**, so you type the first task immediately (**Enter then creates it and settles it to the normal highlighted-row border — one Enter finishes it; it no longer stays in the green "editing" state**). This works on **both** surfaces: a single empty project (the per-project list you switch to) and the consolidated "All Projects" view when **every** project is empty (there the first task files into **Inbox**, never "Unfiled"). Same no-blank-task rule as the inline row — typing only creates on Enter — except a **bare Enter still makes a blank editable row** (the legacy affordance the keyboard's create-then-cancel flow relies on, so nothing about that flow regresses). Desktop auto-focuses on entry **and** on re-entry (switching back into a kept-warm panel); mobile shows the field but doesn't auto-focus, so it never pops the on-screen keyboard. Lives in `TaskV2EmptyStateEntry.tsx` (shared by both hosts); locked by the `empty-state-entry` invariant in the contract.

## Related

The overview, the other parts, and everything else worth reading next all sit on [Tasks (markdown outliner, in development)](tasks-v2.md).
