miniCycle miniCycle
A repeatable multi-step process tool

The .mcyc routine format

A routine is a list of steps you repeat. .mcyc is a plain-JSON file that holds one: its steps, their order, their schedules, and how it resets. One file, no server, no account. Readable in a text editor and writable by any program.

The format is open

You do not need permission, a license, or a conversation with us to use it.

Free to implement

Anyone may read, write, generate, or validate .mcyc files in their own software (commercial or not, open source or closed) without asking permission and without a fee. There is no patent claim and no trademark condition on producing or consuming the format.

The format is open. The miniCycle application is proprietary. Those are separate things, and only the first one is being granted here.

That split is deliberate. A routine you spent months refining should not be trapped in one vendor's app, including ours. If miniCycle disappeared tomorrow, a .mcyc file would still be a readable, documented description of your routine, and anything could open it.

If you build something that reads or writes .mcyc, we would genuinely like to hear about it, but that is an invitation, not a requirement.

At a glance

Extension.mcyc files. A .json extension is also accepted on import.
Media typeapplication/json
EncodingUTF-8, uncompressed. Emoji are fine and widely used in task text.
ContentsExactly one routine per file
Required fieldsname and tasks. Nothing else.
Task ceiling150 tasks. Longer files import, truncated to the first 150.
SchemaJSON Schema 2020-12. Pinned to 2.5 for CI, or current for editors.

A complete file

This is valid and complete. Save it as morning.mcyc and import it.

{
  "name": "morning_routine",
  "title": "☀️ Morning Routine",
  "autoReset": true,
  "deleteCheckedTasks": false,
  "cycleCount": 0,
  "tasks": [
    { "id": "t1", "text": "💧 Drink a glass of water", "completed": false },
    { "id": "t2", "text": "🧘 Stretch for five minutes", "completed": false },
    { "id": "t3", "text": "📓 Write down today's top task", "completed": false, "highPriority": true }
  ]
}

Strip it further if you like. This is the smallest file that imports:

{ "name": "minimal", "tasks": [] }

With a scheduled task

Set recurring and describe the schedule. Only the sub-object matching frequency is read, so you can leave the others out:

{
  "name": "weekly_shop",
  "title": "🛒 Weekly Shop",
  "autoReset": false,
  "tasks": [
    {
      "id": "t1",
      "text": "🧺 Check what's already in the pantry",
      "completed": false,
      "recurring": true,
      "recurringSettings": {
        "frequency": "weekly",
        "indefinitely": true,
        "weekly": { "days": ["Sat"] },
        "time": { "hour": 10, "minute": 0, "meridiem": "AM" }
      }
    }
  ]
}

You do not need to write a recurringTemplates map. The app derives one from every task marked recurring when the file is imported.

Field reference

Defaults below are what the importer actually applies to a file that omits the field, which is not always the same as the value you would guess.

Root object

FieldTypeDefaultNotes
namestringrequiredIdentifier for the routine. A file without it is rejected, even when title is present.
tasksarrayrequiredMust be an array; empty is valid. Order is the display order.
titlestringfalls back to nameThe display name, and where emoji belong.
autoResetbooleantrueAuto mode. See the note under surprises: omitting this does not give you a manual routine.
deleteCheckedTasksbooleanfalseTo-Do mode. Wins over autoReset when both are true.
cycleCountinteger0Times completed. Ship shared templates with 0.
themestringapp defaultVocabulary theme. An unknown value falls back rather than failing.
recurringTemplatesobjectderivedUsually omit. Derived from recurring tasks; anything you supply is merged on top.

Task object

FieldTypeDefaultNotes
textstringnoneWhat the user reads. Technically repairable, but always supply it.
idstringgeneratedMust be unique within the file. Duplicates make reordering drop a task.
completedbooleanfalse
highPrioritybooleanfalse
priorityColorstring / nullnullHex, e.g. "#dc3545". Ignored unless highPriority is true.
dueDatestring / nullnullISO 8601. Invalid values are discarded, not rejected.
remindersEnabledbooleanfalse
recurringbooleanfalseAlso changes the default for deleteWhenCompleteSettings.
recurringSettingsobjectnoneRead only when recurring is true.
deleteWhenCompleteSettingsobjectsee below{ "cycle": bool, "todo": bool }. The durable setting.
deleteWhenCompletebooleanderivedDo not author. Recomputed on every load.
schemaVersioninteger2Optional; the importer sets it.

recurringSettings

frequency selects which sub-object is consulted, and may be hourly, daily, weekly, biweekly, monthly, or yearly. Use indefinitely: true to repeat forever, or indefinitely: false with a repeatCount. Weekday names are three letters: Sun through Sat. Full details, including the monthly and specific-date forms, are in the full specification.

Modes: how a routine resets

Two booleans at the root decide what happens when everything is checked off. There is no mode field. The mode is inferred.

ModeSetBehavior
To-DodeleteCheckedTasks: trueFinished tasks are removed. Checked first, so it wins over autoReset.
AutoautoReset: trueThe routine resets itself the moment every task is done.
Manualboth falseYou decide when the routine has been completed.

Three things that surprise people

Each of these is a real behavior of the importer, not a caveat about edge cases.

1. Omitting autoReset gives you an Auto routine, not a Manual one

The importer reads this field as “true unless explicitly false.” If you leave it out, you get a routine that resets itself. Write "autoReset": false when you want the user to decide.

2. deleteWhenComplete is derived, so authoring it does nothing

The durable field is deleteWhenCompleteSettings, which holds a separate boolean per mode. The flat deleteWhenComplete is just whichever entry matches the routine's current mode, and it is recomputed every time the routine is opened.

A file that sets only "deleteWhenComplete": true will appear to import correctly and then lose the value the first time the routine loads. Set the per-mode object instead:

"deleteWhenCompleteSettings": { "cycle": true, "todo": true }

The defaults differ by task type, which is usually what you want: recurring tasks default to { "cycle": true, "todo": true }, so the occurrence is removed on reset and the schedule brings it back, while ordinary tasks default to { "cycle": false, "todo": true }, so they are unchecked and stay put.

3. Unknown fields are dropped, not preserved

The importer copies a fixed set of fields. Extra keys of your own will not survive a round trip through the app, so don't use a .mcyc file to carry metadata you need back later.

Validating a file

A machine-readable schema is published alongside this page, so you can check a file before handing it to anyone.

Both URLs below are JSON Schema, draft 2020-12, and both describe what the importer accepts: a file that validates will import. They carry identical rules today. Pick one based on whether you want it to stay still.

URLUse it when
/schema/mcyc-2.5.schema.json Pinned to format 2.5. Never changes. Reference this from CI or a build step, where a validation result that shifts under you is a broken build.
/mcyc.schema.json Current. Tracks the format as it grows, so it may gain fields. Good for editor autocomplete, where you want the newest hints.

A new format version gets a new filename, never an edit to an existing one. That is the guarantee that makes the pinned URL worth pinning, and it is why the two are served with different cache lifetimes: the versioned file is immutable, the current one stays correctable.

From the command line

# Node, using ajv (pinned: safe in CI)
npx ajv-cli validate -s mcyc-2.5.schema.json -d my-routine.mcyc --spec=draft2020

# Python, using check-jsonschema (reads the URL directly)
pipx run check-jsonschema \
  --schemafile https://minicycle.app/schema/mcyc-2.5.schema.json \
  my-routine.mcyc

In an editor

Most editors will offer completion and inline errors if you point them at the schema. In VS Code, map the extension once in your settings:

"json.schemas": [
  {
    "fileMatch": ["*.mcyc"],
    "url": "https://minicycle.app/mcyc.schema.json"
  }
]

Being strict is on you

The importer is deliberately forgiving: it repairs missing ids, coerces odd types, and discards bad dates rather than refusing the file. That is good for users and unhelpful for authors, because a sloppy file imports quietly. Validate before you publish.

Versioning and stability

Files carry a task-level schemaVersion of 2, matching the app's current 2.5 data schema. The format has been stable since then, and the importer still reads older files, including pre-2.5 layouts.

Our intent for anything built on this: existing files keep importing. New fields may be added, so treat unknown keys as ignorable rather than as errors, which is also how the app treats them.

Published schemas follow the same rule. Each format version gets its own permanent URL (/schema/mcyc-2.5.schema.json today), and a version that has shipped is never edited in place. If the format reaches 2.6, that becomes a new file and the 2.5 document stays exactly as it is, so anything pinned to it keeps validating the way it did on the day you pinned it.

The full specification carries the complete field-by-field reference, every recurring form, worked examples, security notes, and generator scripts for Node and Python. This page is the short version.