yatt-md 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,172 @@
1
+ # YATT — Yet Another Task Tracker
2
+
3
+ **Markdown is the database. Git is the history. YATT is the UI.**
4
+
5
+ YATT is a plain-text task tracker that lives inside your Markdown files. Tasks are written in a simple one-line format — status, name, duration, assignee, tags — and YATT renders them as a Gantt timeline, Kanban board, or people view. Because everything is text, your whole team can edit tasks in any editor, review changes in pull requests, and get a full audit trail from git for free.
6
+
7
+ ![build](https://img.shields.io/badge/build-passing-brightgreen) ![license](https://img.shields.io/badge/license-MIT-blue)
8
+
9
+ ---
10
+
11
+ ## Why YATT?
12
+
13
+ Most task trackers store data in a database you can't read or diff. YATT flips this: **the `.md` file is the source of truth.** This means:
14
+
15
+ - **Git-native** — every task change is a commit. Branch for experiments, merge to ship, revert mistakes.
16
+ - **No lock-in** — your tasks are plain text. Open them in Vim, VS Code, Obsidian, or cat them in a terminal.
17
+ - **Team-friendly** — pull requests *are* planning reviews. Comment on task lines, suggest changes, approve.
18
+ - **Offline-first** — nothing to sync. The file is always up to date.
19
+ - **Lightweight server** — run `yatt serve` to get a live Gantt, Kanban, and People view. Commit and push without leaving the browser.
20
+
21
+ ---
22
+
23
+ ## What it looks like
24
+
25
+ ![YATT — write, visualize, edit](./examples/animated-showcase.svg)
26
+
27
+ > *3 slides: write plain text → timeline view → kanban board. Cycles automatically.*
28
+
29
+ ```yatt
30
+ title: Product v2 Launch
31
+ start: 2026-01-05
32
+
33
+ [x] Discovery & planning | id:phase1 | 5d | @alice | delayed 3d
34
+ // Research took longer than expected — scope was broader than estimated.
35
+ >> Kickoff complete | after:phase1
36
+
37
+ parallel: design | after:phase1
38
+ [done] UX wireframes | 4d | @carol | %100
39
+ [~] Visual design | 3d | @carol | %80 | delayed 2d
40
+ end: design
41
+
42
+ parallel: engineering | after:phase1
43
+ [x] API scaffold | id:api | 3d | @bob | blocked 1w | delayed 2d
44
+ [ ] Auth service | 4d | @bob | after:api | %45
45
+ [ ] Core features | 1w | @alice | after:api
46
+ end: engineering
47
+
48
+ [ ] Integration & QA | id:qa | 5d | @alice @bob | after:design,engineering
49
+ >> v2.0 Release | after:qa | +deadline
50
+ ```
51
+
52
+ Tasks render as a minimal **line + circle** timeline — a filled dot at start, hollow ring at end, bright leading segment for progress. Milestones are bullseye circles. Hover any row for a details card.
53
+
54
+ ---
55
+
56
+ ## Quick start
57
+
58
+ ```bash
59
+ # Serve a folder of Markdown files
60
+ npx yatt serve ./docs
61
+
62
+ # Or install globally
63
+ npm install -g yatt
64
+ yatt serve .
65
+ ```
66
+
67
+ Open `http://localhost:3000`. Pick any `.md` file from the sidebar. YATT finds all ` ```yatt ` blocks in the file and renders them.
68
+
69
+ The **Edit** tab lets you write raw YATT syntax. Changes save automatically and the view updates live.
70
+
71
+ ---
72
+
73
+ ## Task syntax
74
+
75
+ One task per line:
76
+
77
+ ```
78
+ [status] Task name | duration | @assignee | #tag | %progress | id:slug | after:dep
79
+ // Optional description — attach one or more comment lines immediately after a task
80
+ ```
81
+
82
+ **Status sigils:**
83
+
84
+ | Sigil | Word | Meaning |
85
+ |---|---|---|
86
+ | `[ ]` | `[new]` | Not started |
87
+ | `[~]` | `[active]` | In progress |
88
+ | `[=]` | `[review]` | In review |
89
+ | `[!]` | `[blocked]` | Blocked |
90
+ | `[o]` | `[paused]` | Paused |
91
+ | `[x]` | `[done]` | Complete |
92
+ | `[_]` | `[cancelled]` | Cancelled |
93
+
94
+ **Other syntax:**
95
+
96
+ ```
97
+ >> Milestone name | after:id | +deadline ← milestone; +deadline draws a full-height line
98
+ parallel: name ← parallel block start
99
+ end: name ← parallel block end
100
+ [ ] Task | blocked 2w ← externally blocked for 2 weeks (red ghost)
101
+ [ ] Task | delayed 3d ← running 3 days late (orange overrun)
102
+ [ ] Task | 5bd ← 5 business days
103
+ [ ] Task | 2026-04-01 ← fixed start date
104
+ [ ] Task | !high ← priority: low / normal / high / critical
105
+ ```
106
+
107
+ ---
108
+
109
+ ## The server
110
+
111
+ ```bash
112
+ yatt serve [folder] [--port 3000]
113
+ ```
114
+
115
+ Four views, switchable by tab:
116
+
117
+ | View | What you see |
118
+ |---|---|
119
+ | **Timeline** | Minimal Gantt — lines and circles, hover for details |
120
+ | **Kanban** | Columns by status; drag to reassign; empty columns collapse to slim strips |
121
+ | **People** | Tasks grouped by assignee |
122
+ | **Edit** | Raw YATT source with live save |
123
+
124
+ Click any task in any view to open the **edit modal** — change status, assignees, dates, priority, delayed/blocked duration, and description.
125
+
126
+ ### Git integration
127
+
128
+ The top bar shows your current branch and sync state. No terminal needed for day-to-day use:
129
+
130
+ - **Pull** — fetch and merge the latest from remote
131
+ - **Commit** — stages everything (`git add -A`) and commits with your message
132
+ - **Push** — pushes to remote
133
+
134
+ Merge conflicts and auth are left to the CLI — YATT surfaces the error and tells you to resolve from the terminal.
135
+
136
+ ---
137
+
138
+ ## Key features
139
+
140
+ - **Descriptions** — `//` comment lines immediately after a task become its description (shown in hover card and list view)
141
+ - **Dependencies** — `after:a,b` (AND), `after:a|b` (OR), cross-block by ID
142
+ - **Parallel workstreams** — `parallel: name` … `end: name`
143
+ - **Subtasks** — leading `.` or `..` (sequential within parent)
144
+ - **Progress** — `%60` renders as a bright leading segment on the timeline bar
145
+ - **Delayed / blocked** — `delayed 3d` extends the end; `blocked 2w` shifts the start; both show ghost indicators
146
+ - **Business days** — `5bd` or header `schedule: business-days`
147
+ - **Milestones** — `>> name | +deadline`
148
+
149
+ ---
150
+
151
+ ## Examples
152
+
153
+ | File | Description |
154
+ |---|---|
155
+ | [01-hello-world.md](./examples/01-hello-world.md) | Simplest chart — sequential tasks with descriptions |
156
+ | [02-team-sprint.md](./examples/02-team-sprint.md) | Sprint with statuses, assignees, priorities, dependencies |
157
+ | [03-product-launch.md](./examples/03-product-launch.md) | Phased launch with milestones, subtasks, cross-phase deps |
158
+ | [04-parallel-workstreams.md](./examples/04-parallel-workstreams.md) | Multiple workstreams converging on shared milestones |
159
+ | [05-enterprise-program.md](./examples/05-enterprise-program.md) | Full-scale program — all features combined |
160
+ | [06-delays-and-blocks.md](./examples/06-delays-and-blocks.md) | `delayed X` and `blocked X` with ghost indicators |
161
+
162
+ ---
163
+
164
+ ## Documentation
165
+
166
+ - [SPEC.md](./SPEC.md) — Full language reference
167
+
168
+ ---
169
+
170
+ ## License
171
+
172
+ MIT
package/SPEC.md ADDED
@@ -0,0 +1,368 @@
1
+ # YATT Language Specification
2
+
3
+ ---
4
+
5
+ ## Syntax Cheatsheet
6
+
7
+ ```
8
+ title: My Project
9
+ start: 2026-04-01
10
+ schedule: business-days
11
+
12
+ # Section name
13
+
14
+ [ ] Task name | 3d | @alice | #tag | !high | %40 | id:slug | after:other | <2026-05-01
15
+ // Optional description line (immediately follows the task)
16
+ // More description — shown as tooltip or annotation.
17
+
18
+ [~] Active task | 2bd | @bob | delayed 2d
19
+ [x] Done task | 1w | @carol | %100
20
+ [~] Blocked | 4d | @dave | blocked 1w
21
+ [=] In review | 2d
22
+ [?] At risk | 3d | !high
23
+ [>] Deferred | 2d
24
+ [_] Cancelled | 1d
25
+
26
+ >> Milestone name | >2026-05-15 | +deadline | id:ms
27
+
28
+ parallel: workstream-name | after:slug
29
+ [ ] Task A | 3d
30
+ [ ] Task B | 2d
31
+ end: workstream-name
32
+
33
+ [~] Parent task | 5d | id:parent
34
+ . [x] Sub one | 2d
35
+ . [ ] Sub two | 3d
36
+ .. [ ] Nested | 1d
37
+
38
+ *daily Standup | 15m | @team
39
+ *weekly Review | 1h
40
+ ```
41
+
42
+ ---
43
+
44
+ ## Header Block
45
+
46
+ `key: value` lines at the top of the file, before any task lines. All fields are optional.
47
+
48
+ | Field | Type | Default | Description |
49
+ |---|---|---|---|
50
+ | `title` | string | `"Untitled"` | Display name |
51
+ | `start` | `YYYY-MM-DD` | today | Absolute schedule start date |
52
+ | `end` | `YYYY-MM-DD` | derived | Optional hard end date |
53
+ | `schedule` | `calendar-days` \| `business-days` | `calendar-days` | Default duration semantics |
54
+ | `timezone` | IANA timezone | `UTC` | For date calculations |
55
+ | `locale` | BCP 47 tag | `en` | Date formatting in output |
56
+ | `owner` | string | — | Default assignee for tasks without `@` |
57
+ | `week-start` | `mon` \| `sun` | `mon` | First day of week |
58
+ | `version` | string | — | Free-form version label |
59
+
60
+ Header parsing ends at the first non-header, non-blank, non-comment line.
61
+
62
+ ---
63
+
64
+ ## Line Classification
65
+
66
+ Lines are classified in this order (first match wins):
67
+
68
+ | Pattern | Classification |
69
+ |---|---|
70
+ | Starts with `//` | Comment / task description |
71
+ | `key: value` in header zone | Header field |
72
+ | Starts with `#` + space | Section header |
73
+ | Starts with `>>` | Milestone |
74
+ | Starts with `parallel:` | Parallel block open |
75
+ | Starts with `end:` | Parallel block close |
76
+ | Starts with `*` | Recurring task |
77
+ | Starts with `.`+ space + `[` | Subtask |
78
+ | Starts with `[` | Task |
79
+ | Blank line | Ignored |
80
+ | Anything else | Parse warning; line skipped |
81
+
82
+ ---
83
+
84
+ ## Task Syntax
85
+
86
+ ```
87
+ [status] Task name | field | field | field ...
88
+ // Optional description line
89
+ // Another description line
90
+ ```
91
+
92
+ The status token uses either sigil or word form. The task name is everything after `]` up to the first `|`, trimmed. Fields are pipe-separated and order-independent.
93
+
94
+ **Task descriptions:** `//` lines immediately following a task (with no blank line in between) are that task's description. They are displayed as a tooltip or annotation in rendered output. Standalone `//` lines not immediately following a task are ignored by the parser.
95
+
96
+ ### Status Vocabulary
97
+
98
+ | Sigil | Word | Colour | Scheduling |
99
+ |---|---|---|---|
100
+ | `[ ]` | `new` | steel blue | Normal |
101
+ | `[~]` | `active` | blue | Normal |
102
+ | `[x]` | `done` | green | Normal (historical) |
103
+ | `[!]` | `blocked` | red + stripes | Normal (chain continues unless `+hard-block`) |
104
+ | `[?]` | `at-risk` | amber | Normal |
105
+ | `[>]` | `deferred` | purple | Skipped in chain (zero-duration for deps) |
106
+ | `[_]` | `cancelled` | grey + strikethrough | Fully excluded |
107
+ | `[=]` | `review` | violet | Normal |
108
+ | `[o]` | `paused` | slate-dark | Normal |
109
+
110
+ `deferred` — the following task starts where the deferred task would have started.
111
+ `cancelled` — fully excluded; downstream `after:` references resolve to its start date.
112
+
113
+ ### Field Reference
114
+
115
+ | Sigil | Name | Example | Notes |
116
+ |---|---|---|---|
117
+ | (no sigil) | duration | `3d`, `2bd`, `1w` | Defaults to `1d` if absent |
118
+ | `@` | assignee | `@alice`, `@alice @bob` | Multiple: space-separated or repeat `@` fields |
119
+ | `#` | tag | `#backend` | Multiple allowed |
120
+ | `!` | priority | `!high` | `critical` / `high` / `medium` / `low` |
121
+ | `%` | progress | `%40` | Integer 0–100; fills bar |
122
+ | `id:` | task ID | `id:auth-refactor` | Slug; unique per document |
123
+ | `after:` | dependency | `after:a,b` / `after:a\|b` | AND (comma) or OR (pipe) |
124
+ | `+` | modifier | `+deadline`, `+fixed` | See Modifiers section |
125
+ | (space-separated) | time-shift | `delayed 3d`, `blocked 2w` | See Time-shift Modifiers |
126
+ | `>` | start floor | `>2026-03-01` | Task can't start before this date |
127
+ | `<` | soft due date | `<2026-03-31` | Flags overrun visually |
128
+ | `$` | ticket ref | `$JIRA-42` | Opaque; rendered as link if URL template configured |
129
+
130
+ ---
131
+
132
+ ## Duration Grammar
133
+
134
+ A duration is a positive number (integer or decimal) followed immediately by a unit suffix, no spaces.
135
+
136
+ | Suffix | Meaning | Respects `schedule` setting? |
137
+ |---|---|---|
138
+ | `h` | Hours | No — always calendar hours |
139
+ | `d` | Calendar or business days | Yes |
140
+ | `bd` | Business days | Always business days |
141
+ | `w` | Weeks (7 calendar days) | No |
142
+ | `m` | Calendar months | No |
143
+ | `q` | Quarters (3 months) | No |
144
+
145
+ Examples: `3d`, `5bd`, `2w`, `1.5h`, `0.5m`, `1q`. Decimal durations are valid. Compound durations (`2w3d`) are not supported.
146
+
147
+ ---
148
+
149
+ ## Scheduling Model
150
+
151
+ Tasks are scheduled sequentially by default — Task N starts the day after Task N−1 ends. The first task starts on the document `start` date.
152
+
153
+ **Start date resolution (in order of precedence):**
154
+
155
+ 1. `after:` dependencies — start is the maximum end date of all resolved deps (AND) or minimum (OR).
156
+ 2. `>start-date` field — task cannot start before this date; whichever is later wins.
157
+ 3. Sequential position — starts immediately after the preceding task ends.
158
+
159
+ `deferred` and `cancelled` tasks are skipped in the sequential chain. `done` tasks occupy their slot normally (start + duration); they are historical.
160
+
161
+ `+fixed` combined with `>date` pins the task absolutely — no upstream dependency can push it later.
162
+
163
+ ---
164
+
165
+ ## Parallel Blocks
166
+
167
+ ```
168
+ parallel: blockname | after:other | >start-date
169
+ [ ] Task A | 3d | @alice
170
+ [ ] Task B | 2d | @bob
171
+ end: blockname
172
+ ```
173
+
174
+ Tasks within a block are scheduled sequentially among themselves, starting at the block's anchor point. Multiple parallel blocks opening at the same document position run concurrently — they do not advance the outer sequential chain. The block name is its implicit ID for `after:` references.
175
+
176
+ **Block completion:** `after:blockname` resolves to the end date of the last task in that block (the latest end across all members). To sequence the outer chain after parallel blocks, use explicit `after:` references:
177
+
178
+ ```
179
+ parallel: phase-a
180
+ [ ] Work A | 3d
181
+ end: phase-a
182
+
183
+ parallel: phase-b
184
+ [ ] Work B | 2d
185
+ end: phase-b
186
+
187
+ [ ] Integration | 2d | after:phase-a,phase-b
188
+ ```
189
+
190
+ Block names and task IDs share the same namespace. Nested parallel blocks are not supported.
191
+
192
+ ---
193
+
194
+ ## Task IDs and Dependencies
195
+
196
+ **IDs** (`id:slug`) are slugs (lowercase letters, digits, hyphens), unique within the document. Tasks without an `id:` cannot be referenced by `after:`. Subtask IDs are in the same global namespace as top-level IDs.
197
+
198
+ **AND dependency** (`after:a,b`) — starts after all listed deps have ended.
199
+ **OR dependency** (`after:a|b`) — starts after any one dep has ended.
200
+ AND and OR cannot be mixed on the same `after:` field.
201
+
202
+ The parser performs cycle detection after resolving all `after:` references. A circular dependency is a parse error.
203
+
204
+ ---
205
+
206
+ ## Subtasks
207
+
208
+ ```
209
+ [~] Parent task | 5d | @alice | id:parent
210
+ . [x] Research | 1d
211
+ . [~] Implement | 3d | id:impl
212
+ .. [ ] Unit tests | 1d
213
+ .. [ ] Integration| 1d | after:impl
214
+ . [ ] Review | 1d
215
+ ```
216
+
217
+ Leading dots indicate depth: `.` = level 1, `..` = level 2, `...` = level 3. Subtasks are scheduled sequentially within the parent, starting at the parent's start date.
218
+
219
+ If the parent has no explicit duration, it is computed as the sum of its subtasks' durations. If the parent has no explicit `%progress`, it is the weighted average of subtask progress (by duration).
220
+
221
+ ---
222
+
223
+ ## Milestones
224
+
225
+ ```
226
+ >> Milestone name | >2026-03-15 | +deadline | id:go-live | after:phase1
227
+ ```
228
+
229
+ Milestones have zero duration and appear as a point (diamond ◆) on the timeline. If a `>date` field is present, the milestone is pinned to that date; otherwise its date is the end of the preceding sequential element. Milestones accept `id:`, `after:`, `>`, `+modifier`, `@`, and `#` fields. They participate in the sequential chain (zero duration — start and end are the same day).
230
+
231
+ ---
232
+
233
+ ## Modifiers
234
+
235
+ Modifiers are `+keyword` fields that attach flags to tasks or milestones.
236
+
237
+ | Modifier | Scheduling Effect | Rendering Effect |
238
+ |---|---|---|
239
+ | `+deadline` | Emits overrun warning if computed end > `<due-date` | Red flag/diamond at due date |
240
+ | `+fixed` | Pins task to `>start-date`; deps cannot push it later | Lock icon or hatched bar |
241
+ | `delayed X` | Shifts start+end forward by X; stores original as `plannedStart`/`plannedEnd` | Orange ghost bar at original position |
242
+ | `blocked X` | Same time-shift; semantically: held up externally | Red ghost bar at original position |
243
+ | `+hard-block` | Stops sequential chain at this blocked task | — |
244
+ | `+external` | None | Different bar colour (third-party/vendor work) |
245
+ | `+critical` | None | Bold/bright bar (critical path) |
246
+ | `+tentative` | None | Dashed bar or diamond outline |
247
+ | `+at-risk` | None | Yellow warning icon |
248
+ | `+waiting` | None | Clock icon |
249
+
250
+ ### Time-Shift Modifiers
251
+
252
+ `delayed X` and `blocked X` accept a duration value (`3d`, `2w`, `1bd`, etc.) and push the task's computed start and end forward by that amount. The original (unshifted) dates are preserved and rendered as a ghost bar:
253
+
254
+ - **`delayed X`** — internal slip (team-side). Ghost bar is **orange**.
255
+ - **`blocked X`** — held up by an external factor for a known duration. Ghost bar is **red**.
256
+
257
+ ```
258
+ [~] API integration | 5d | @alice | delayed 3d
259
+ // Was planned for Mon; environment issues pushed start to Thu.
260
+
261
+ [~] SWIFT certification | 8d | @carol | blocked 2w
262
+ // Waiting on SWIFT sandbox credentials — estimated 2-week hold.
263
+ ```
264
+
265
+ Both can be applied to the same task; shifts are applied sequentially.
266
+
267
+ ---
268
+
269
+ ## Recurring Tasks
270
+
271
+ ```
272
+ *daily Standup | 15m | @team
273
+ *weekday Async update | 5m | @team
274
+ *weekly Sprint planning | 2h | @team
275
+ *biweekly Architecture sync | 1h | @leads
276
+ *monthly Stakeholder review | 2h | @pm
277
+ *quarterly Business review | 3h | @exec
278
+ ```
279
+
280
+ Recurring tasks do not participate in the sequential scheduling chain and do not affect other tasks' start dates. They are rendered as repeating blocks across the document's date range.
281
+
282
+ ---
283
+
284
+ ## Sections and Comments
285
+
286
+ **Sections** (`#` or `##` + space + name) group tasks visually. They have no effect on scheduling. A renderer may draw a divider row with the section label.
287
+
288
+ **Standalone comments** (`//` lines not immediately following a task) are ignored by the parser. A `//` sequence within a task name or field value is treated as literal characters.
289
+
290
+ **Task descriptions** (`//` lines immediately following a task, with no blank line) are attached to that task as its description. See Task Syntax above.
291
+
292
+ ---
293
+
294
+ ## Formal Grammar
295
+
296
+ PEG-style sketch (informative, not normative):
297
+
298
+ ```peg
299
+ Document <- Header? Body
300
+ Header <- HeaderLine+
301
+ HeaderLine <- Key ":" SP Value NL
302
+ Key <- [a-z] [a-z0-9-]*
303
+ Value <- (!NL .)+
304
+
305
+ Body <- BodyLine*
306
+ BodyLine <- Comment / Section / Milestone / ParallelOpen
307
+ / ParallelClose / RecurringTask / Subtask / Task
308
+ / BlankLine
309
+
310
+ Comment <- "//" (!NL .)* NL
311
+ Section <- "#"+ SP Name NL
312
+ Name <- (!NL .)+
313
+
314
+ Milestone <- ">>" SP Name PipeFields? NL
315
+
316
+ ParallelOpen <- "parallel:" SP BlockName PipeFields? NL
317
+ ParallelClose <- "end:" (SP BlockName)? NL
318
+ BlockName <- [a-zA-Z0-9_-]+
319
+
320
+ RecurringTask <- "*" RecurToken SP Name PipeFields? NL
321
+ RecurToken <- "daily" / "weekday" / "weekly" / "biweekly"
322
+ / "monthly" / "quarterly"
323
+
324
+ Subtask <- Dots SP "[" Status "]" SP Name PipeFields? NL
325
+ Dots <- "." / ".." / "..."
326
+
327
+ Task <- "[" Status "]" SP Name PipeFields? NL
328
+
329
+ Status <- " " / "~" / "x" / "!" / "?" / ">" / "_" / "=" / "o"
330
+ / "new" / "active" / "done" / "blocked" / "at-risk"
331
+ / "deferred" / "cancelled" / "review" / "paused"
332
+
333
+ PipeFields <- (SP? "|" SP? Field)+
334
+ Field <- DurationField / AssigneeField / TagField / PriorityField
335
+ / ProgressField / IdField / AfterField / ModifierField / ShiftField
336
+ / StartDateField / DueDateField / TicketField
337
+
338
+ DurationField <- NUMBER ("h" / "bd" / "d" / "w" / "m" / "q")
339
+ AssigneeField <- "@" [a-zA-Z0-9_-]+ (SP "@" [a-zA-Z0-9_-]+)*
340
+ TagField <- "#" [a-zA-Z0-9_-]+
341
+ PriorityField <- "!" ("critical" / "high" / "medium" / "low")
342
+ ProgressField <- "%" [0-9]+ ("%"?)
343
+ IdField <- "id:" Slug
344
+ AfterField <- "after:" Slug ("," Slug)* / "after:" Slug ("|" Slug)*
345
+ ModifierField <- "+" ModifierWord
346
+ ShiftField <- ("delayed" / "blocked") SP DurationField
347
+ StartDateField <- ">" ISODate
348
+ DueDateField <- "<" ISODate
349
+ TicketField <- "$" [A-Z0-9_-]+
350
+
351
+ Slug <- [a-z0-9-]+
352
+ ISODate <- [0-9]{4} "-" [0-9]{2} "-" [0-9]{2}
353
+ NUMBER <- [0-9]+ ("." [0-9]+)?
354
+ SP <- " "+
355
+ NL <- "\n" / "\r\n"
356
+ ```
357
+
358
+ ---
359
+
360
+ ## Rendering Model
361
+
362
+ A rendered YATT document is a horizontal Gantt chart with one row per task, subtask, and milestone; section headers as divider rows; and a date axis scaled to the full document range.
363
+
364
+ **Bar colours by status:** `new` → steel blue · `active` → blue · `done` → green · `blocked` → red stripes · `at-risk` → amber · `deferred` → grey · `cancelled` → light grey + strikethrough · `review` → violet · `paused` → slate-dark.
365
+
366
+ **Progress fill:** `%progress` splits the bar — filled portion uses the status colour; remainder uses a lighter tint. `%100` renders identically to `done`.
367
+
368
+ **Standard annotations:** today line (vertical dashed), overrun highlight (if computed end > `<due-date`), assignee initials on bars, optional dependency arrows, hover tooltips on interactive renderers.