--- name: gantora-plans description: > Read, report on, and carefully extend Gantora project plans (.gantora files). Use when the user mentions Gantora, a .gantora file, or a DB folder of plans — to summarise status, list overdue or blocked tasks, add new tasks, or move a task's status on. Gantora plans are plain pipe-separated text files; this skill teaches the exact format and the safety rules for writing them. --- # Gantora plans Gantora is a single-file HTML Gantt / project planner. It keeps its plans as **plain text files** in a `DB/` folder — one file per plan, one row per task, cells separated by `|`, a header row first. There is no server, no lock and no database: **the file on disk is the whole truth**, and the app rewrites the whole file on every save. This document is the format as the app's own parser reads and writes it (checked against the parser on 2026-08-19), plus the rules that keep a plan safe when something other than the app writes it. | File | What it is | |---|---| | `DB/.gantora` (older plans: `.csv`) | one plan — header row, task rows, then a few `#`-lines | | `DB/_lists.csv` | the folder's shared registry of Teams / Systems / People — **read it, never write it**; the app maintains it | | `DB/_*` anything else | the app's own; leave alone | ## 0. Read this part twice — it is where plans actually go wrong These are not style notes. Every rule below is here because an assistant broke it on a real plan, on three separate attempts at the same one (measured 2026-08-20). None of the breakages looked like errors: the file parsed, opened, and was quietly wrong. 1. **Read this whole file before you write anything.** If you cannot read it — the fetch failed, the content came back as binary, you are guessing from the name — **stop and say so.** Ask the person for the file — or, if they do not have one yet because you are about to make their first plan, open . If neither works, say what you could not read and ask them to paste the spec; do not reconstruct the format from a sample — the parts that matter are the ones a sample does not show. *(An assistant wrote this rule for itself, in those words, after failing the other way.)* 2. 🔴 **`Milestone=yes` is a project-level, standalone, zero-day event.** It is NOT a way to mark a task as important, and a milestone is **never a member of a group**. This is the single most common mistake, and the most damaging: a milestone inside a group breaks the group's own dates and the health of everything above it. If you want to say "this one matters", say it in `Notes`. 3. **Never write dates on a group row.** A group row — one that other rows name in their `Parent` cells — is a container, and the app computes its `Start`, `End` and `Duration` from its members every time it loads. Dates you type there are overwritten — and until they are, they disagree with the plan and mislead whoever reads it. 4. **`Status` is exactly one of six words:** `Planned`, `In Progress`, `On Hold`, `Blocked`, `Done`, `Cancelled`. Not a sentence, not a percentage, not a description. Anything the person wants to *say* about a task belongs in `Notes`. `Cancelled` means it will not happen — never use it for "finished", and never delete a row to mean it. 5. **The 28 columns keep their order, and every row has all of them.** Do not skip a column because it is empty — write the empty cell. A single missing `|` shifts every value after it into the wrong field, and the file still opens. **And no trailing `|` after the last cell** — 28 cells is 27 pipes; a pipe at the row's end makes a 29th cell, and the app refuses the file as truncated rather than guess which cell is the ghost. 6. 🔴 **If you are unsure about grouping, leave it out.** A flat list of tasks with correct dependencies is a *valid, good, useful* plan. A wrong `Parent` cell — one naming a UID that is not in the file, or a chain nested past four levels — is worse than no grouping at all, and the person can group rows in the app in seconds. **When in doubt: no groups.** 7. **`Parent` is the child's cell, and it holds a UID.** Membership is written exactly where every assistant expects it: on the member, naming its parent — by the parent's `UID`, never its `Seq` (positions renumber; identity does not). The parent row's own `Parent` cell stays empty unless it too sits inside a larger group. ``` ✓ RIGHT — each child names its parent, by UID 5|Packing|…|2026-03-02|…|2026-03-06|…||no|…|uid-of-nobody-needed 6|Boxes|…|2026-03-02|…|2026-03-03|…|pk4x-…-packing|no|… 7|Labelling|…|2026-03-04|…|2026-03-05|…|pk4x-…-packing|no|… ``` *(In an old file — format 1, no `#META|format` line — membership sits the other way round: a `Group` column on the parent, listing its members' IDs. Read it if you meet it; the app upgrades such files on their next save, and everything you write is format 2.)* Two more that cost real time on the same test: - **Do not invent columns or reorder them** to be helpful. Since format 2 an unknown column does survive the round trip — no Gantora will eat it — but no reader gives it meaning either: the app cannot show it and another assistant will not understand it. What you want to say belongs in `Notes`, or in a `#META` line if it is about the whole plan. - **Read `Predecessors` before you renumber anything.** They point at `ID`s, and `ID`s are positions in time order — see §5. Renumbering without fixing them turns a schedule into a shuffle. ## 1. The file, byte by byte - **UTF-8, no BOM.** The app writes `\r\n` line ends and **always ends the file with a line break**; the reader accepts `\n` too. Write it the way the app does. - **Blank lines are ignored.** - 🔴 **Never touch a file whose name ends in `.trash`.** That is the app's wastebasket: a plan somebody deleted, kept under a name the plan list does not recognise so it can be brought back. Do not read it, do not write it, do not offer to tidy it away. If somebody asks for a deleted plan, tell them the Wastebasket is in the app's DB menu. - **Line 1 is the header** — these 28 names, in this order, separated by `|` (format 2, 2026-08-23; the older names `ID` and `Group` still *read*, but everything you write uses these): ``` Seq|Activity Name|Duration|Start|Hour (Start)|End|Hour (End)|Predecessors|Team|System|Accountable|Responsible|Parent|Milestone|Notes|Status|History|Event|Emoji|Not Finalized|Baseline End|Date History|UID|Created|Modified|History By|Reported|Labels ``` The reader maps columns **by header name**, not by position. A name it does not know is carried through open and save untouched — but carries no meaning to any reader. Do not invent columns. - **A task row is any line whose first cell is a positive integer.** A line whose first cell is not a number is skipped by every reader there has ever been — that is how the `#VIEW` / `#DEL` / `#META` lines below work. - **Truncation is refused, not repaired.** The app will not open a file whose last line is a row narrower than the header, or a row wider than it. Always write complete rows and the final line break. - **Inside a cell**, `|` and line breaks cannot appear as themselves. They are escaped — **`~p` is `|`, `~n` is a line break, `~t` is a literal `~`** — and the reader decodes every cell. Encode `~` first on the way out, or you encode your own output. (A note typed on three lines is stored as `line one~nline two~nline three`.) Do **not** replace a pipe with `/` — that was the app's behaviour until August 2026 and is now simply data loss. - Cells are trimmed on read; a missing trailing cell reads as empty. ## 2. The columns | Column | Format | Notes | |---|---|---| | `Seq` | positive integer | **A position, not a name** — which is why it is no longer called "ID" (the app shows it as `#`). Rows are kept in time order and numbered 1, 2, 3… down the file; a group takes the number directly above its members. The app **renumbers on open**, so a new row with `max+1` is fine — what matters is that `Predecessors` reference Seq values *in this same file*. For identity, use `UID`. | | `Activity Name` | text | | | `Duration` | calendar days, both ends counted (`End − Start + 1`); **`0` on a milestone and on an event** (even a period) | **Recalculated by the app on every save.** Write the arithmetic; never trust it from a hand-edited file. | | `Start`, `End` | `YYYY-MM-DD` | `End ≥ Start`. **A group's dates — and its hours — are derived:** its members' first start (with that member's start hour) and last end (with that end hour); the app recalculates them on every save. | | `Hour (Start)`, `Hour (End)` | `HH:MM` or empty | empty = any time of that day | | `Predecessors` | IDs, `;`-separated | must exist in this file; a task may not precede itself; a task may not start before a predecessor ends | | `Team`, `System` | text | free text; the registry offers suggestions, nothing enforces them | | `Accountable` | **one** `Name` or `Name:hours` | hours a day, `0 < h ≤ 24`, optional; a name without a number costs nothing | | `Responsible` | `Name[:hours]` entries, `;`-separated | same rule | | `Parent` | the parent row's **UID** | on the CHILD row — a row somebody names as their parent **is a group**. Hierarchy is by reference, not indentation, and by identity, not position: the cell survives renumbering untouched. A task belongs to at most one group; groups nest at most **4** levels (group → sub → sub² → sub³ → task). A milestone can neither be nor hold a member. | | `Milestone` | `yes` / `no` | a point in time: `End = Start`, no team/system/people/group | | `Notes` | text, max 500 chars | line breaks escaped as `~n` | | `Status` | exactly one of `Planned` · `In Progress` · `On Hold` · `Blocked` · `Done` · `Cancelled` | **empty on an event**. Not derived from dates — a task past its End is not Done until somebody says so. `Cancelled` is work that will not happen: it counts towards nothing, it can never be late, and the row is kept rather than deleted so the plan still records that somebody meant to do it. Never use it to mean "finished". | | `History` | `YYYY-MM-DD Status;…`, oldest first | **append-only.** Every status change is one entry dated the day it happened; the first entry is the day the row entered the plan. Two entries on one day are allowed. Setting the status a row already has is not a change and writes nothing. Empty on an event. | | `Event` | `yes` / `no` | an event is a milestone *flavour* the plan does not own (a holiday, a freeze): `Milestone` is `yes` on it too, it may have `End > Start` (then it is a period), it carries no status and no history | | `Emoji` | one emoji, events only | empty otherwise | | `Not Finalized` | `yes` / `no` | "these dates are still a guess". Never `yes` on a milestone or an event. | | `Baseline End` | `YYYY-MM-DD` or empty | **only when typed by hand** to overrule the recorded baseline; empty means "work it out from Date History" | | `Date History` | `YYYY-MM-DD YYYY-MM-DD;…` — *the day it was written*, then *the End date that day* | **Written by the app, never by you.** It opens the first time a task's End moves *after the task has left Planned* (first entry = the End it had, and that entry is never rewritten); then one entry per day, last value wins. Empty on an event. | | `UID` | `[A-Za-z0-9_-]{4,40}` | the row's **permanent name** — minted once, never changed, shown nowhere, what a merge matches rows by. See §4 for minting one. | | `Created` | `YYYY-MM-DDTHH:MM:SSZ ` | UTC to the second, then a short writer id, then a display name (optional). Written once. | | `Modified` | same shape | the last change that actually changed something. A renumber is not a change. | | `History By` | `YYYY-MM-DD ;…` | one entry per `History` entry, **same order, same dates**; `-` for an unknown name. If the dates fall out of step with History, the app drops all the names and keeps the history. | | `Reported` | `yes` / `no` | whether the management report speaks about this row. **A main task (top level, not in any group, not a milestone) is always `yes`** — the app forces it. | | `Labels` | words, `;`-separated | free tags; two labels differing only in case are one label | **Reading the 'yes/no' columns:** the parser accepts anything starting with `y`, `i`, `1` or `t` as yes. Always *write* `yes` / `no`. ## 3. The `#`-lines at the end of the file After the last task row, in this order. **Preserve every one of them verbatim when you rewrite a file** — they are somebody's settings and somebody's deletions — except the single `writer` line, which you replace with your own. ``` #VIEW|hiddenColumns|duration;team #VIEW|shownColumns|history #VIEW|columnOrder|id;name;status;start;end;… #VIEW|collapsed|3;7 #VIEW|savedView||view=zoom;level=day;px=24;at=2026-04-15 #DEL||| #META|format|2 #META|readOnly|yes #META|writer| #META|spec|https://gantora.net/format.md ``` - `#VIEW` — how the plan is looked at (folded columns, column order, folded groups, saved views). `~s` escapes a `;` inside a saved view's values. Never generate these; carry them through. - `#DEL` — **a tombstone**: this uid was deleted, when (a `Created`-style stamp) and what it was called. It is what lets a merge tell *deleted here* from *added there*. **Therefore: do not delete rows.** A row that simply goes missing comes back in the next merge with somebody's copy, old uid and all. If the user insists, either point them at the app, or remove the row **and** append `#DEL|||`. - `#META|format|2` — the format's version stamp (2026-08-23). **Write it on every file.** A file without it is format 1 — older column names, membership the other way round (see rule 7) — which the app reads forever and upgrades on its next save. The number moves only on a breaking change: a new column or a new `#META` key is NOT one. - `#META|readOnly|yes` — the plan declares itself read-only (2026-08-19). It is accident-prevention, not protection — but it is the file's own statement, and the app honours it: **when it is present, do not write the file.** Offer to write an editable *copy* under another name, without this line. Absence of the line means writable; never write `readOnly|no`. **And if you set this lock while working on a file, LIFT it when you deliver** — the lock belongs to the writer, and a lock left behind closes the plan to the person it was written for. - `#META|writer|` — which program saved the file last. The app writes `gantora-app`; **you write `claude-skill`**. It is a fact for a support conversation, never a gate: **never refuse a file because of what this line says**, and never refuse one with a `#`-line you do not recognise. - `#META|spec|https://gantora.net/format.md` — where this very format is written up. Write it on every save: it is the file carrying its own manual, so a `.gantora` dropped into any assistant with zero context can explain itself. Never a gate, never fetched by the app. - `#META|summary|` — the plan's **written** executive summary (2026-08-25). Optional: with no such line the report works its own summary out from the state of the plan, and that is the normal case. When the line is present it **replaces** the automatic one rather than being added to it, so write it only when the user has actually said what the summary should be. The text carries a **tiny inline markup**, and nothing outside it — no HTML, ever, because a plan is a file that travels and its summary is drawn in somebody else's app: | | | |---|---| | `*bold*` | bold | | `_italic_` | italic | | `[red]…[/red]` | and `[blue]`, `[orange]`, `[green]` | | anything else | text, including a stray bracket | Emoji need no markup — they are characters. Escape the cell like any other (`~t` `~p` `~n`): a summary sentence normally contains a pipe or a line break. Keep it under 2000 characters; longer is cut. ## 4. Minting identity: UID and stamps Pick yourself a **writer id**: 3–8 lowercase letters/digits. Use `clde` unless the user gives you one. Then: - **UID for a new row:** `---<4 random base36 chars>`, e.g. `clde-mfa3k9x2-0-7hq4`. Check it is not already in the file. **Never change an existing UID** — not on a rename, not on a move, not on a copy; a *duplicate* row is a new row and gets a new uid. - **Stamp** (for `Created`, `Modified`, `#DEL`): `2026-08-19T14:02:11Z clde Claude` — UTC ISO-8601 to the second, no milliseconds, then the writer id, then a name. Name may be the user's name if they gave one. ## 5. The rules, in order of importance 1. **Never lose what you don't understand.** Round-trip every cell and every `#`-line you are not deliberately changing, byte for byte. Unknown columns are someone's data; unknown `#`-lines are someone's settings. 2. **Respect `#META|readOnly|yes`** — read freely, write never; offer a copy. 3. **Read fresh, then write, in one breath.** Never write over a plan you did not read *immediately* before: the user may have edited it in the app in the meantime, and these folders are often synced (Dropbox, OneDrive), so somebody else's save may have landed. Re-read, compare with what you read earlier, and if it changed, re-apply your edit to the new content — or stop and say so. Write the **whole file in one operation**; where the channel lets you, write a temp file in the same folder and rename it over the original. Never leave a half-written plan. 4. **Prefer adding over editing.** Safe: a new task row; a status move (Status + History append + History By append + Modified). Leave to the app: deleting rows, changing UIDs, restructuring groups, rewriting dates that are already being worked to (that is what Date History tracks), editing `#VIEW` lines. 5. **Keep the file's invariants** — exactly six status words; `yes`/`no`; dates as `YYYY-MM-DD`; references to IDs that exist; one group per task; nothing on a milestone but a name, a date and a status; `Reported` = `yes` on every main task; a group's dates are its members' first start and last end; **rows in time order** (by Start, then by Start hour; a group directly above its members; events last) and numbered 1, 2, 3… in that order — the app renumbers on open anyway, but a file already in order is one it rewrites nothing in; the header row first; the final line break. 6. **Stamp your writes:** `Modified` on every row you touched, `Created` on every row you made, `#META|writer|claude-skill` and `#META|spec|https://gantora.net/format.md` at the end. 7. **Never refuse a newer file.** A column, a `#`-line or a writer you do not recognise is not an error — read what you can, say what you skipped. ## 5b. Who owns the plan — two patterns, and the file says which - **A plan the user owns.** They edit it in the app; you are the advisor — review it, add to it, reschedule *on request*. Its name is theirs, agreed in the conversation, in their own language and words, with no marker — even if you wrote its first version. No `readOnly` line. - **A plan you keep for them** — a status board, a roadmap you maintain from what they tell you; they only look at it in the app. Then **you write `#META|readOnly|yes`** (so a stray tap in the app cannot write over your next update — the app offers *Save As* for an editable copy), and the file name carries an **`__ai` suffix** before the extension: `Kitchen renovation__ai.gantora`. The suffix is a beacon for people and programs; the truth is the flag and the writer stamp. **And keep it current as you go** — a status board that lags the conversation is one the person stops trusting, so the plan must never say less than what you have already been told. What "as you go" means depends on the channel (§5c): where you can write — connector, local folder, MCP — every new task and every status change goes into the file the moment it comes up, not batched for later; on a channel with no way back (an attachment, a link) hand over the updated file as soon as the conversation has changed the plan, without waiting to be asked. - **Never rename a plan, and never write over a name that is taken.** On a clash, ask — or add a date to the name you propose. ## 5c. How you reach the file — the channels The rules above are the same on every channel; what differs is the mechanics. | Channel | Read | Write | Mind | |---|---|---|---| | **The user attaches a `.gantora` to the chat** | the attachment | hand back a complete file to download — never a fragment, never "paste this row" | say what you changed, row by row | | **A Dropbox (or similar) connector** — the user's `DB` folder in their Dropbox | read the file through the connector, *immediately* before you write | one call that writes the **whole file**; no temp file is possible, so the fresh-read rule carries the weight | the app's own Dropbox mode writes the same folder — never assume the file is as you left it | | **A local folder** the user has opened for you | read it | temp file + rename | `DB/_lists.csv` and anything `_*` are the app's | | **The Gantora MCP server** | its tools | its tools — they enforce these rules in code | prefer it when it is there | | **A link** — `…/gantora.html#plan=…` | — | hand the user one link; the app opens it read-only, as a shared copy, with no file and no folder | for *showing* a plan, not for keeping one: nothing comes back | **Making a plan link.** The whole plan travels after the `#`, so it never reaches a server. Take the file's text (UTF-8, exactly as you would write it), **deflate-raw** it, base64url-encode the bytes (`A–Z a–z 0–9 - _`, no padding), and put a `~` in front: `#plan=~`. Add `&file=` for the plan's name (the extension is added if missing), and `&view=gantt` / `&lang=hu` if you want a view or a language. Without the `~` the value is read as plain UTF-8 in base64url — a shape a person can make by hand. A fifty-row plan is about 4–8 KB of link; most chat clients carry that, a few truncate very long messages — if in doubt, send the file as well. The base address is whatever Gantora page the user opens: the copy on gantora.net once it is published, or their own `gantora.html`. ## 5d. Before you write a plan from scratch — the planner's interview A good plan is decided before the file: ask, briefly, then write. 1. **What is being done, and in what breakdown** — by room or by trade? by phase or by deliverable? That choice *is* the group structure, so it is worth one question. 2. **Who does what** — `Team` / `Responsible`, even roughly ("me", "the electrician", "the venue"). 3. **What has to wait for what** — the real `Predecessors`, not every row chained to the one above it. 4. **The waiting times, as rows of their own** — drying, curing, delivery lead time, approvals. They are what turn "two weeks of work" into eight weeks on the chart, and a plan without them is a plan that will be late. 5. Dates you are unsure of: write them and tick `Not Finalized` = `yes` — the app draws those pale and dashed, which is exactly what they are. Then write it in time order (rule 5), stamp it (rule 6), and tell the user what the file is called and where it is. ## 6. Reading a plan well - **Where does the project stand?** Count leaf tasks (not groups — a group's status and dates are its members'; not milestones — those get a section of their own). Report by top-level group: Done / In Progress / Blocked / On Hold, **overdue** (`End < today` and Status not Done/Blocked/On Hold), late starts (`Start < today`, still Planned), and what falls in the next week. - **Drift:** baseline = `Baseline End` if set, else the End of the *first* `Date History` entry, else the current End. Drift = End − baseline, in days; positive is late. A row still `Not Finalized` has no drift to report. - **A group's status** is the loudest member: `Blocked > On Hold > In Progress > Planned > Done` — and one member begun makes the group In Progress. - The `History` / `History By` pair tells you who moved what and when; the `Modified` stamp tells you who touched the row last. - **Remember rows by UID, never by ID.** The ID is a position: the app renumbers the plan into time order on every open, so an ID noted earlier in the conversation — or in an earlier conversation — may point at a different row by now. When you expect to come back to a task, note its UID. - **Report, don't echo.** When asked where a plan stands, answer with the counts and the rows that matter (overdue, blocked, this week) — never by reproducing the whole file; a large plan pasted into the chat drowns the answer and the context both. ## 7. Typical asks - *"Add a task: …"* — new row: next free ID, dates as given (`Duration` computed), `Status` = `Planned`, `History` = ` Planned`, `History By` = ` `, fresh UID, `Created` = `Modified` = your stamp, `Reported` = `yes` if top level else `no`, `Not Finalized` = `no`. To put it in a group, **write the parent row's `UID` into the new row's `Parent` cell** — nothing else says membership, and the parent row is not touched at all. Then update `#META|writer`. - *"Mark X done / in progress / blocked."* — change `Status`, **append** to `History` (` `) and to `History By` (` `), set `Modified`. Touch nothing else on the row. If the row already has that status, do nothing and say so. - *"Which tasks are overdue / blocked / due this week?"* — read-only; answer from the rules in §6. ## 7a. Bringing a plan in from another tool ### First, do not do it by hand if the app can do it **The app reads four foreign formats itself**, through its `Import…` menu item: | | | |---|---| | XML Spreadsheet 2003 | `.xml` — what Excel writes on "Save as… XML Spreadsheet" | | HTML table | `.html`, `.htm`, and the `.xls` Excel writes as "Web Page" | | Microsoft Project XML (MSPDI) | `.xml` — MS Project's own "Save as… XML" | | Delimited text | `.csv`, `.tsv`, `.txt` — comma, semicolon or tab, sniffed from the header line | **If the user has one of those and the app in front of them, say so and stop.** The app's importer shows a preview at the depth the rows will land at, repairs what it can, and names every column it could not place — none of which a conversion in a chat window can offer. Converting by hand is the *worse* road when the better one exists. Two things it deliberately will **not** do: a real `.xlsx` (a zip of parts — the app bundles no library and fetches nothing), and a pipe-delimited file, which is one of its own plans and belongs in the DB menu instead. ### When conversion IS your job A format the app cannot read (`.xlsx`, a Jira/Asana JSON export, a screenshot, a description in prose), or a plan the user wants written straight into their cloud folder without opening the app. Then you are writing a `.gantora` from §1–§5, and this is what the source usually gives you: | The source's column | Goes to | Watch for | |---|---|---| | Task / Summary / Activity / Name / Title | `Activity Name` | | | Start / Start Date / Begin | `Start` | reformat to `YYYY-MM-DD`; a time goes to `Hour (Start)`, not into the date | | Finish / End / Due date / Deadline | `End` | same | | Duration / Effort | *nothing* | 🔴 `Duration` is **derived from Start and End** by the app. Never carry a source duration into it — if the source has only a start and a duration, compute the End and write that | | Predecessors / Depends on / Blocked by | `Predecessors` | see below | | Resource Names / Assignee / Owner | `Team` **or** `Responsible` | a person → `Responsible`; a team, department or role → `Team`. If unsure, `Responsible` and say so | | Outline level / indent / Parent task | `Parent` | see below | | % Complete | `Status` | `100` → `Done`, `1–99` → `In Progress`, `0` → leave **empty**, not `Planned`. "Not started" and "planned" are different claims and only one of them was made | | Status / State | `Status` | must become one of the six exactly (§2). A word you cannot map is **reported, never guessed** — a plan silently reset to `Planned` on the way in is worse than one that arrives with no status | | Notes / Description / Comment | `Notes` | escape it: `~` → `~t`, `\|` → `~p`, a line break → `~n` | | Labels / Tags / Category | `Labels` | | | the source's own ID | *nothing* — see below | | ### The four that go wrong 1. **Hierarchy is carried by the CHILD.** A source that indents (an outline level, a "Parent task" column, a WBS code) becomes `Parent` cells: each child row holds **its parent's UID**. The parent row is not touched, and there is no list of members anywhere. Convert the outline into UIDs *after* you have minted every row's UID, not while writing the rows. 2. **`Predecessors` are the file's own `Seq` numbers**, not the source's IDs and not UIDs. So: mint the plan first, then translate each source dependency through a source-ID → new-`Seq` map you built as you went. A dependency you cannot resolve is dropped **and reported** — never pointed at a guess. 3. **Do not carry the source's ID into `UID`.** UIDs are minted here (§4) and must be unique in this file forever; a foreign ID collides the day two exports are merged. If the source ID is worth keeping, it goes in `Notes`. 4. **Do not carry the source's timestamps into `Created` / `Modified` / `History`.** Those record what happened *to this file*. Everything you write is created now, by you, with your stamp — that is what makes the plan's own history readable later. ### Finish with a report, not a file Say what came across and what did not, in this order: how many rows, how many groups, how many milestones; every column you could not place; every status word you could not map; every dropped dependency. **A conversion that reports nothing is not a clean conversion — it is an unexamined one**, and the person has no way to tell those two apart. ## 8. A minimal example ``` Seq|Activity Name|Duration|Start|Hour (Start)|End|Hour (End)|Predecessors|Team|System|Accountable|Responsible|Parent|Milestone|Notes|Status|History|Event|Emoji|Not Finalized|Baseline End|Date History|UID|Created|Modified|History By|Reported|Labels 1|Design|10|2026-09-01|09:00|2026-09-10||||||||no||In Progress|2026-08-19 Planned;2026-09-01 In Progress|no||no|||clde-mfa3k9x2-0-7hq4|2026-08-19T14:02:11Z clde Claude|2026-09-01T08:10:00Z ak7 Anna Kovacs|2026-08-19 Claude;2026-09-01 Anna Kovacs|yes| 2|Workshop|3|2026-09-01|09:00|2026-09-03|16:00||PMO|ERP|Anna Kovacs:4|Bela Nagy:6|clde-mfa3k9x2-0-7hq4|no|Room booked~nAgenda: scope~p risks|Done|2026-08-19 Planned;2026-09-03 Done|no||no|||clde-mfa3k9x2-1-q2m8|2026-08-19T14:02:11Z clde Claude|2026-09-03T15:30:00Z ak7 Anna Kovacs|2026-08-19 Claude;2026-09-03 Anna Kovacs|no|wave-1 3|Write the spec|7|2026-09-04||2026-09-10||2|PMO|ERP|Anna Kovacs||clde-mfa3k9x2-0-7hq4|no||Planned|2026-08-19 Planned|no||yes|||clde-mfa3k9x2-2-z0p1|2026-08-19T14:02:11Z clde Claude|2026-08-19T14:02:11Z clde Claude|2026-08-19 Claude|no|wave-1;risk 4|Spec approved|0|2026-09-11||2026-09-11||3||||||yes||Planned|2026-08-19 Planned|no||no|||clde-mfa3k9x2-3-8hd3|2026-08-19T14:02:11Z clde Claude|2026-08-19T14:02:11Z clde Claude|2026-08-19 Claude|yes| 5|Christmas freeze|0|2026-12-22||2026-12-29||||||||yes||||yes|🎄|no|||clde-mfa3k9x2-4-k4n7|2026-08-19T14:02:11Z clde Claude|2026-08-19T14:02:11Z clde Claude||yes| #META|format|2 #META|writer|claude-skill #META|spec|https://gantora.net/format.md ``` Row 1 is a group (rows 2 and 3 carry its UID in their `Parent` cells); its dates, its `09:00` start hour and its status are derived from them. Row 2 shows a note with an escaped line break and pipe, people with hours, and a Done row. Row 4 is a milestone; row 5 an event — a period with an emoji, no status, no history, and `Duration` 0 like every milestone-flavoured row. --- *This format description may be used freely to read and write Gantora plans from any tool. The Gantora application itself is separately licensed.*