Daylight

How Daylight Works

Files, sync, grouping, and what happens when things go sideways

The File Lifecycle

Every task is a Markdown file with YAML frontmatter. Here’s what happens from creation to completion.

┌─────────────────────────────────────────────────────┐
│ CREATE │
│ New .md file with YAML frontmatter + content │
└─────────────────┬───────────────────────────────────┘
┌─────────────────────────────────────────────────────┐
│ ACTIVE │
│ Task visible in app, grouped by scheduled date │
│ Status: active │
└─────────────────┬───────────────────────────────────┘
┌───────┴───────┐
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ COMPLETED │ │ ARCHIVED │
│ status: done │ │ status: arch. │
│ Shows in │ │ Hidden from │
│ Wrapped group │ │ all views │
└─────────────────┘ └─────────────────┘

Creating a Task

When you create a task in Daylight, it writes a Markdown file:

---
title: Review pull request
status: active
created: 2026-01-28
scheduled: 2026-01-28
tags:
- work
- code-review
project: Backend API
---
Check the auth changes in PR #142.
Look for edge cases in token refresh.

The file lives in your chosen task folder. Daylight watches that folder and reloads when files change.

Editing Tasks

Changes in the app write directly to the file. Changes to the file (from any editor) appear in the app when Daylight reloads.

You can edit the YAML frontmatter in VS Code, and Daylight will reflect those changes. The file is the source of truth.

Completing Tasks

Completing a task changes status: active to status: completed. The file stays in the same folder—only the status field changes.

Completed tasks appear in the Wrapped group for the rest of the day, then hide from default views.

Archiving Tasks

Archiving sets status: archived. The file stays in place but disappears from all views. Archive is soft-delete—the file still exists, and you can restore it by changing the status back.


Smart Grouping

Daylight organizes tasks into four groups based on their relationship to today:

GroupShows tasks where…
Pastscheduled < today AND status = active
Nowscheduled = today (or no scheduled date) AND status = active
Upcomingscheduled > today AND status = active
Wrappedstatus = completed AND completed today

How It Works

  1. Daylight reads all .md files in your task folder
  2. Parses the YAML frontmatter to get scheduled and status
  3. Compares scheduled to today’s date
  4. Places each task in the appropriate group

No manual sorting. No priority algorithms. Just date math.

Tasks Without Scheduled Dates

If a task has no scheduled field, it appears in Now. The assumption: if you haven’t scheduled it, it’s available for today.

Recurring Task Instances

For recurring tasks, each instance appears in its appropriate group:

  • Past instances (not yet completed) appear in Past
  • Today’s instance appears in Now
  • Future instances appear in Upcoming

Sync

Daylight doesn’t sync—your file sync tool does. This is intentional.

The Model

┌─────────────┐ Syncthing ┌─────────────┐
│ Phone │ ←───────────────→ │ Laptop │
│ ~/Tasks/ │ │ ~/Tasks/ │
└─────────────┘ └─────────────┘

Each device has a complete copy of your tasks. Syncthing (or Dropbox, or whatever you use) handles synchronization.

Setup

  1. Install Syncthing on all your devices
  2. Create a shared folder for tasks
  3. Point Daylight to that folder on each device

That’s it. When you create or edit a task, Syncthing propagates the change to other devices.

Why BYO Sync?

  • No server to maintain — Syncthing is peer-to-peer
  • You control the sync — Choose what syncs, when, and where
  • No vendor dependency — Syncthing is open source; so is Dropbox sync, iCloud, etc.
  • Privacy by architecture — Your tasks never pass through our servers (we don’t have servers)

Conflicts

Conflicts happen when the same file is edited on two devices before they sync. Syncthing handles this by creating a conflict file.

What a Conflict Looks Like

Tasks/
├── weekly-review.md ← Current version
└── weekly-review.sync-conflict-20260128.md ← Conflicting version

Daylight sees both files as separate tasks. You’ll notice a duplicate.

Why Conflicts Happen

  1. Offline edits: You edit on your phone, your laptop is asleep, you edit on your laptop before they sync
  2. Simultaneous edits: Two devices edit the same task faster than sync can propagate
  3. Recurring tasks: Two devices complete the same instance before sync

Resolving Conflicts

  1. Find the conflict file (look for .sync-conflict- in the filename)
  2. Open both files—compare what’s different
  3. Keep the version you want, delete the conflict file
  4. Wait for sync to propagate

It’s a 30-second fix. The benefit: you always know what happened, nothing is silently lost.

Best Practices to Avoid Conflicts

  • Let Syncthing finish before switching devices
  • Complete tasks on one device (phone or laptop, not both)
  • Check sync status before editing recently-changed tasks

Recurring Tasks

Recurring tasks use the iCalendar RRULE standard—the same format your calendar uses.

How It Works

recurrence:
rule: FREQ=WEEKLY;BYDAY=FR
instances:
- date: 2026-01-24
status: completed
- date: 2026-01-31
status: active

The rule defines the pattern. The instances array tracks each occurrence with its own status.

Instance-Based Tracking

When you complete a recurring task, Daylight marks that specific instance as completed. Other instances are unaffected.

  • Complete Friday’s instance → Friday is done, next Friday still shows
  • Skip an instance → It’s marked skipped, series continues
  • Reschedule an instance → That instance moves, others stay put

Why This Matters

Most apps track recurrence as “next date”—they just bump the date forward when you complete. This causes problems:

  • Complete early? You might skip the next occurrence
  • History? Often lost or limited
  • Reschedule one? Might move the whole series

Instance-based tracking eliminates these issues.


Time Tracking

Daylight tracks time with manual entries, not timers.

How It Works

time_entries:
- date: 2026-01-28
duration: 45
notes: Initial review
- date: 2026-01-28
duration: 30
notes: Follow-up questions

Each entry has a date, duration (in minutes), and optional notes. Add entries whenever you want—during work, at the end of the day, or retrospectively.

Why No Timers?

Timers assume uninterrupted work. In reality:

  • You start a timer, get interrupted, forget to stop it
  • Timer runs for 4 hours while you did 45 minutes of actual work
  • Timer state needs to persist across app restarts and device switches

Manual entry captures what actually happened. At the end of a work session, you know how long you worked better than a timer does.

15-Minute Snapping

Daylight snaps to 15-minute increments by default. “About 45 minutes” becomes 45. This makes entry fast while maintaining useful precision.


Edge Cases

What happens when…

ScenarioBehavior
Task file is edited externallyDaylight reloads on next app focus
Task file has invalid YAMLTask doesn’t appear; fix the YAML, it comes back
Task folder is emptyEmpty state shown; create your first task
Syncthing isn’t runningEverything still works locally; sync happens when Syncthing starts
Task scheduled for past dateAppears in Past group until completed
Recurring task rescheduled to futurePast instances hide; future instances show
Large task files (>1MB)Works but slower; consider splitting notes

Data Flow Summary

Read: Folder → Parse YAML → Build groups → Render UI

Write: User action → Update YAML → Write file → Syncthing picks up change

Sync: Syncthing detects change → Propagates to other devices → Daylight reloads

The file is always the source of truth. The app is just a nice way to read and write it.