The .shira file format

A complete description of the JSON document that Shira roadmaps are saved, exported and imported as — for anyone building an integration or generating roadmaps programmatically.
Extension
.shira
Encoding
UTF-8 JSON (RFC 8259)
Media type
application/json
Root
single JSON object
Spec version
1

What a .shira file is

A .shira file is one JSON object that fully describes a roadmap: its timeline window, its display settings, its rows (categories), its activity bars and its milestone markers. There is no binary container, no compression and no wrapper envelope — the file's entire content is the object described below.

The same object is the unit of exchange everywhere in Shira. Save to file writes it, pretty-printed, to <roadmap title>.shira, and Open file accepts any file whose content parses into this shape. Everything a roadmap needs in order to be drawn is in the file; nothing is inferred from anywhere else.

It is strict JSON: no comments, no trailing commas, no NaN or Infinity, no single-quoted strings. Property order is irrelevant. Properties that are not part of the format are ignored when the file is read.

Design principle

The format stores plan data plus presentation intent — never computed geometry. Pixel positions, row heights and stacking are worked out when the roadmap is drawn, so the same file renders correctly at any zoom, in any timescale, and in every export.


The shortest valid file

Every required field, nothing else:

{
  "title": "Q3 Launch Plan",
  "description": "",
  "canvasStartDate": "2026-06-01",
  "canvasEndDate": "2026-12-31",
  "activities": [
    {
      "id": "a1b2c3d4",
      "title": "Beta programme",
      "startDate": "2026-07-01",
      "endDate": "2026-09-30",
      "line": 1
    }
  ]
}

That file opens, renders one uncategorized bar on a month timescale, and is a legitimate roadmap. Everything else on this page is optional refinement.


The root object

Field Type Presence Meaning & default
title string required Roadmap name. Shown in the canvas header and used as the export filename.
description string required Reserved for future use. Write "".
canvasStartDate string · YYYY-MM-DD required First day of the visible timeline window.
canvasEndDate string · YYYY-MM-DD required Last day of the window, inclusive. Must be on or after the start, and the window must span at least 28 days.
activities Activity[] required The activity bars. May be an empty array.
milestones Milestone[] optional Diamond markers. Default [].
categoriesConfig {title, color?}[] optional Row order. Default [].
timescale "year" | "half" | "quarter" | "month" | "week" | "day" optional Granularity of the column header and grid lines. Default "month". May be adjusted when the file is opened.
displayDensity "compact" | "condensed" | "comfortable" optional Row height preset. Default "compact".
textTruncation "clip" | "overflow" optional What a label does when it is wider than its bar: "clip" cuts it at the bar edge, "overflow" lets it spill past. Default "clip".
categoryView "show" | "hide" optional "hide" collapses the left-hand category label column, giving the grid its full width. Default "show".
todayMilestone boolean optional Draw the vertical "today" line across the canvas. Default true. Only a real boolean false disables it.

The activity object

An activity is one horizontal bar: a titled span of time on a row.

Field Type Presence Meaning
id string required Stable identity, unique across the whole file. Any unique non-empty string is valid.
title string required Label drawn on the bar.
startDate string · YYYY-MM-DD required First day of the activity.
endDate string · YYYY-MM-DD required Last day, inclusive — the bar is drawn through the end of that day.
line number required 1-based row index within its category.
category string optional Name of the row group. Omit the key entirely to place the activity in the uncategorized group.
color string · #RRGGBB optional Bar fill. Default #93C5FD.

A single-day activity

Set startDate equal to endDate and the bar occupies exactly that day. For a point in time that should read as an event rather than a span, prefer a milestone.

Activities outside the timeline window

An activity whose dates fall partly or wholly outside the canvas window is still valid and is still loaded. It renders clipped at the canvas edge, and an activity entirely outside the window is simply not visible. Narrowing the window is a view operation — it never destroys plan data.


The milestone object

A milestone is a dated diamond marker with a label — a moment, not a span.

Field Type Presence Meaning
id string required Unique identity, same rules as an activity id.
title string required Label drawn beside the marker. Must not be empty or whitespace-only — an empty title rejects the whole file.
date string · YYYY-MM-DD required The day the marker sits on. Must parse as a date, or the file is rejected.
category string optional Which category's milestone row it belongs to. Omit for the uncategorized group.

Every category gets one reserved milestone row above its activity lines. Milestones are not positioned by hand: when several fall close enough together that their labels would collide, they are packed into stacked lanes automatically. A file therefore never needs to encode vertical milestone placement, and there is no field for it.

Note

A milestone may reference a category that no activity uses. That category still gets a row — a milestone-only row is a normal, supported shape.


Categories, rows and lines

Which rows exist, and in what order

The set of rows comes from the items, not from categoriesConfig. Every distinct category value appearing on an activity or a milestone becomes a row. categoriesConfig then only decides order:

  1. Entries in categoriesConfig, in array order — but only those actually used by at least one activity or milestone. A configured category with no items is silently dropped.
  2. Then any remaining used categories that categoriesConfig did not mention, in the order they were first encountered.

So: to control row order, list every category in categoriesConfig in the order you want, and make sure each one carries at least one item. To leave ordering to the data, omit categoriesConfig entirely.

The color property on a categoriesConfig entry is accepted but is not applied to rendering today. Treat it as reserved.

The uncategorized group

An item with no category key belongs to the uncategorized group, which renders as a row with an empty label. Do not write "category": "" to express this — an empty string is a distinct, non-canonical value, and items written that way can behave inconsistently once edited. Omit the key.

line: stacking within a category

line is a 1-based integer that is scoped per category — line 1 in "Sales" and line 1 in "Marketing" are different rows. Within one category:

  • Two activities on the same line render side by side on one row. That is the point of the field: put sequential, non-overlapping work on one line to keep the roadmap compact.
  • Two activities on the same line whose date ranges overlap will be drawn on top of each other. Nothing repacks them when the file is opened — assigning lines so that same-line activities never overlap is the generator's job.
  • Line numbers should be contiguous from 1. A gap (1, 2, 4) renders as an empty row where 3 would be.
  • Never emit fractional lines such as 2.5.

Assigning lines: the greedy rule

Within each category, walk the activities and place each on the lowest-numbered line where it collides with nothing already placed. Two items collide when a.startDate ≤ b.endDate and b.startDate ≤ a.endDate. This is optimal in row count and keeps the roadmap as short as it can be.


Dates and rules

Date handling

  • Every date is a calendar day in YYYY-MM-DD form, interpreted as UTC midnight. No other format is supported — no timestamps, no offsets, no DD/MM/YYYY.
  • End dates are inclusive, both for the canvas window and for activities.
  • Because dates are UTC-anchored, a file renders identically for viewers in every timezone. Emit each date as a literal string: building it from a local-midnight date object and serialising with toISOString() shifts every date one day earlier for anyone west of UTC.

Rules that reject a file

These are checked when a file is opened. If any fails, the roadmap is refused as a whole — it is not partially loaded.

Rule Detail
Well-formed JSON A parse error is reported as an unreadable file.
canvasEndDatecanvasStartDate An inverted window is rejected.
Window spans ≥ 28 days Measured inclusively (end − start + 1 day). Shorter windows are rejected.
Every milestone has a non-empty title Whitespace-only counts as empty.
Every milestone date parses An unparseable date rejects the file.
Not validated — but still your job

Activity dates are not checked: a reversed activity (endDate before startDate) loads and renders as a zero-or-negative-width bar. Duplicate ids, overlapping same-line activities and non-contiguous line numbers are likewise accepted and simply render badly. Validate these in your generator — the checklist below covers them.

Timescale adjustment

The requested timescale is checked against the window length, because fine granularities are unreadable across long spans. Two adjustments happen silently:

Requested Condition Result
"day" window longer than 45 days becomes "month"
"week" window longer than 100 days becomes "month"
any other used as written

So a multi-year roadmap should request "month", "quarter", "half" or "year"; asking for "day" will not be honoured.


Colour

An activity's color is a hex string in #RRGGBB form. Omitting it yields the default #93C5FD. Shira ships a curated palette of fourteen mid-light swatches held in one lightness/chroma band, so bars read as a designed set rather than a random mix. Generators should draw from it, typically one hue per category.

#93C5FD
#A5B4FC
#C4B5FD
#F0ABFC
#FDA4AF
#FCA5A5
#FDBA74
#FCD34D
#BEF264
#86EFAC
#6EE7B7
#5EEAD4
#67E8F9
#CBD5E1

Bar labels are drawn in a dark ink, so very dark fills reduce legibility. Staying inside the palette avoids the problem entirely.


A complete example

Three ordered categories, activities stacked on two lines within a category, one uncategorized activity, and milestones — including one attached to a category that carries no bars of its own.

{
  "title": "New Business Development Plan 2026-2027",
  "description": "",
  "canvasStartDate": "2026-06-01",
  "canvasEndDate": "2027-12-31",
  "timescale": "month",
  "displayDensity": "compact",
  "textTruncation": "clip",
  "categoryView": "show",
  "todayMilestone": true,
  "activities": [
    {
      "id": "bd01mkta",
      "title": "Market & Competitor Analysis",
      "startDate": "2026-06-01",
      "endDate": "2026-08-31",
      "category": "Research & Strategy",
      "color": "#93C5FD",
      "line": 1
    },
    {
      "id": "bd03case",
      "title": "Business Case & Financial Model",
      "startDate": "2026-09-01",
      "endDate": "2026-11-30",
      "category": "Research & Strategy",
      "color": "#A5B4FC",
      "line": 1
    },
    {
      "id": "bd02disc",
      "title": "Customer Discovery Interviews",
      "startDate": "2026-07-01",
      "endDate": "2026-09-30",
      "category": "Research & Strategy",
      "color": "#93C5FD",
      "line": 2
    },
    {
      "id": "bd11play",
      "title": "Sales Playbook & Pricing",
      "startDate": "2027-01-01",
      "endDate": "2027-03-31",
      "category": "Sales & Revenue",
      "color": "#FDBA74",
      "line": 1
    },
    {
      "id": "bd99misc",
      "title": "Board reporting cadence",
      "startDate": "2026-06-01",
      "endDate": "2027-12-31",
      "color": "#CBD5E1",
      "line": 1
    }
  ],
  "milestones": [
    {
      "id": "ms01gono",
      "title": "Go / No-Go Decision",
      "date": "2026-11-30",
      "category": "Research & Strategy"
    },
    {
      "id": "ms03glau",
      "title": "General Availability Launch",
      "date": "2027-05-01",
      "category": "Offering & Pilots"
    }
  ],
  "categoriesConfig": [
    { "title": "Research & Strategy" },
    { "title": "Offering & Pilots" },
    { "title": "Sales & Revenue" }
  ]
}

JSON Schema

Draft 2020-12. Rules that cannot be expressed in the shape of the document — the 28-day minimum window, the timescale adjustment, per-category line packing — are listed above and in the checklist below.

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://shiraroadmap.com/schema/shira-1.json",
  "title": "Shira roadmap (.shira)",
  "type": "object",
  "required": ["title", "description", "canvasStartDate", "canvasEndDate", "activities"],
  "properties": {
    "title":           { "type": "string" },
    "description":     { "type": "string" },
    "canvasStartDate": { "$ref": "#/$defs/calendarDate" },
    "canvasEndDate":   { "$ref": "#/$defs/calendarDate" },
    "timescale": {
      "enum": ["year", "half", "quarter", "month", "week", "day"],
      "default": "month"
    },
    "displayDensity": { "enum": ["compact", "condensed", "comfortable"], "default": "compact" },
    "textTruncation": { "enum": ["clip", "overflow"], "default": "clip" },
    "categoryView":   { "enum": ["show", "hide"], "default": "show" },
    "todayMilestone": { "type": "boolean", "default": true },
    "activities":     { "type": "array", "items": { "$ref": "#/$defs/activity" } },
    "milestones":     { "type": "array", "items": { "$ref": "#/$defs/milestone" }, "default": [] },
    "categoriesConfig": {
      "type": "array",
      "items": {
        "type": "object",
        "required": ["title"],
        "properties": {
          "title": { "type": "string" },
          "color": { "$ref": "#/$defs/hexColor" }
        }
      },
      "default": []
    }
  },
  "$defs": {
    "calendarDate": { "type": "string", "pattern": "^\\d{4}-\\d{2}-\\d{2}$" },
    "hexColor":     { "type": "string", "pattern": "^#[0-9a-fA-F]{6}$" },
    "activity": {
      "type": "object",
      "required": ["id", "title", "startDate", "endDate", "line"],
      "properties": {
        "id":        { "type": "string", "minLength": 1 },
        "title":     { "type": "string" },
        "startDate": { "$ref": "#/$defs/calendarDate" },
        "endDate":   { "$ref": "#/$defs/calendarDate" },
        "line":      { "type": "integer", "minimum": 1 },
        "category":  { "type": "string", "minLength": 1 },
        "color":     { "$ref": "#/$defs/hexColor" }
      }
    },
    "milestone": {
      "type": "object",
      "required": ["id", "title", "date"],
      "properties": {
        "id":       { "type": "string", "minLength": 1 },
        "title":    { "type": "string", "minLength": 1 },
        "date":     { "$ref": "#/$defs/calendarDate" },
        "category": { "type": "string", "minLength": 1 }
      }
    }
  }
}
Why category has minLength 1

The schema forbids "category": "" on purpose. An empty string is not how the format spells "uncategorized" — omitting the key is.


Generator checklist

For code or an AI agent emitting a .shira file. Everything here is either enforced when the file is opened, or silently produces a broken-looking roadmap.

Must hold, or the file is rejected

  • The document is strict, parseable JSON — no comments, no trailing commas.
  • title, description, canvasStartDate, canvasEndDate and activities are all present.
  • canvasEndDate is on or after canvasStartDate, and the window is at least 28 days long.
  • Every milestone has a non-empty title and a parseable date.

Must hold, or the roadmap renders wrong

  • Every id is unique across all activities and milestones.
  • Every activity's endDate is on or after its startDate.
  • All dates are YYYY-MM-DD strings emitted literally — never derived from a local-timezone date object.
  • Within each category, line values run contiguously from 1, and no two activities sharing a line have overlapping date ranges.
  • No fractional line values.
  • Uncategorized items omit the category key rather than setting it to "".
  • Every categoriesConfig entry is used by at least one item, otherwise it is dropped and the ordering you intended is lost.
  • color values are #RRGGBB, ideally from the palette above.

Worth doing

  • Choose a timescale the window can actually carry: "month" or coarser beyond about three months.
  • Give the canvas window a little air on each side of the earliest and latest item.
  • Use one colour per category so the roadmap reads as grouped work.
  • List categories in categoriesConfig in a deliberate order — most strategic at the top usually reads best.
  • Prefer a milestone over a one-day activity for decision points, launches and deadlines.

This page describes the .shira format as implemented by the Shira roadmap application. Fields marked reserved are part of the stored document but carry no rendering behaviour today; they may gain meaning in a later revision, so a generator should write the documented placeholder values rather than inventing its own.

On this page