Punchlist
build notes
Back to the site

Build notes

What changed in each build, why, & how to use it. Newest entry first.

The shared-address warning is out of the way

The security notice about Punchlist sharing browser storage with other apps on the same domain is now hidden. That shared address currently hosts apps controlled by the same owner, so the recurring board-top warning and the matching notice above the GitHub sync token were more interruption than help.

This is an explicit temporary product policy. The origin detector and warning model remain in the app, dormant behind one switch, so the migration to a custom or dedicated domain can restore or retire the old-address guidance deliberately instead of rebuilding it later.

Nothing about storage or sync changed. Existing boards, images, history, exports, GitHub sync settings, offline reopening, and direct file:// use behave exactly as before. This build only removes the visible notification.

The phone build keeps the board under your fingers

This is the mobile stability batch tested on the physical phone. Completing one task after another no longer moves the next checkbox away from the place you were tapping, and revealing a task's actions no longer grows its row or steals width from deeply nested text. Every mobile task keeps a direct +; Delete appears only on the selected row, at its top-right edge.

Deleting a nested branch points at the branch you chose. Its confirmation sits directly above that exact row, follows its indentation, and leaves the rest of the tree visible. Delete and Cancel keep full touch targets. Group deletion uses the same compact treatment instead of squeezing the group title.

Focus mode fits the phone now. Long text, metadata, images, captions, and nested outlines stay inside the screen. Back and Fold all move to a bottom-left dock on small screens, above the phone's safe area, so leaving or folding the view does not require reaching back to the top.

The hamburger stays reachable while you scroll. It pins to the top-right after its original place scrolls away, then returns to that place when you reach the top. Completed and Trash also gain enough end space to clear Safari's bottom controls at maximum scroll.

Hosted and downloaded copies remain portable. The hosted service worker now asks the network for a fresh app shell before falling back to its cache, while offline reopening and direct file:// use still work. No storage key or board format changed, so existing boards, exports, history, images, and sync settings carry forward. A downloaded copy also checks the small latest.json channel rather than waiting for the next GitHub release page poll.

Mobile task actions stay out of the way

Every mobile task now keeps a direct + button at its right edge for adding a subtask. Selecting the row reveals a small close-style delete button above it, so deleting remains deliberate without taking a permanent second full-size button from the task text.

The row does not grow when the delete control appears. That keeps the checkbox directly below a selected row in the same on-screen place, while nested tasks retain their usable text width.

A downloaded copy hears about updates faster

The update check used to poll GitHub's Releases page. Releases are now cut by hand at milestones, sometimes weeks apart, so a downloaded copy could sit unnotified for 24 builds in a row while the hosted site had long since moved on.

It now checks latest.json instead, a small file the build writes fresh every time. Same silent, no-account, zero-network-otherwise check as before: only a copy opened from disk, only with the setting on, never in the demo, & it says nothing at all if it can't reach the file.

Ctrl+↑ & Ctrl+↓ reach focus mode

Ctrl+ folds the row your caret is in, Ctrl+ opens it back up. On the board that already worked, on tasks & on groups, & it kept working while you were typing inside the row. In focus mode it did nothing at all. The outline there draws the same chevrons the board does & had no key bound to them, so the pair you reach for was dead in the one view built for concentrating on a subtree.

The caret holds its row through the fold. The outline redraws when a row collapses, & landing somewhere else afterwards would cost you your place, which is the whole point of being in focus mode.

The row you focused on is left out on purpose. Focus mode draws its children whatever its own folded state says, so folding the root would change the board underneath you while nothing moved on screen. A key that quietly edits state you cannot see is worse than no key.

The chevron names its shortcut on hover now, the way the other row controls do.

Bold, italic, strikethrough & code, without the caret games

**bold**, *italic*, ~~struck out~~, backticks for code, & the underscore spellings. ***both*** nests. Ctrl+B, Ctrl+I & Ctrl+Shift+S apply them, & they write plain Markdown into the task so an export or a paste elsewhere carries the formatting with it.

The row you are typing in shows its markers. Every other row stays formatted. That is the whole design, & it is the reason this works where the first attempt did not. The markers are never removed from the text, only hidden, so clicking into a row re-renders nothing & your caret cannot end up somewhere you did not put it. While you edit, what is on screen is exactly what is stored.

Markers need a space outside them. So 2*3*4, some_file_name.txt & build_task_board stay literal, while bolding a whole sentence still works. The test is the spaces outside the asterisks, not inside.

Settings → Formatting has all three switches: the mode (raw while editing, always rendered, or never rendered), the shortcuts on or off, & the whole-words rule on or off.

Pasting formatted text keeps its formatting now. Copy a few paragraphs out of a browser, a document or a chat & the bold stays bold, the links stay links, & the line breaks stay where they were. It arrives as plain Markdown in the task rather than as foreign markup, so what lands in your board is text you could have typed yourself. One gap: a pasted table runs its cells together on one line.

Type a colon for emoji

Type : and keep typing. Arrows move, Enter or Tab inserts, Escape keeps the word literal.

It only triggers on a colon that starts a word. So 10:30 stays a time, note: something stays a note, & https:// stays a link. Press Escape & it stays out of your way while you finish that word, so :fire can just be the word fire.

About 250 emoji, weighted towards what a task board actually needs: status, work, time, feeling. Not the full Unicode set, which with searchable names would be around 200 KB, roughly half the app.

Move done tasks down is a new setting under Lifecycle, off by default. Turn it on & ticking a task sends it to the bottom of its group, under everything. One thing to know first: a task nested under another leaves that nesting to get there, & unticking does not put it back, because nothing recorded where back was.

Reporting a bug takes two fields, & both of them arrive

Summary & What happened. The summary becomes the issue title. Every issue used to open titled "Bug report", which made a list of them impossible to read at a glance, & the one line that would have said what broke was buried in the body.

Two ways out, told apart by colour. Purple opens a prefilled GitHub issue. Blue copies the author's email with everything you wrote, & it is now the big obvious button under a small line for anyone who does not have or want a GitHub account.

Your text is not thrown away. Opening the issue clears the form, because by then your words are safely in it. The email path keeps the dialog open & keeps your text, because a copy can fail silently & retyping a bug report is worse than an extra click. Closing the dialog clears it.

Screenshots. They cannot travel from the app into a prefilled issue: the URL carries text only, & GitHub will not render an embedded image in a body it was handed. So the issue asks you to attach them there, where the upload box is.

The board is harder to wipe by accident

Restore example board asks twice, & offers you a backup on the way. It used to be a full-width red button in the middle of Settings behind a single browser confirm, one slip from erasing everything. It is a small underlined line at the very bottom now, under Report a bug. Pressing it opens a dialog that counts what you are about to lose in your own numbers, puts Export JSON right there so the backup is one click, & asks again in different words before anything happens.

The sidebar rows line up. Views was a small uppercase caption with no icon while History & Help were full rows with icons, & the gaps between the three ran 10px, 12px & 22px depending where you looked. One row shape, one spacing, & Views has an icon.

The drop line during a drag is a line. It was drawn as an inset shadow, which browsers paint inside the border box, so it followed the row's rounded corners & curled up at both ends. Straight across now.

A stuck backdrop that ate every click. Open the sidebar drawer on a narrow window, then widen the window: the drawer's full-screen backdrop stayed up while the sidebar went back to being an ordinary column behind it, so the whole page dimmed slightly & swallowed every scroll & click over the sidebar. Widening the window now closes the drawer with it.

Sync knows your devices by name, & you can forget one

Enter splits a line where you put the caret. A caret sitting at the visual end of a rendered link is actually inside it, & the code that mapped the caret back to your stored text stopped counting the moment it found the caret, so the closing marker never got counted & Enter split the line in the wrong place. Bold had the same bug; pulling bold only hid it.

Naming this device is required for sync. Two names live in Settings & they answer different questions: your name travels with anything you send someone, the device name never leaves this browser & is what tells your phone from your laptop when they disagree about the board. Sync needs the second one, because a conflict dialog that says "another device" cannot be answered. A board already syncing without a name says so on open.

You can forget a device. Settings, Sync, hover a device & press the x. It goes from the list on every device at the next sync, & Ctrl+Z brings it back. One honest caveat, spelled out in the app: forgetting tidies the list, it does not revoke access. Every device shares one token, so a device still holding it rejoins the moment it syncs. The sync guide walks through rotating the token when you actually need to cut one off.

Plus three smaller ones: the keyboard walk down the sidebar no longer stalls at Settings, Enter flips a toggle instead of doing nothing, & an open section's arrow points down at its content rather than up.

Text formatting is gone, & nothing you wrote was touched to do it

Bold, italic & strikethrough are pulled. They had too many rough edges to be worth what they gave you, so **bold** shows its asterisks again.

Read this part if you used it. There were two ways to do this. One strips the markers out of everything you have stored, so your text comes out clean & the change is permanent. The other stops rendering & leaves every character exactly where you typed it. This build does the second one. Nothing was rewritten, no migration ran, & the markers are back in your text because they never left it.

That also means a Markdown export still carries them, so **bold** means bold again the moment it lands in Notion or Obsidian.

Ctrl+B, Ctrl+I & Ctrl+Shift+S went with it. They only ever typed those markers, & a shortcut that promises bold & delivers asterisks is worse than no shortcut.

Links are not formatting & stay exactly as they were: [label](url), bare URLs, & pasting a URL over selected text.

One thing this quietly fixed: pasting bold text in from a browser or a document used to add asterisks to your task, because the pasted styling was converted to markers on the way in. It arrives as plain text now.

Every button tells you its shortcut, & Settings works from the keyboard

Punchlist has always been meant for the keyboard. The problem was finding out what the keys were.

Hover a button & it names its key. The checkbox says Ctrl+Enter. A chevron names the Ctrl+arrow that matches whether it is open or closed. The plus says Enter then Tab, the bin says Backspace, the grip says Alt+arrows. Nothing new happens, these were all already there; you just had to know.

Alt+S shows & hides the sidebar. It sits next to Alt+A for a new group. On a narrow window it slides the drawer instead, same as tapping the hamburger, & it works while you are typing.

Settings browses like the board does. Arrows walked the sidebar before, but they could not open a section & they skipped straight over every dropdown & text field inside one, so the settings themselves were mouse-only in practice. Now arrows walk everything on screen, Right opens a section, Left closes it & then climbs to the section holding it, & Ctrl+arrows expand & collapse the same way they do on the board.

One deliberate choice in there: arrows walk past a dropdown rather than changing its value, the way arrows move between rows instead of editing them. Alt+Down drops it open when you do want to change it.

There is a cheatsheet now. Every shortcut on one page, grouped by what you are trying to do, with five picked out at the top. It prints. It is linked from Help inside the app too.

Sync stopped travelling back in time

If you sync across devices, this is the build to be on, & so is every other device you own.

When two devices disagreed about the board, Punchlist picked the winner by counting how many edits each one had made. Counting, not timing. So a phone sitting on fifty edits from last week outranked the laptop you touched a minute ago, pushed its old board over your fresh work, & the laptop then pulled that old board down onto itself. That is the trip back in time.

Every edit now stamps the time beside the counter, & sync reads both. When they agree it resolves silently exactly as before, so an ordinary day gets no new dialogs. When they disagree, which is precisely the case that used to lose work, it stops & asks, & the dialog names both edit dates so the choice is obvious instead of blind.

The catch worth saying plainly: this only protects you when both sides of a sync have it. A device on an older build sends no stamp, so the newer one falls back to counting, & the old device has no protection at all. Update every device, not just one.

Underscores make text italic & bold now, without wrecking your file names

Until today only asterisks worked. _like this_ just sat there as literal underscores. Now _this_ is italic, __this__ is bold, & ___this___ is both, matching the asterisk forms you already had.

The part that took the actual thought: an underscore in the middle of a word is not formatting. If it were, then build_task_board.mjs would turn into buildtaskboard.mjs the moment you typed it, & so would every snake_case name & every __init__ you ever paste in. So an underscore only starts or ends emphasis at the edge of a word. Asterisks keep working mid-word, because that is the rule everywhere else too.

One thing to expect: underscores are stored as asterisks. Type _italic_, edit the line again, & you will see *italic*. It looks exactly the same on screen. Both spellings mean one thing, & keeping track of which one you typed would buy you nothing.

Moving a task stops repainting the whole board, & bold stops running away

Four fixes, all things you feel rather than read about.

Moving an item used to redraw everything. Every row on the board was destroyed & rebuilt on each Alt+Arrow, which is why the whole list blinked. Measured on a test board: 26 rows out of 26 thrown away. It now rebuilds one, the row that moved. Your selection & your keyboard focus survive the move as well, which they did not before.

Adding a sub item used to flash the item above it. That item was the parent, & it was being rebuilt along with its child list for no reason. Adding a sub item now touches nothing that already exists on screen, it just inserts the new row. The parent keeps its chevron, its text, & your place in it.

Bold no longer sticks. Press Ctrl+B on a word & keep typing: everything after it came out bold too, because the cursor was left sitting inside the bold text. It now steps out, so what you type next is plain. Backspace & the arrow keys still land on the real character either side, no dead keystroke.

And bolding twice no longer doubles up. Bold a word, then select the whole line & bold that: the markers nested inside each other & you got stray asterisks printed in your text. Bolding a selection that is already part bold now simply makes all of it bold, & bolding it again clears it. The same goes for italic over a bold word.

Restore from history could bring back the wrong item. If you had deleted two items with the same name, every Restore button that mentioned that name pointed at the same deletion, the oldest one. So restoring the newer one quietly handed you the older item instead. Restore now matches on when the deletion actually happened, so each history row brings back its own item.

One more, quieter: on a phone, a drag that got cancelled part way could leave the row it touched faded & outlined for the rest of the session. That row now clears properly.

The status board became a tool of its own, & the app did not move

Nothing in Punchlist changed today. The built file is byte for byte the one from the July 21 build, same version, same behavior. If you use the app, there is nothing here for you & nothing to re-download. This entry is about the workbench beside it.

The status board is the local page a coding agent & I share while working on Punchlist: your task board on the left, a graph of what the agent is doing, & a chat channel. It only ever ran inside this repo, because it looked for its files next to its own scripts & served the board pane from a hardcoded path into the app folder. Now it reads whichever project you start it from, & the board pane is looked up in three places instead of one. Copy thirteen files into any project & it runs there, with that project's tasks, on its own port.

The board pane is still Punchlist. That is the whole reason this is cheap: Punchlist builds to one HTML file with no dependencies & no network calls, so a second project carries a copy of it & nothing else. A new script keeps that copy current by pulling the published build over HTTP, & it refuses to install a file that is missing anything the wrapper reaches for. The server now reports which build the pane is running every time it starts.

One real bug fell out of the survey. The graph's timeline view looked for a group by a fixed name, that group was renamed during a recent cleanup, & the view had been quietly blank ever since. It now works out which group to show from the board itself.

Sync can no longer roll your board back to an old version

A board that had not been touched in days could still overwrite your current one. When two devices had both changed, sync always let the local one win, with no check for which board was actually newer. So a device that woke up after five days, still carrying one unsynced edit, pushed its stale board over everyone else. That is what reverted a board to a five day old state.

Now every board carries a small counter that ticks up on each real edit, and sync compares those counters instead of guessing. A stale device can never overwrite a newer board, it takes the newer one instead. When two devices genuinely both moved ahead, the one with more changes wins and the other version stays in your Undo and in the repo history, and a real tie asks you which to keep. No clocks are involved, so a device with a wrong clock cannot fool it.

Also in this build: expanding or collapsing a big group no longer rebuilds the whole group, so the toggle stops lagging on large boards.

The flash is gone: edits repaint rows, not the board

The oldest open complaint here was a visible flash while editing a big board. It turned out to be the whole board repainting in one long freeze, and two habits made it worse: link badges were recounted from scratch for every row drawn, and edits to linked copies (the day-planning kind) always repainted everything.

Measured on a 1,400-task board: completing a linked task went from 650ms to 31ms, pressing Enter on a top-level row from around 300ms to 25ms, and hiding a completed task now removes just that row instead of redrawing the page. Same board, same edits, about twenty times less work.

Report a bug, calmer image settings, and focus knows where you are

Settings gained a Report a bug button. Describe what happened and one click opens a prefilled GitHub issue (your text plus the app version), or copies an email instead. No account gymnastics for testers.

The image resolution setting now explains itself: changing it never touches images already on the board, only new pastes, and it says so when you change it, on hover, and in Help.

Focus mode keeps its bearings on long lists: the top bar shows what you are focused on while you scroll, group focus shows the group's total tracked time, and the keyboard toggle (Ctrl+Alt+F) is now visible instead of secret.

The phone margin bug is dead, measured this time

The board on a phone could sit shifted, dead space on the left and the card running off the right edge. A live probe on the actual device measured the group card at 393px on a 375px screen: one CSS grid track allowed itself to grow past the viewport whenever a task held a wide pasted image. The track is clamped now, and the same board measures a clean 20px margin on both sides with images shrinking to fit.

Checkboxes also toggle reliably now. Pressing the checkbox of an unselected, half-visible row used to select the row and scroll it, which swallowed the click before it could check the box.

Sync stops eating edits and throwing you around the board

Three sync fixes in one pass. If you typed something and switched tabs fast, the incoming sync could decide your device had nothing new and pull the other version over your unsaved words; it now lands your typing first, every time. A pull used to jump the view back to the top of the board; it now keeps your place and your selection exactly where they were, and if you are mid-edit it waits until you finish before applying anything. And the "GitHub answered 409" failure now retries by itself instead of sitting there until you poked something.

Buttons stay on one line, and the phone stops drifting sideways

Two small fixes. Button labels like Collapse all no longer fold onto a second line when the sidebar is narrow. And on the phone, the page could slowly drift sideways as you scrolled (a left margin creeping in while the right edge slid off screen); the page now refuses sideways panning outright, the same guard the landing page already had.

The downloaded file can tell you when it is out of date

If you run the downloaded copy, it now checks once on load for a newer release and shows a small, dismissible note when one exists, with a link to get it and to see what changed. It never reaches out for anything else, and there is a switch in Settings to turn the check off entirely, so the file stays fully offline if you want. The hosted version is always current, so this only matters for a downloaded file.

Settings shows your current version now, and there is a short guide on updating a downloaded copy without losing your board.

Focus mode scrolls, and its header shrank

Two quick focus fixes from real use. Long lists in focus now scroll; before, anything past one screen sat stuck out of reach. And the big title at the top was cut down so the tasks lead, not the heading.

Focus mode folds now

Focus mode used to open every row flat, so a long list was a wall you had to scroll. Now each row with children has a fold arrow, and it opens in the same folded state as the main board. A Fold all button in the top bar collapses the whole thing, or opens it back up if it is already folded.

This is aimed at the big group-focus case, where dozens of items used to spill out at once.

Dead expand arrows are gone

A task could show an expand arrow that opened onto nothing. It happened once every subtask under it was finished and had aged out of the active view: the arrow still counted those hidden children, so tapping it revealed an empty gap. Now the arrow appears only when there is something to reveal.

Small fix, but a real papercut in long-lived lists where completed subtasks pile up under a parent. Your boards are otherwise untouched.

Choose how sharp images stay

Pasted images were quietly shrunk to 800px and re-saved at low quality, with the original discarded. A new setting controls how much resolution is kept, in Settings under Paste creates.

The Settings sidebar showing the new Image keep resolution dropdown set to Medium
The new setting sits in Settings, right under Paste creates.

What changed

The four levels

It applies to new pastes only, and every level except Original re-encodes to WebP. Original is the pick when the source bytes genuinely matter, not just a sharp preview.

The phone stops fighting back

A touch re-test on the new build surfaced four issues. Three are fixed.

The Punchlist board on a phone-width screen
The board at phone width, where the touch fixes land.

What changed

Still open

Holding one row longer to select it was still misbehaving in testing and is being pinned down before the gesture timing changes. 110 tests pass, one new that checks the sweep fills, reverses, and keeps earlier holds.

The landing page catches up to the app

The front page had drifted out of date and got cramped on phones. This build brings it back in line.

The Punchlist landing page hero
The landing page: current version, calmer on phones.

What changed

The page still makes zero network requests. The single-file app now also ships as a tagged GitHub release you can download, cut with one command at each milestone.

Enter at the start of a line, and a batch of small fixes

What changed

110 tests green, verified in a real browser.

The ghost that ate taps, & images move out of the board

The ghost that ate taps

A "toggles sometimes stop working" report turned out to be a ghost. When the app repaints the row under the pressing finger, the browser silently drops that touch, with no release event ever. The 1.5-second hold timer kept running, armed drag-select with no finger on the glass, and that ghost then ate the next tap's click or every scroll until reload. Reproduced both ways in a real browser, then fixed with one rule: any new press wipes every stale gesture first. A regression test now fails the build if it ever returns.

Two more touch fixes

Images move out of the board

v1.4.0 moves image bytes out of the board file. Each image becomes an immutable record in IndexedDB on the device and one file under assets/ in the sync repo; the board keeps a reference measured in bytes. Push uploads what the remote is missing before the board, pull downloads what the device is missing, every image transfers once, and both devices end up with everything offline. Two ceilings retire together: the 1MB read cap that split phone and laptop, and the browser's ~5MB board limit. Existing images migrate out on next load; exports still embed everything, so backups stay lossless and readable by older builds.

108 tests green, 5 commits, migration and rendering and lightbox and reload all verified in a real browser.

The night the agent learned to listen

The agent learned to listen

The biggest fix was not code, it was attention. Messages typed into the status chat used to sit unread until someone typed in the terminal. A file watcher now wakes the agent on every chat message and every intake press; it is part of the session-start ritual for all future agents, and the pause/stop buttons that ride on it got their first real use within the hour.

The graph, redesigned

The old stacked columns were rejected; three rounds of sketches converged on the staircase, now built: a horizontal trunk that reads as a timeline, one path of children stepping down under whichever node is selected, +N pills for collapsed siblings, blue dots on anything new since the last look, and the whole thing walkable with arrow keys.

App and chat

23 commits in one evening, 100 tests green, every interface change verified in isolated browser pages before touching the live tab.

Phone gestures: hold to move, hold longer to grab a bunch

The gesture spec, built

A tap selects. A sideways drag indents or outdents. A press held under 1.5 seconds arms move, now from anywhere on the row, not just the grip. Hold past 1.5 seconds and it flips into drag-select: the row joins, sweeping takes every row the finger crosses, and a fast sweep fills the range so it cannot skip rows. Release, hold another row past 1.5 seconds, and it joins too. A plain tap starts over.

Two fixes rode along

98 tests pass, one new for the selection logic. Evening addition: the status chat now wakes the agent, and desktop notifications got three fixes: run-complete and input-needed fire while the page is open, an unfocused tab counts as away, and a profile that never granted permission shows a "click to allow" button. The running dots stop lying too, a dead server clears them in seconds.

New tasks start empty

Focus mode showing a group's tasks as a clean checklist outline
Focus mode already created empty tasks; the board caught up.

What changed

87 tests pass, 3 new for these fixes.

The project now runs its own status board

What changed

How to use it

  1. Open http://localhost:4173/ during any Claude Code session on this project.
  2. Edit it like any board — check things off, reorder, add notes.
  3. Dump anything into Braindump at the top. Claude does not read that group until you press the Intake braindump button next to its name; then everything gets triaged into the right groups with priorities, & duplicates get checked against what's already tracked (open issue? unbuilt idea? already shipped?) & merged instead of doubled.
  4. Agent_Active shows what's being worked on right now. Add to it when you want; a legend in the header names the three backdrop colors — purple glow for the item being worked this minute, blue for agent-added rows, green for yours. Finished items fold into a collapsed Completed row so only actionable things stay visible.
  5. A purple ACTIVE bar at the very top names what the agent is working on this minute — click it & the page jumps to that item, which carries a slowly glowing purple backdrop. While a braindump is being processed, the Intake button itself glows & reads "Intaking braindump…" until the group is empty.
  6. Press Chat in the top bar to talk to the agent without a terminal. Messages travel through a local file; nothing leaves the machine.
  7. Press Graph for the execution graph: one thread from recently-completed through the glowing active item into the queue ahead (forking where work parallelizes), with named agent chips riding the items being worked. Drag to browse — it glides back to the active item after 15 seconds. The bottom edge drags to resize.

A hosted snapshot of the same board lives at the project's private artifact link; it's rebaked when the status changes. The snapshot is view-only in spirit: edits there don't reach Claude & reset on reload.

v1.3.0: named devices in history & signed exports

What changed

83 automated tests pass on this build, 6 of them new. The signing round trip also ran in a real browser: two fully separate browser profiles exchanged an export, & the second one recognized the first by key on the second import & refused a tampered copy.

How to name your devices

  1. Open Settings on each device.
  2. Fill Username once (it syncs; it names you, not the machine).
  3. Fill Device name on each device separately (it never syncs; that's the point).

Skipping the device name costs nothing but readability: unnamed devices show as device plus the last 4 characters of their id.

What an import tells you now

What the signature proves, & what it doesn't

It proves the file was produced by the holder of a specific key & untouched since, which is what "same sender as their last export" needs. It does not prove a legal identity, & the sender name is self-chosen; only the fingerprint is checked. The private key never leaves your own storage: exports carry only the public half, & the automated suite has a test whose whole job is to fail if a private key ever lands in an export. The full design record, including diagrams of every flow, is in docs/IDENTITY.md in the repository.

What's next

The funnel is declared done-for-now, & the next direction is written up in docs/DIRECTIONS.md: mobile & touch refinement driven by a real friction list, with the phone-capture loop running alongside as the test bed. The same file holds a ponytail audit of the codebase (verdict: lean; about 107 removable lines across 4 findings, zero dependencies to cut) & the skills matched to the mobile work.

v1.2.0: sync between your devices, the demo un-cropped, & an iOS storage warning

What changed

77 automated tests pass on this build, 8 of them new for sync. Every sync path (first push, edit push, pull, conflict) also ran in a real browser against a mocked GitHub API before shipping.

How to turn on sync

Sync v1 assumes you have a GitHub account. It costs nothing: a free account includes unlimited private repositories.

  1. Create a private repository to hold the board, for example punchlist-data. Empty is fine; the app creates the file itself.
  2. Create a token the board will use. On GitHub: Settings, Developer settings, Personal access tokens, Fine-grained tokens, Generate new token. Under "Repository access" pick Only select repositories & choose the repo from step 1. Under "Permissions", set Contents to Read and write. Nothing else. That token can touch that one repository & nothing more.
  3. Enter both in the app. Open Settings, expand Sync, tick GitHub sync, then fill the two fields. It should look like this:

Repeat step 3 on each device: open the same board URL (or file) there & paste the same repository & token. The first device to sync fills the repository; every later device pulls that board on its first sync.

Order matters once: do this first on the device whose board you want to keep. Its first push becomes the shared board, & the other devices adopt it. Their previous local boards are replaced (a backup lands in History, & you can export JSON first if you want belt & braces).

How sync behaves day to day

Why the iPhone popup exists

WebKit's tracking prevention wipes a site's script-writable storage after 7 days of Safari use without a visit to that site. A task board you open daily is safe; one you leave for a week on a trip isn't. Adding the board to the Home Screen exempts it, & with sync on, the GitHub copy survives regardless. The banner shows once & never returns after "Got it".