@lovek4d/kickflip 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/CONTEXT.md +414 -0
  2. package/LICENSE +57 -0
  3. package/README.md +23 -2
  4. package/SKILL.md +121 -0
  5. package/bin/doctor.mjs +3 -0
  6. package/bin/install.mjs +3 -0
  7. package/bin/kickflip.mjs +125 -0
  8. package/bin/statusline.mjs +4 -0
  9. package/bin/wait-events.mjs +3 -0
  10. package/bin/web-ts.mjs +2 -0
  11. package/board/bin/agent.mjs +2 -0
  12. package/board/bin/board.mjs +207 -0
  13. package/board/bin/builder-stop-hook.mjs +5 -0
  14. package/board/build-script.mjs +1 -0
  15. package/board/domain.mjs +3 -0
  16. package/board/public/app-asks.js +169 -0
  17. package/board/public/app-banner.js +48 -0
  18. package/board/public/app-cards.js +200 -0
  19. package/board/public/app-core.js +96 -0
  20. package/board/public/app-header.js +108 -0
  21. package/board/public/app-lanes.js +92 -0
  22. package/board/public/app-needs.js +30 -0
  23. package/board/public/app-snapshot.js +97 -0
  24. package/board/public/app-threads.js +165 -0
  25. package/board/public/app.html +83 -0
  26. package/board/public/app.js +327 -0
  27. package/board/public/attach.js +84 -0
  28. package/board/public/dialogs.js +360 -0
  29. package/board/public/hub-kit.js +151 -0
  30. package/board/public/hub.css +164 -0
  31. package/board/public/kickflip.png +0 -0
  32. package/board/public/prompts.js +64 -0
  33. package/board/public/prototype-kit.js +193 -0
  34. package/board/public/prototype-switcher.js +226 -0
  35. package/board/public/prototype.html +55 -0
  36. package/board/public/router.js +223 -0
  37. package/board/public/rules-editor.js +127 -0
  38. package/board/public/shell.js +298 -0
  39. package/board/public/sidebar.css +199 -0
  40. package/board/public/sidebar.js +287 -0
  41. package/board/public/style.css +570 -0
  42. package/board/public/toasts.js +179 -0
  43. package/board/public/ui-cards.js +153 -0
  44. package/board/public/ui-enums.js +10 -0
  45. package/board/public/ui-header.js +84 -0
  46. package/board/public/ui-hub.js +94 -0
  47. package/board/public/ui-links.js +142 -0
  48. package/board/public/ui-markdown.js +80 -0
  49. package/board/public/ui-page-header.js +63 -0
  50. package/board/public/ui-prompts.js +39 -0
  51. package/board/public/ui-questions.js +130 -0
  52. package/board/public/ui-sidebar.js +177 -0
  53. package/board/public/ui-snapshot.js +56 -0
  54. package/board/public/ui-threads.js +81 -0
  55. package/board/public/ui.js +235 -0
  56. package/board/public/view-frame.js +93 -0
  57. package/board/public/view-hub-settings.js +266 -0
  58. package/board/public/view-hub.js +99 -0
  59. package/board/public/view-notes.js +29 -0
  60. package/board/public/view-polling.js +87 -0
  61. package/board/public/view-questions.js +391 -0
  62. package/board/public/view-settings.js +328 -0
  63. package/board/server.mjs +12 -0
  64. package/board/store.mjs +7 -0
  65. package/board/tickets.mjs +3 -0
  66. package/board/worktree.mjs +14 -0
  67. package/docs/hub-session.md +11 -0
  68. package/hooks-plugin/.claude-plugin/plugin.json +5 -0
  69. package/hooks-plugin/hooks/codex-share-auto.mjs +21 -0
  70. package/hooks-plugin/hooks/hooks.json +54 -0
  71. package/hooks-plugin/hooks/usage-gate.mjs +18 -0
  72. package/hub/board-repo.mjs +1 -0
  73. package/hub/boards.mjs +4 -0
  74. package/hub/close-board.mjs +1 -0
  75. package/hub/files.mjs +1 -0
  76. package/hub/github.mjs +5 -0
  77. package/hub/home.mjs +1 -0
  78. package/hub/hub.mjs +17 -0
  79. package/hub/icon-180.png +0 -0
  80. package/hub/icon-192.png +0 -0
  81. package/hub/icon-512.png +0 -0
  82. package/hub/icons.mjs +1 -0
  83. package/hub/linear.mjs +2 -0
  84. package/hub/live.mjs +2 -0
  85. package/hub/locks.mjs +1 -0
  86. package/hub/new-board.mjs +2 -0
  87. package/hub/permission-hook.mjs +3 -0
  88. package/hub/presence.mjs +1 -0
  89. package/hub/prompts.mjs +2 -0
  90. package/hub/push.mjs +2 -0
  91. package/hub/repos.mjs +2 -0
  92. package/hub/routes.mjs +1 -0
  93. package/hub/rules.mjs +1 -0
  94. package/hub/schedule.mjs +1 -0
  95. package/hub/sessions.mjs +2 -0
  96. package/hub/shell.mjs +1 -0
  97. package/hub/stream.mjs +19 -0
  98. package/hub/sw.js +144 -0
  99. package/hub/try.mjs +3 -0
  100. package/hub/usage.mjs +3 -0
  101. package/hub/views.mjs +1 -0
  102. package/package.json +26 -4
  103. package/shared/codex.mjs +1 -0
  104. package/shared/colors.css +13 -0
  105. package/shared/repos.mjs +1 -0
  106. package/shared/service.mjs +33 -0
  107. package/shared/usage-gate.mjs +1 -0
  108. package/starter/cross-site.mjs +1 -0
  109. package/starter/public/claude.js +67 -0
  110. package/starter/public/index.html +49 -0
  111. package/starter/server.mjs +4 -0
package/CONTEXT.md ADDED
@@ -0,0 +1,414 @@
1
+ # Kickflip hub and boards
2
+
3
+ Pages on Love's tailnet that Claude sessions serve, so the owner can follow and steer their work from a phone. The hub lists the sites and sends pushes. A board is a site that shows a session's work as cards.
4
+
5
+ ## Language
6
+
7
+ ### Hub and sites
8
+
9
+ **Kickflip folder**:
10
+ The installed npm package or the owner's Git clone used through `npm link`, supplying the running app's code and linked into Claude Code's skills by install. It is separate from the live state in `~/.claude/kickflip/` ([0101](docs/adr/0101-kickflip-ships-as-a-minified-npm-package.md)). Install supplies kickflip's status line and hooks as well as the skill links, so setup is independent of the owner's dotfiles ([0080](docs/adr/0080-kickflip-installs-its-own-status-line-and-hooks.md)).
11
+ _Avoid_: live skill folder
12
+
13
+ **Doctor**:
14
+ The hub setup check, `kickflip doctor`: each prerequisite, installed piece and site is ok, fixable by kickflip, the owner's to fix with the printed command, optional (Codex installed and logged in) or a note (a down site the hub restarts). `doctor --fix` runs the same setup as first-run `install` and checks again; another tool's status line stays the owner's choice ([0085](docs/adr/0085-doctor-checks-and-fixes-a-hub.md)).
15
+ _Avoid_: status (the former CLI command)
16
+
17
+ **Board repo**:
18
+ A project's shared checkout in `<clone>/.repos/<owner>/<repo>` for a clone or `~/.claude/kickflip/repos/<owner>/<repo>` for an installed package, chosen from the GitHub list or a pasted GitHub link or `owner/name` when making a board ([0078](docs/adr/0078-a-pasted-github-link-picks-any-reachable-repo.md)). Each board keeps its own branch, and its builders work in separate worktrees based on that branch ([0073](docs/adr/0073-a-new-board-chooses-a-repo-and-branch.md)).
19
+ _Avoid_: repo folder (for the repo choice)
20
+
21
+ **Hub**:
22
+ The one server on the host that answers the tailnet address, lists every site, forwards to each, checks GitHub for the open PRs on boards' cards, and sends pushes. Changes on the hub, boards and starter sites come from the app's own pages or non-browser clients; another site's page cannot act on them ([0069](docs/adr/0069-the-hub-starts-a-new-boards-session.md)).
23
+ _Avoid_: portal, gateway
24
+
25
+ **Host**:
26
+ The machine the hub and its sessions run on, a Mac or a Linux machine. Its **hub service** keeps the hub running: a launchd login item on macOS, a systemd user unit on Linux, chosen by `shared/service.mjs` at install; install, doctor, land's restart and uninstall all go through it ([0093](docs/adr/0093-the-hub-runs-under-the-hosts-service-manager.md)).
27
+ _Avoid_: the Mac, server (for the machine)
28
+
29
+ **Sidebar**:
30
+ The list of every site beside each board and hub page: New version with Reload when the hub’s code changed since the app loaded, then Hub, then Turn on notifications when needed, then Pages (sites that aren't boards), then Boards with their status words, lane chips and waiting counts. The last row of Boards is **New board**, a + tile opening a sheet that chooses a GitHub repo from the list or a pasted GitHub link or `owner/name`, then its branch, Name, optional What it’s for and Tickets (Internal · INT by default). Start is the confirm: the hub starts the board’s session itself once the request passes its checks; a muted, non-link row reads **Cloning…**, then **Starting…** until its route joins. When claude fails to start, or after 15 minutes without a join, it reads **Didn’t start**, with Dismiss, and with the clone or session error line when there is one ([0069](docs/adr/0069-the-hub-starts-a-new-boards-session.md)). Hub carries notification problems and the tools’ 5-hour usage bars under its sub line, and a moon after its name while pushes are held or snoozed, whose tooltip says until when; the whole row opens the hub page and stays selected on the hub page and its settings modals. Boards and Pages stay apart wherever sites are listed. The header's logo toggles it at every width; it carries no count, and shows a plain accent dot while a new version waits. From 900px it is a rail, which its own logo collapses out of sight until the header's logo brings it back; narrower, it is a drawer. Its header reads Kickflip. Below 900px every page header is a bottom bar, and the drawer header sits at its foot under New board; desktop headers stay at the top ([0070](docs/adr/0070-phone-headers-sit-at-the-bottom.md)). In the **app shell** it is mounted once, beside every view and framed page, and a site's row swaps the view in place. `/` opens this device's **last board**, which the app shell stores as each board opens, never a page ([0050](docs/adr/0050-a-sidebar-of-every-board-replaces-the-hub-home.md), [0055](docs/adr/0055-one-app-for-the-whole-hub.md)).
31
+ _Avoid_: hub home, switcher, nav
32
+
33
+ **Switch mode**:
34
+ The sidebar showing each linked row's key after s is pressed twice outside a text field: the first nine boards' numbers on their tiles, and every linked row's letter from its name, in builder orange, while the Boards label reads "Boards – press a key to switch" over a bar that drains, and no row moves. The next key opens that row in place; Escape, a click or 3 seconds with no key ends it. Boards claim letters first, in the order they joined the hub, so a new board never takes another board's letter ([0056](docs/adr/0056-switch-mode-letters-come-from-names-in-join-order.md)).
35
+ _Avoid_: shortcuts, hotkeys, jump mode
36
+
37
+ **Shortcuts sheet**:
38
+ The centred Keyboard shortcuts sheet over the dimmed page that h pressed twice within a second shows or hides, outside a text field and with no modifier, wherever the sidebar runs; Escape closes it. It lists every keyboard shortcut with its keys drawn as keycaps, from the one list in `ui-sidebar.js`. A device without a fine pointer that hovers, such as a phone, never shows it, and nothing else opens it.
39
+ _Avoid_: help modal, cheat sheet
40
+
41
+ **App shell**:
42
+ The one page a browser's load of `/`, a hub page or any site's page gets, `board/public/app.html`: the sidebar, mounted once, beside one **view** at a time, and the hub's one stream, `/_hub/stream`. Following a link to a view swaps it in place, with no reload, and leaving one with an unsaved edit asks first. Every URL stays a real address. The board server answers a board's four view paths with the shell too, and the hub every load of `/_hub/`, `/_hub/settings` and `/_hub/settings/polling`, so curl and a frame get it; a frame's shell hands its URL to the app around it. While the host can't be reached, the hub's service worker opens the last shell the hub served, with its last sidebar, and each view shows its own offline state ([0055](docs/adr/0055-one-app-for-the-whole-hub.md)).
43
+ _Avoid_: SPA, wrapper, frame
44
+
45
+ **View**:
46
+ One page's content in the app shell, mounted when its URL opens and unmounted, with every listener, timer and stream it added, when another takes its place. A board is a view, and so are its Waiting on you and Notes, and the **hub page**; hub settings open over Hub and Board Settings opens over its board as modals; a board left and opened again paints from its **last visit**, live when its stream sent no change since. Any other page, another site's or a board's prototype, is a **frame view**: the page in a frame beside the board header, or its own when it draws that header, with Open alone ↗ to load it by itself.
47
+ _Avoid_: screen, tab, page (a page is its own document)
48
+
49
+ **Page tree**:
50
+ The levels back climbs in the app shell: a board, its Waiting on you, then a separately opened prototype page tied to one of its questions by `?q=`. On each, the back arrow, the browser's back and a swipe all go up one level: from a prototype to Waiting on you on its question, from there to the board, with the card still open when the owner left the board from that card's thread. However the owner arrives, by a push, a link or a reload, the levels above are written into history under the page first ([0058](docs/adr/0058-back-follows-the-page-tree.md)).
51
+ _Avoid_: nav stack, breadcrumbs
52
+
53
+ **Hub page**:
54
+ The hub's status page, `/_hub/`, which the sidebar's Hub row opens: a pause or Keep going banner at the **pause line**, a banner while pushes are held or snoozed, with End snooze on a snooze, then **usage readings**, then the **pending prompts** of sessions that own no board, each site's health with each board's hub links and watcher, each session's remembered Always allow rules read-only under its board, polling status, then the **hub settings** summary rows ([0050](docs/adr/0050-a-sidebar-of-every-board-replaces-the-hub-home.md), [0060](docs/adr/0060-background-sessions-prompts-come-to-the-phone.md)).
55
+ _Avoid_: dashboard, console, hub home
56
+
57
+ **Usage reading**:
58
+ The last reported Claude or Codex usage for its 5-hour and weekly windows, with reset times and when read. The sidebar’s Hub row shows each tool’s 5-hour bar and the hub page shows both windows with the **pause line** tick; a reading older than its window reads “last seen” with a grey striped bar, otherwise a window whose reset passed reads as reset. Claude appears after its status line reports usage. Codex's bars show only while the **Codex setting** in effect isn't Off; the hub page keeps Codex's block either way, opening with the setting's control ([0082](docs/adr/0082-the-sidebar-shows-claude-and-codex-usage-under-hub.md), [0086](docs/adr/0086-codex-share-sits-in-the-usage-card.md), [0092](docs/adr/0092-codex-share-follows-whether-codex-is-usable.md)).
59
+ _Avoid_: quota, live usage
60
+
61
+ **Pause line**:
62
+ The Usage card's “Pause work at” under Claude's bars: `pauseAt` in `~/.claude/kickflip/settings.json`, 50–100 in steps of 5, default 90 (10% buffer); 100 reads Off. Either live Claude window at or over it pauses new work until that window resets. Running steps finish; builders and new agents wait, watchers hold the hub's work events, and owner messages get a brief “paused until <time>”. Keep going lifts that window's pause until its reset; Stop restores it. Codex hands work to Opus builders at the same line, without pausing. The Hub banner and sidebar and board header status slots show the pause, never the logo. `shared/usage-gate.mjs` owns it; `kickflip usage-gate` prints it ([0094](docs/adr/0094-work-pauses-at-the-pause-line.md)).
63
+ _Avoid_: buffer threshold, quota guard
64
+
65
+ **Board registry**:
66
+ `hub/boards.mjs`, the only hub code that reads a board's `board.json` and wave files. Every hub feature that serves boards (the GitHub check, wakes, rules, the handshake, the not-listening push) takes its boards from it, so a board over the waves and a board over its own file get the same links ([0040](docs/adr/0040-one-board-registry-for-every-hub-feature.md)).
67
+ _Avoid_: board list, wave boards and file boards (as separate code paths)
68
+
69
+ **Site**:
70
+ A route on the hub (`/<route>`) served by one owning Claude session, or by none for a **plain page**.
71
+ _Avoid_: app, page (a site has several pages)
72
+
73
+ **Plain page**:
74
+ A site joined with `kickflip join <route>` and `--port` (a server its session runs, which reads Down while nothing answers) or `--dir` (files the hub serves itself), without `--session`. It has no owning session, events, watcher or Needs you badge, and shows under Pages like any site that isn't a board. `--watch --session <id>` joins a site with all of those ([0087](docs/adr/0087-a-page-joins-without-a-session.md)).
75
+ _Avoid_: minimal site, unwatched site
76
+
77
+ **Owner**:
78
+ Love, the person the sites serve. The word for the session that serves a site is **owning session**.
79
+ _Avoid_: user, admin
80
+
81
+ **Board session**:
82
+ A board's owning session as the manifest records it in the site's owner: its id, its folder (`repo`, where `claude --resume` finds it) and an optional model. `kickflip start` launches one in the background; `kickflip resume` brings it back and records the id it comes back under. A rejoin keeps the label, model and folder it leaves out.
83
+ _Avoid_: orchestrator process, agent
84
+
85
+ **Hub session**:
86
+ The dev-time session that builds the app from inside: the owning session of the **hub board**, the one board whose manifest entry has `hub: true` (`kickflip set <route> --hub`), opened in the owner's terminal by `kickflip hub`. Code finds the hub board by that flag, never by its route. The product needs only the hub server, with boards added in the app, and no feature depends on this session ([0068](docs/adr/0068-the-hub-session-is-a-dev-time-role.md)).
87
+ _Avoid_: hub server, permanent orchestrator
88
+
89
+ **Live**:
90
+ A session whose `~/.claude/sessions/<pid>.json` record names it and whose process still runs, started when the record says. A live session is never started or resumed again.
91
+
92
+ **Listening**:
93
+ A board is listening while its owner session is live and a watcher runs for it. A watcher outlives its session, so a watcher alone isn't listening. A board that isn't listening is **asleep** or deaf. The board header, the sidebar, the events link and the not-listening push read that state, and the **session wake** uses this test ([0061](docs/adr/0061-a-boards-session-wakes-when-an-event-arrives.md)).
94
+
95
+ **Asleep**:
96
+ A board whose owner session isn't live but that the hub can wake on its next event: a session and folder are recorded, and no **session wake** of this outage has failed or gone unanswered. It reads a muted Asleep in the sidebar, its events link passes, and it gets no not-listening push. A board that is neither listening nor asleep is deaf and reads Not listening in red ([0061](docs/adr/0061-a-boards-session-wakes-when-an-event-arrives.md)).
97
+ _Avoid_: idle, stopped, offline
98
+
99
+ **Session wake**:
100
+ The hub resuming a board session as soon as an event lands while that session isn't live, or, as a fallback, once its board hasn't been listening for 3 minutes with an event its session hasn't heard. Once per outage, then one push to the owner if the board still isn't listening 5 minutes later. A session that idles out with nothing waiting stays stopped ([0061](docs/adr/0061-a-boards-session-wakes-when-an-event-arrives.md)).
101
+ _Avoid_: auto-resume, respawn, wake (a daily wake is the morning `check-github`)
102
+
103
+ **Push**:
104
+ A notification from the hub, on the phone or a desktop browser, that opens a page. Each push has a kind, and `hub/push.mjs` `deliver()` refuses any other: `action` when a card's **Action needed** appears or changes, `prompt` for a **pending prompt**, `fault` for what only the owner can fix (a board not listening, a session that didn't come back, an urgent production problem) ([0006](docs/adr/0006-pushes-only-when-something-waits-on-the-owner.md)). Answers, merges, Stale and other news never push; the threads' unread marks and the board carry them. Questions, steps and prototypes go at once; drafts on Ready and Merge go when their tick ends; each arrives as its own notification, and one under a card's tag replaces the last and alerts again ([0030](docs/adr/0030-pushes-go-by-kind-and-each-arrives-on-its-own.md)). Replies don't push; a push that goes out anyway says how many came since the last one. With notifications off every push is dropped; a snooze and Off on weekends hold pushes until they end, and urgent ones go through those holds ([0032](docs/adr/0032-notifications-can-be-off-snoozed-or-off-on-weekends.md)). While the owner is **at the host**, an action push goes to the host only ([0065](docs/adr/0065-pushes-go-to-the-mac-only-while-the-owner-is-at-it.md)).
105
+ _Avoid_: alert, notification (for the act of sending)
106
+
107
+ **At the host**:
108
+ The owner's presence that keeps pushes off the phone: the host's screen is unlocked and a key or the pointer was used in the last 3 minutes, whatever app is in front (`hub/presence.mjs`). It is read from macOS `ioreg`, so on a Linux host the owner always reads away. Then an action push goes only to the desktop subscriptions, and the phone never gets it later; prompts, faults and urgent pushes still reach the phone, and with no desktop subscription every push does. Otherwise the owner is away and every push goes to every device ([0065](docs/adr/0065-pushes-go-to-the-mac-only-while-the-owner-is-at-it.md)).
109
+ _Avoid_: at the Mac, active, online, desktop app active
110
+
111
+ **Polling interval**:
112
+ The hub's one schedule, a list of windows with their minutes: on each slot it checks GitHub, wakes a board's owning session with `github-change` when a PR on its cards changed, pushes once for each board that isn't listening, then sends the queued pushes. At each daily wake time it also sends each board one `check-github {reason: "morning"}`, changed or not. With hold pushes overnight on, pushes queued in the last window wait for the first window's start ([0025](docs/adr/0025-the-hub-polls-github-and-sends-pushes-on-one-interval.md)).
113
+ _Avoid_: push schedule, send-at-once hours, cron
114
+
115
+ **Hub settings**:
116
+ The five summary rows below the **hub page**’s health and status: Notifications, Polling, Linear, Rules for every board and Closed boards. Each opens its controls in a centred modal with its own scroll area, including Polling’s schedule editor. Old Hub Settings links open the matching modal over Hub ([0075](docs/adr/0075-hub-settings-open-as-modals.md)).
117
+ _Avoid_: Hub Settings page, preferences, hub config
118
+
119
+ **Board Settings**:
120
+ The centred modal opened from a board header’s gear, at every width, which scrolls inside on a phone. It contains only that board’s settings: repo and branch (Change uses the same list and GitHub link search as New board), ticket source, auto-merge, auto-build, reviewers, rules and Close board with typed-name confirmation. Closing returns to the same board and open card without another history entry ([0074](docs/adr/0074-hub-settings-expand-in-place-and-board-settings-open-in-a-modal.md)).
121
+ _Avoid_: Settings page
122
+
123
+ **Codex setting**:
124
+ The one choice for every board, made in Codex’s block of the hub page’s Usage card ([0086](docs/adr/0086-codex-share-sits-in-the-usage-card.md)): Auto and Always on have Codex build and review, with Opus builders taking over while Codex is past the **pause line** until it resets; out of usage, Auto keeps Opus builders on and Always on pauses that work until a usage-reset check succeeds; Off means no Codex. Unset means Auto. While Codex isn't usable, installed and logged in, the setting in effect is Off whatever is stored, and the control is greyed out; running out of usage doesn't make Codex unusable ([0092](docs/adr/0092-codex-share-follows-whether-codex-is-usable.md)). A session's own `/codex-share` overrides it for that session ([0066](docs/adr/0066-one-codex-setting-for-every-board.md)).
125
+ _Avoid_: Codex rule, board Codex toggle
126
+
127
+ **Waiting count**:
128
+ The badge on a site's row in the sidebar: how many things the site's `GET /_claude/needs` lists as waiting on the owner. It is unknown, with no badge, when the site doesn't answer. The Home Screen app's badge is the sum over every site.
129
+ _Avoid_: unread count, inbox
130
+
131
+ **Status word**:
132
+ The coloured word that leads a site's sub line in the sidebar when something urgent holds, most urgent first: Paused while the **pause line** holds, otherwise Down, Not listening (a board that is neither **listening** nor **asleep**), Starting, "N need you", Working, Asleep (muted) or Building. Lane chips with the site's open-card counts follow it; a calm site shows only the chips ([0037](docs/adr/0037-the-hub-home-shows-status-not-descriptions.md), [0077](docs/adr/0077-need-you-outranks-working.md)).
133
+ _Avoid_: activity line, subtitle
134
+
135
+ ### Boards
136
+
137
+ **Board**:
138
+ A site built from the board template that shows one or more waves as cards in lanes.
139
+ _Avoid_: dashboard, kanban
140
+
141
+ **Closed board**:
142
+ A board whose session and server have stopped and whose sidebar row is gone, with its folder, cards, threads and notes kept on the host. The hub page lists it under Closed boards; Reopen restores it and resumes its recorded session. Open PRs stay on GitHub, and running builder records end with a note ([0072](docs/adr/0072-a-closed-board-keeps-its-folder-and-can-be-reopened.md)).
143
+ _Avoid_: deleted board, archived board
144
+
145
+ **Hub links**:
146
+ What the hub owes a board, each passing or broken: GitHub polling, events (the board is **listening**), daily wakes, pushes (a device is subscribed) and reviewer links. The hub link (the hub runs the code in its files) is the hub's own, shown in amber only on the sidebar's Hub row and the hub page's Health card ([0064](docs/adr/0064-every-builder-works-in-a-worktree.md)). The In Review header's GitHub mark opens them as a sheet, red-dotted only while one stays broken past its grace period; until then it is **settling** ([0046](docs/adr/0046-hub-links-turn-red-only-after-a-grace-period.md)). A new board tests them with a **handshake**: a `hello` event the session answers with `board.mjs hello-ack`, with a Connecting checklist on the board until the session answered and GitHub checks reach it ([0040](docs/adr/0040-one-board-registry-for-every-hub-feature.md)).
147
+ _Avoid_: health, status, integrations
148
+
149
+ **Change signal**:
150
+ The board server's note on its `api/stream` that a file the board reads changed; the hub passes it on as `change {route}` on its one stream, and an open board reloads at once. The header's time counts from the last one that brought something new, or the last ↻ tap ([0031](docs/adr/0031-the-board-reloads-on-a-change-signal.md)).
151
+ _Avoid_: push (a push is a notification from the hub), live update
152
+
153
+ **Wave**:
154
+ A set of work items built and reviewed together, with its reviewers and follow-ups.
155
+ _Avoid_: batch, sprint, epic
156
+
157
+ **Reviewers**:
158
+ The people who get a board's PR links once they are ready, and the weekday digest. Each board keeps its own in `board.json`, which its Settings modal lists, and a new wave copies them into its own `reviewers`.
159
+ _Avoid_: approvers, watchers
160
+
161
+ **Finished wave**:
162
+ A wave with work items that are all merged, done or dropped. Done shows it as one row that opens to its cards, and it leaves the wave filter ([ADR 0012](docs/adr/0012-lists-never-cut-silently.md)).
163
+ _Avoid_: closed wave, archived wave
164
+
165
+ **Work item**:
166
+ One unit of work in a wave, usually one PR. Its key is `<wave>/<id>`.
167
+ In Todo, including while held, a card with a known creation time shows a muted "N days ago" once it is two full days old, counting whole days since creation.
168
+ _Avoid_: task (a **new task** is the owner's), ticket (the identity of its work in the board’s ticket source)
169
+
170
+ **Ticket**:
171
+ The identity of a work item or follow-up in its board’s **ticket source**, shown as a badge that opens the internal card, GitHub issue or Linear issue. A ticket id belongs to its source: one stored under another source shows as its own id, unlinked. An ask has no ticket; filing it as work creates or links one ([0081](docs/adr/0081-a-board-has-one-ticket-source.md)). A card moves its ticket one way, building to In Progress, a PR to In Review and done to Done, when the owner is assigned it or kickflip created it; a ticket never moves its card ([0088](docs/adr/0088-kickflip-reaches-linear-with-a-key-on-the-mac.md)).
172
+ _Avoid_: card (the board’s work item), task
173
+
174
+ **Ticket source**:
175
+ The board’s one choice for every card’s tickets: Internal with a prefix (INT by default), GitHub issues in the board’s repo, or a Linear workspace, team and project, listed through Hub’s **Linear key** when one is set and typed otherwise. New board and Board Settings share the choice ([0081](docs/adr/0081-a-board-has-one-ticket-source.md)).
176
+ _Avoid_: integration, tracker, GitHub project
177
+
178
+ **Linear key**:
179
+ The owner’s Linear personal API key, pasted in Hub settings and kept only on the host; pages learn only whether it is set and its workspace. kickflip lists teams and projects, creates projects and issues, and moves tickets with it. The key is optional: without it, `board.mjs` prints `ticket` lines and the board’s session does that work through its Linear MCP ([0088](docs/adr/0088-kickflip-reaches-linear-with-a-key-on-the-mac.md)).
180
+ _Avoid_: Linear token, API secret
181
+
182
+ **Internal item**:
183
+ A work item about the sites themselves rather than the product. Its legacy ref `S<n>` reads `INT-<n>`, or the board’s internal prefix when set.
184
+ _Avoid_: meta task, chore
185
+
186
+ **Badge**:
187
+ A chip on a card or page that shows a status, such as Action needed, Install, a PR's state or "held". It may link to what it is about: on a closed card, Action needed and Install go straight to the thing to act on (owner, question `badge-tap`). A badge never acts on the board; a **button** does.
188
+ _Avoid_: chip (in what the owner reads), tag, label
189
+
190
+ **Button**:
191
+ A control that acts: it sends something to the board, such as Merge, Hold or Grill, or opens a place to act, such as Open questions. The open card's workflow row holds a follow-up's Build, Merge, Hold or Resume, then the **action button**, below the details list whose final line holds Notes, Install and Review on GitHub links in that order ([0067](docs/adr/0067-a-cards-external-links-share-the-last-details-line.md)). A card that can be bumped carries its bump as an icon at its top right instead, closed or open, as a New card does; it confirms like a button ([0019](docs/adr/0019-a-card-that-hasnt-started-can-be-bumped.md)). The control that clears the card's **Action needed** is orange.
192
+ _Avoid_: badge, pill (a pill is only a look)
193
+
194
+ **Badge tone**:
195
+ The color of a badge (a status chip on a card or page), picked from one fixed set by what the badge says, through `BoardUI.badge` in `board/public/ui.js`: quiet (gray), normal (blue), notice (orange), danger (red) or ready (green). Each tone takes its color from a **status color**, so a badge is one consumer of the colors, not their source. No badge has a look of its own, so the same tone looks the same everywhere. Buttons and links styled like chips, such as quick replies, a follow-up's Build and "Review on GitHub →", aren't badges; the orange of the button that clears Action needed is the same notice color.
196
+ _Avoid_: chip color, badge style, variant
197
+
198
+ **Status color**:
199
+ A color named by what it means, `--color-quiet`, `--color-info`, `--color-notice`, `--color-danger` or `--color-success`, in `shared/colors.css` beside the base palette, in light and dark. That file is the one set of colors for the hub and every board; no page's CSS defines a color of its own ([0033](docs/adr/0033-one-colour-constants-file-shared-by-the-hub-and-every-board.md)). Which one a meaning takes follows the **colour rule**; done or passing is success.
200
+ _Avoid_: tone token, theme color, badge color
201
+
202
+ **Colour rule**:
203
+ A colour says what waits: blue (info) only for review and notice, something for the human to read or see, such as an unread review or a new version to reload; orange (notice) for attention, an agent or builder waiting on something, such as Action needed, a permission or a pick, or working; red (danger) for danger, such as failed, offline or spending; grey (quiet) for idle, which takes no other colour. Pick a new colour by it ([0096](docs/adr/0096-colour-says-whos-waiting.md)).
204
+ _Avoid_: warning colour (for attention), action colour (for blue)
205
+
206
+ **Follow-up**:
207
+ Work discovered while building a wave, with a ticket from the board’s source. It is a Todo card of that wave, with a Follow-up badge, until it is done, and an open one keeps its wave unfinished ([0034](docs/adr/0034-follow-ups-are-todo-cards-of-their-wave.md)).
208
+ _Avoid_: backlog item, todo
209
+
210
+ **Lane**:
211
+ Where a card sits (In Review, Stale, In Progress, Todo, Done), set by who acts next ([0045](docs/adr/0045-the-lane-follows-who-acts-next.md)). In Progress means Claude or a builder is working: a running builder, a **checking** item, or a PR whose CI is red or, on a draft, still running. Building and fixing count only while a builder record on that card is open; otherwise its PR and other state place it as if that status were absent. In Review means finished work waits on a person: the reviewers, or the owner's Ready, Merge, manual steps or an ask's Done (see **Action needed**). A card waiting only on those moves there from In Progress; one Claude still works on stays in In Progress. Todo holds work not yet started: the New cards, which wait on Claude ([0047](docs/adr/0047-the-plus-makes-a-new-card.md)), and any card that hasn't started and waits only on its open questions or a prototype pick, with Action needed. Answering them moves it to In Progress as its work starts; a started card that gets a question keeps its lane ([0059](docs/adr/0059-an-unstarted-card-waits-on-its-questions-in-todo.md)).
212
+ _Avoid_: column, status (status is the item's own field)
213
+
214
+ **Checking**:
215
+ The item status a builder sets when it hands back finished work. The card sits in In Progress reading "Orchestrator review" while the owning session verifies it, then the session sets it done. Only done, merged and dropped unblock dependents ([0022](docs/adr/0022-site-cards-build-in-parallel-when-their-files-dont-overlap.md)).
216
+ _Avoid_: verifying, in QA, review (review is the reviewers' part)
217
+
218
+ **Stale**:
219
+ A work item that has waited in review more than seven days since its first review link went out. Its card moves to the Stale lane ([0044](docs/adr/0044-a-pr-in-review-shows-its-age-and-goes-stale-after-seven-days.md)).
220
+ _Avoid_: old, overdue
221
+
222
+ **Next step**:
223
+ Claude's next step on a card and what sets it off, such as "Next: merge when CI is green", kept in the item's `next` and cleared once done. A card waiting on its reviewers reads its next step from GitHub instead ([0002](docs/adr/0002-a-card-derives-its-lane-from-github.md)), and one waiting on the owner reads its action.
224
+ _Avoid_: plan, notes (**Notes** is the card's write-up), todo
225
+
226
+ **Waits on**:
227
+ Who the card is waiting for: the owning session's check, the owner's Ready, the owner's other action, the builder, the reviewers, the merge, or nobody.
228
+ _Avoid_: blocked by, assignee
229
+
230
+ **Action needed**:
231
+ The notice-tone badge on a card waiting on the owner, in any lane: an open question or a prototype pick, a green draft's Ready, an ask's Done, manual steps on an approved PR, or, with **auto-merge** off, an approved PR's Merge. It links to the thing to act on. Reviewer waits and held cards get none ([0045](docs/adr/0045-the-lane-follows-who-acts-next.md)). An unstarted card with open questions carries it in Todo ([0059](docs/adr/0059-an-unstarted-card-waits-on-its-questions-in-todo.md)).
232
+ _Avoid_: open items, needs you (Needs you is the box at the top of the board)
233
+
234
+ **Action button**:
235
+ The open card's one workflow button, which `board/domain.mjs` picks as the card's `actionButton`: **Open questions** (`openQuestions`) while it has open questions or a prototype pick, opening the first; **Mark as done** (`markDone`) on an ask waiting only on the owner's Done, sending the same `ask-done` as the thread's Done; otherwise **Grill** (`grill`), which reads "Writing questions…" while a grill waits on Claude. Open questions win over Mark as done. A Done card, and an ask with neither, has none ([0059](docs/adr/0059-an-unstarted-card-waits-on-its-questions-in-todo.md)).
236
+ While the card carries **Action needed**, the button that clears it is orange, in the notice color: the card's `resolvedBy` from `board/domain.mjs`, by the action's kind. A question or a pick makes Open questions orange, an ask's Done makes Mark as done and the thread's Done orange, a green draft's Ready or manual steps make Review on GitHub orange, and an approved PR's Merge makes Merge orange. A held card's Open questions, and Grill, stay plain. A follow-up's Build is always orange.
237
+ The button names (`BUTTON`: the action buttons, `github` and `merge`) and the action kinds (`ACTION_KIND`) are two frozen enums in `board/public/ui-enums.js`, which the page loads as a script and `board/domain.mjs` runs in a vm context; code compares against their members, never a bare string.
238
+ _Avoid_: Open items, Action needed (the badge)
239
+
240
+ **Auto-merge**:
241
+ A board setting, `auto_merge` in its `board.json`, off unless set: on, monitor merges a PR once it is approved, CI is green and its merge state is CLEAN; off, the card shows the owner Action needed and a Merge button, which reads "Merging…" until the PR merges or Claude says in the thread why it didn't. The board's Settings modal toggles it, and the watcher prints it with every wake as `setting auto-merge on|off`. A setting, not a **rule**, since the board itself acts on it ([0052](docs/adr/0052-auto-merge-is-a-board-setting-off-by-default.md)).
242
+ _Avoid_: merge rule, merge policy
243
+
244
+ **Review on GitHub**:
245
+ The open card's link to its PR, on every card with an open PR, last on the details list's final line after Notes and Install when present. It reads "Review draft on GitHub ↗" on a draft and "Review on GitHub ↗" once ready, with no PR number, and is orange while a green draft's Ready or manual steps wait on the owner ([0067](docs/adr/0067-a-cards-external-links-share-the-last-details-line.md)).
246
+ _Avoid_: Review #<n> on GitHub, PR link
247
+
248
+ **Install**:
249
+ The app card's one badge that opens its build on the owner's phone, or the badge on main's slim bar above the lanes. A ready card reads "Install ↗", or "Install (stale) ↗" when its build's sha differs from the current branch head. While a build runs it reads "Building…" with an inert spinner; a failed build reads "Build failed ↗" and opens its thread. The open card's badge sits between Notes and Review on GitHub. The rightmost workflow button starts a Build, or Retry after failure. A Done card has no try badge ([0099](docs/adr/0099-a-boards-build-script-serves-or-builds-each-card-and-main.md)).
250
+ _Avoid_: download, TestFlight (a different channel), older build
251
+
252
+ **Build script**:
253
+ The repo's reviewed, executable `.kickflip/build`, run by the hub from each card's worktree and main's hub-owned detached worktree. The hub passes the key (or `main`) and worktree, plus `KICKFLIP_BOARD` and `KICKFLIP_BASE`. Printing `port <n>` and staying alive serves a website; exiting 0 without a port finishes an app build that publishes an install through the board CLI. The last finished run decides the board's kind. The hub writes try state and logs; failed card runs append one thread line, and successful runs none. There is no script path setting ([0099](docs/adr/0099-a-boards-build-script-serves-or-builds-each-card-and-main.md)).
254
+ _Avoid_: install script, session install runner
255
+
256
+ **Serve**:
257
+ The rightmost workflow button for a website work card with a build script. It confirms before the hub runs the script. Its one badge reads "Serving…", "Visit ↗", "Visit (stale) ↗" or "Serve failed ↗". A served card lives at `/<board>/try/<id>/`, stops after 30 minutes with no visit and can be served again. Main lives at `/<board>/try/main/` and never idles out. Both paths open the server itself, outside the app shell.
258
+ _Avoid_: preview site, separate page
259
+
260
+ **Auto-build**:
261
+ The board's `auto_build` setting, off by default. On, each card with commits beyond main builds or serves after each new head; off, cards wait for the owner's Serve or Build. Main always builds after every merge. The hub runs a nonoverlapping tick every 15 seconds and fetches other boards' origin branches on each GitHub polling slot. The Settings toggle saves immediately; `settings set auto-build on|off` sets it through the CLI.
262
+ _Avoid_: auto-serve, build rule
263
+
264
+ **Notes**:
265
+ The owning session's understanding of a work item so far: what was asked, what is settled, what is open. A Markdown file in its wave's folder that the item names in `plan` (`item set --notes-file`), rewritten as the grill settles things, or with no file the item's short `notes` text. "Notes ↗" opens them as a page sized for the phone, first on the open card's final details line before Install and Review on GitHub; the board keeps no summary of them and a card with no notes omits the link. The thread keeps the short news ([0053](docs/adr/0053-notes-replace-the-full-plan-and-cards-start-on-their-own.md), [0062](docs/adr/0062-notes-is-a-link-in-the-details-list.md), [0067](docs/adr/0067-a-cards-external-links-share-the-last-details-line.md)).
266
+ _Avoid_: full plan, plan, spec
267
+
268
+ **Held**:
269
+ A work card the owner paused with its Hold button. It doesn't start, and a running one gets no new work, until the owner taps Resume ([0023](docs/adr/0023-a-card-can-be-held-and-every-card-button-confirms.md)). Every action-row button asks to confirm first, through `B.confirm` (`dialogs.js`).
270
+ _Avoid_: paused, frozen, on hold (as a status name)
271
+
272
+ **Builder**:
273
+ An agent (Opus, Codex, Sonnet…) working on a work item, with one builder record each (`board.mjs builder … --agent <name>`; the first is `main`). The record holds its label, kind, start, ETA and finish, not its progress. Each progress line (`builder progress`, which needs `--site`) goes only to the card's thread, as a **status line** ("Codex: Running the benchmark"; the main agent's has no prefix), shown as one line with no bubble. The card's status line under its title is the thread's newest status line since its earliest running agent started, with its age, behind a pulsing dot as Orchestrator review and Working on it have, and an optional "ETA about 4:30 pm" line under it (owner ask on INT-76). In the thread every status line stays orange, dot included, whether its agent is working or done (owner, card INT-77). `builder done` marks the record done and keeps it; closing the card’s last open builder record clears its building or fixing status. Linking a PR clears either status too. The thread shows each record as **agent lines** among its messages, with no bubble: "Opus builder started" at its start, always orange and with its ETA while the agent runs, and "Opus builder finished" at its finish, the only green one (owner, question `int42-agents-look`, option C; owner ask on INT-63). The session's own thread summary closes the work.
274
+ _Avoid_: worker, bot
275
+
276
+ **Stalled**:
277
+ A builder still recorded as running whose last progress, or start before its first progress, was at least 30 minutes ago. Its card carries a red Stalled button until fresh progress or completion; silence calls for a check, without establishing that the process has failed.
278
+ _Avoid_: stuck, hung, failed
279
+
280
+ **Worktree**:
281
+ The git worktree a builder works in, never the folder the session, a running app or the hub uses. A board's is `~/.claude/kickflip/worktrees/<card id>` on branch `<card id>` from its chosen repo branch, or the running kickflip clone's main for the hub board or a board without a GitHub repo (`hub/board-repo.mjs` decides which), made by `board.mjs builder start --worktree` and kept on the builder record; the owning session checks and commits there, then **lands** it (`board.mjs land`): rebased onto main, tested, fast-forwarded into the live folder's main, the hub or board servers restarted, the worktree and branch removed. A repo card's comes from wave-build ([0064](docs/adr/0064-every-builder-works-in-a-worktree.md)).
282
+ _Avoid_: copy, checkout, live folder (for the builder's)
283
+
284
+ ### Talking with the owner
285
+
286
+ **New task**:
287
+ A free-form request or question the owner sends from the board's + button (or by pressing n twice). Code and events call it an **ask**: an `ask` event whose thread is `ask:<event id>`. It is a **New card** at the top of Todo, with no wave and no Action needed, until Claude sorts it one of three ways ([0047](docs/adr/0047-the-plus-makes-a-new-card.md)): files it under a wave as a work item, whose thread opens with it (`item set --from-ask`); folds it into the card it's about, whose thread then holds the message marked Moved (`item relate-ask`, undone by `ask unfold`); or answers it, which makes it a Done card reading Task, under Done's Answered row, whose thread takes the owner's later replies. An answer that leaves a question open on it keeps it in Todo as a Task with Action needed, never in Done, until the owner answers and Claude replies, or it is closed ([0059](docs/adr/0059-an-unstarted-card-waits-on-its-questions-in-todo.md)). When another board fits it better, the owner can have it **moved there** (`ask move`): it becomes a New card on that board reading "Moved from <this board>", and here a Done card whose thread links to it. Folding is within one board; a move is between boards. An answer that waits on the owner, such as a choice only the owner can settle, keeps it in In Review with Action needed until the owner taps Mark as done or the Done in its thread ([0035](docs/adr/0035-an-ask-can-wait-in-progress-until-the-owner-marks-it-done.md)).
288
+ _Avoid_: ask (in what the owner reads), message, prompt, Waiting for Claude strip
289
+
290
+ **Thread**:
291
+ The conversation on one work item: Claude's reports, builders' status lines, the owner's messages, mirrored GitHub comments, and answers to the item's questions. On a card not in Done, its reply form has a Done, left of Send, that closes the card ([0035](docs/adr/0035-an-ask-can-wait-in-progress-until-the-owner-marks-it-done.md)).
292
+ An open card shows its thread's newest 3 messages as it opens, or from its oldest **unread message** when more than 3 are unread, unread as of when the card opened. Messages that arrive while it is open add below, hiding none. Messages are the owner's, Claude's (replies included) and other people's; builder, status and ask-link lines show among the messages they sit between and don't count. "Show more · N previous messages" at the top adds the next 3 older messages each tap and goes when nothing older is left. The expansion lasts while the card stays open, through repaints and new messages, and closing the card puts it back to 3. A message being edited, or waiting on Claude, always shows, with every line after it.
293
+ _Avoid_: comments, chat
294
+
295
+ **Unread message**:
296
+ A line in a card's thread written by someone other than the owner, apart from builder status lines, newer than when this browser last opened that thread. The card shows an accent "N new" chip first in its chip row, before Action needed, until it is opened.
297
+ _Avoid_: inbox, notification. "New" names it only in the chip's "N new"; a New card is an unsorted ask.
298
+
299
+ **Last visit**:
300
+ The board and threads this device last showed, kept in its browser storage. The next visit paints it dimmed at first paint, readable at any age, with the controls that send held until the fresh board lands and replaces it in place, flashing a fading orange border round each card or line that changed ([0028](docs/adr/0028-the-board-opens-on-the-last-visit.md)). A board the app shell left fresh and comes back to with no change since paints it live instead ([0055](docs/adr/0055-one-app-for-the-whole-hub.md)). In code it is the snapshot.
301
+ _Avoid_: cache, offline copy, stale board
302
+
303
+ **Answer banner**:
304
+ The one line at the top of a board, above Needs you, naming new tasks Claude answered without making work whose answer this browser hasn't seen: "1 answer · <ask>" or "2 answers · <newest> and 1 more". A tap opens the newest one's card; ✕ hides it on this device until a newer answer. No push goes for an answer ([0006](docs/adr/0006-pushes-only-when-something-waits-on-the-owner.md)).
305
+ _Avoid_: notification, toast
306
+
307
+ **Owner question**:
308
+ A multiple-choice question Claude needs the owner to decide, attached to a topic. A builder's judgement call becomes one. One marked **urgent** blocks a build or a merge and shows alone; Claude resolves a question in the step that acts on its answer ([0043](docs/adr/0043-needs-you-gathers-what-waits-and-questions-answer-on-one-page.md)), and closing a card resolves each of its questions that has an answer; one with no answer yet stops the close unless forced. Adding a question on the same card or setting its notes file resolves answered questions still waiting on Claude; answering or resolving a question drops its queued `q-<id>` push for this site.
309
+ _Avoid_: poll, decision card
310
+
311
+ **Topic**:
312
+ The group an owner question belongs to; an item's questions use the topic `item:<wave>/<id>`. A Done card's topic with no open question is resolved, so it never waits on Claude.
313
+ _Avoid_: category, section
314
+
315
+ **Needs you**:
316
+ The box at the top of a board listing what waits on the owner, one line per work item, led by the board session's **pending prompts** ([0043](docs/adr/0043-needs-you-gathers-what-waits-and-questions-answer-on-one-page.md)).
317
+ _Avoid_: inbox, digest
318
+
319
+ **Pending prompt**:
320
+ A permission prompt or `AskUserQuestion` from a background board session, which its hook hands to the hub and holds the session on until the owner answers in the board's Needs you, or the hub page for a session that owns no board: Allow once, Always allow or Deny, or the question's options. Always allow returns the session rule to Claude Code and keeps that same rule in the hub's `prompts.json`, keyed by session id. The hub remembers rules only with a reliable process start time and binds board rules to the current ownership's `owner.since`; ownership commands clear remembered rules and pending prompts for both the old and new sessions, including boardless entries, even while the hub is stopped. A later exact match on its command, absolute path, domain or bare tool name is allowed immediately, with no push or Needs you line; prefixes and globs never widen a match, and `AskUserQuestion` always asks. Rules end when the session stops or its board's owning session changes, and the hub page lists them read-only under the board. With no answer in 10 minutes the call is denied with a note to ask on the card instead ([0060](docs/adr/0060-background-sessions-prompts-come-to-the-phone.md)).
321
+ _Avoid_: permission request, approval, dialog
322
+
323
+ **Event**:
324
+ A signal that wakes the owning session: something the owner did on a page (an ask, an answer, a button), or a board change such as `card-unblocked {key}` when a waiting card’s last unfinished dependency finishes ([0020](docs/adr/0020-cards-wait-on-explicit-blocked-by-links.md)).
325
+ _Avoid_: action, request
326
+
327
+ **Rule**:
328
+ A standing instruction from the owner to the sessions that own boards, one line of text kept on Settings. Hub rules apply to every board; each board adds its own on top. The watcher prints both with every wake, and a save wakes each session to say what changes. One Claude codified on the owner's word reads "Added by Claude" ([0039](docs/adr/0039-rules-live-on-settings-at-hub-and-board-level.md)).
329
+ _Avoid_: preference, setting, policy
330
+
331
+ **Reply**:
332
+ Claude's answer to one event, shown under it on the page.
333
+ _Avoid_: response, comment
334
+
335
+ **Pending message**:
336
+ A New card's message, or the owner's message in a thread, that Claude hasn't answered yet: no reply, and in a thread no later Claude line other than a builder status line or agent line. It reads "Working on it" once the session marks it started, and the owner can edit or delete it.
337
+ _Avoid_: unread, queued
338
+
339
+ **Prototype**:
340
+ A page that shows each option of one owner question as it would ship, built from the board's real components fed fake data. An agent records it with `board.mjs prototype add <topic>` in the site's `data/prototypes.json`, with its title, card and takes, and writes only the take files; the board serves `prototype-<topic>.html` from its one template, `board/public/prototype.html`. `question add` accepts a prototype url only for a recorded topic, with option ids a non-empty subset of its take keys in order, without repeats, and every take file written ([0089](docs/adr/0089-the-board-serves-every-prototype-from-one-template.md)).
341
+ _Avoid_: mockup, demo page
342
+
343
+ **Confirm round**:
344
+ One more prototype of the final version, with its question, after the owner answers a prototype question with a note that changes the take. A card builds only once an answer to its prototype questions comes back with no note, or the note itself says go and the session quotes it with `item set --confirmed` ([0091](docs/adr/0091-a-prototype-answer-with-a-note-gets-a-confirm-round.md)).
345
+ _Avoid_: re-grill, final check
346
+
347
+ **Prototype request**:
348
+ The owner asking, from a card's thread, for throwaway prototype pages to compare options. Each prototype comes with an open question on its card, and the page's switcher can send the pick; until the owner picks, the card carries Action needed and keeps its lane, Todo while it hasn't started ([0015](docs/adr/0015-prototypes-come-on-request.md), [0059](docs/adr/0059-an-unstarted-card-waits-on-its-questions-in-todo.md)).
349
+ _Avoid_: mockup request, preview
350
+
351
+ **Take**:
352
+ One option of an owner question shown as what it would be: a framed prototype, or a diagram of boxes and arrows with what it means in practice. A prototype's take is a key (one capital letter), a label and a file, `public/prototypes/<topic>/<KEY>.js`, whose `render(stage, kit)` draws it into the page's stage. Waiting on you keeps takes in option order with the recommended one tagged: small takes share a grid, while a question with a single take, or with any scrolling or full-screen prototype, shows a choice row above one full-width take, which a choice switches in place, followed by the note and Send ([0048](docs/adr/0048-a-questions-takes-show-inline.md), [0089](docs/adr/0089-the-board-serves-every-prototype-from-one-template.md)).
353
+ _Avoid_: variant (the switcher's URL parameter, not the word), mockup
354
+
355
+ **Kit**:
356
+ What a prototype's take gets as `render`'s second argument, from `board/public/prototype-kit.js`: the real sidebar, Hub page, cards and a sheet drawn in the stage's flow, fake data, a toast and `kit.on`. Inside the stage a link or form only toasts, and the listeners and timers a take adds end when another take shows ([0089](docs/adr/0089-the-board-serves-every-prototype-from-one-template.md)).
357
+ _Avoid_: harness, helpers
358
+
359
+ ## Board page files
360
+
361
+ The board page's scripts in `board/public/` are plain scripts with no bundler, loaded in the order `app.html`, the app shell, lists them, from `/_hub/app/`. The `ui*.js` files hold pure renderers on the `BoardUI` global, which `ui.test.mjs` tests. `app.js` defines `BoardView`, and each `app-*.js` file after it registers one part of the board view with `BoardView.part((B) => …)`. `BoardView.create(root, site, opts)` gives each board its own `B` (state, `ClaudeSite.site`, store, and a `$` scoped to the view's root), and a part adds listeners, intervals, timers, watches and unmount steps only through `B.on`, `B.every`, `B.later`, `B.watch` and `B.atUnmount`, so `unmount()` leaves none behind; `view.test.mjs` checks that. Nothing waits while the owner types: a repaint patches the lanes and each thread in place, and a box being typed in keeps its text, cursor and focus ([0057](docs/adr/0057-a-repaint-patches-in-place-so-nothing-waits-while-you-type.md)). An open card moves to its new place at once and the page follows it: the box being typed in, else the open card's title row, stays where it was on screen ([0063](docs/adr/0063-an-open-card-moves-at-once-and-the-page-follows-it.md)); `view.test.mjs` checks both on a board mounted on `board/mini-dom.mjs`, a small DOM. The board's other views (`view-*.js`) get the same site, `$` and recorders from `BoardView.page(root, site, run)`. The view's state, such as `offline` and `data-board`, sits on its root (`.view-board`), not on `<body>`. A site card names the files it expects to touch from this table ([0022](docs/adr/0022-site-cards-build-in-parallel-when-their-files-dont-overlap.md)).
362
+
363
+ Refs in text that Claude or the owner wrote are linked by the renderer, never by hand: pass the text through `ui-links.js` (`linkText`, or `linkHtml` for an HTML reply or line) with the item's `linkContext`. A card's ref (`INT-<n>`, or any `ref` on the board) opens its card, a Linear id or PR opens its page, and a bare URL becomes a link. Write refs as plain text in thread lines, replies, questions and plans.
364
+
365
+ | File | Owns |
366
+ | --- | --- |
367
+ | `../hub/try.mjs` | Hub-owned card and main runs, async git tick, branch fetches, proxy lookup, idle cleanup and atomic try state |
368
+ | `../build-script.mjs` | Fixed build script file, ownership, permissions and executable checks |
369
+ | `../shared/usage-gate.mjs` | The pause line, Keep going, Codex handoff and file reader shared by the hub, CLI, hooks, watcher and builder start |
370
+ | `../shared/colors.css` | Every color, served as `colors.css` and linked before `style.css`; the hub serves it as `/_hub/colors.css` |
371
+ | `ui.js` | Shared helpers: escaping, times, reply bodies, attachments, icons and message buttons, badges by tone, `setHTML`, which patches a region in place and keeps the focused field's focus when it moves, and `fetchJSON`, through which the side pages read the API and wait out a board restart |
372
+ | `ui-links.js` | Linking refs, Linear ids, PRs and URLs in written text; load it right after `ui.js` |
373
+ | `ui-enums.js` | `BoardUI.BUTTON` and `BoardUI.ACTION_KIND`, the **action button**'s enums, shared with `board/domain.mjs`, which runs it in a vm context; load it before `ui-threads.js` and `ui-cards.js`. `view.test.mjs` checks that every action kind makes one button orange and every action button draws |
374
+ | `ui-cards.js` | A card's lines, in order: title with its ticket badge, or New or Task chip; status with its ETA; a Todo work item's age from two full days; Stalled when the builder is silent, then the unread chip, Action needed, PR, review and other chips, then the try badge (Visit or Install, with running, failed and stale states); Blocked by chips; and at its top right a New card's bump, edit and delete icons, or another bumpable card's bump icon; and the action row's Merge and Serve/Build buttons, main's bar and build wording |
375
+ | `ui-sidebar.js` | `BoardUI.sidebar`, the sidebar's markup from `/_hub/sidebar.json`, with the New version row from `updateRow`, headed by `BoardUI.header` itself, with each row's key in **switch mode**; `switchKeys`, which row each key opens; `othersWaiting`, for the logo's label; and `shortcuts`, the **shortcuts sheet**'s content from the one list of shortcuts. Needs `ui-page-header.js` loaded before it: `hub/hub.mjs` renders the app shell's first sidebar with both in a vm context |
376
+ | `sidebar.js` | Painting the sidebar from the copy written into the shell, else this device's last copy, then from each copy the hub's stream sends; this device's push subscription for its Turn on row; the logo's toggle, the drawer and its swipes, the rail's collapse, which this device keeps, and the logo's label; **switch mode**'s keys, from the document and a framed page, each row opened through the shell; the **shortcuts sheet**'s h h and Escape |
377
+ | `app.html` | The app shell: the sidebar, the view's slot, and its scripts and CSS from `/_hub/app/`, with a board's loads started in its `<head>`; the hub serves it for a browser's load of `/` or a site's page outside try URLs, and every load of a hub page, with the sidebar and its data written in (`hub/shell.mjs` `pageOf`), and the board server for a board's four view paths without them (`shellHtml`) |
378
+ | `router.js` | `AppRouter`: every hub URL as a view and each view as its URL (`ui.test.mjs` tests both), the views the shell mounts in place, framed pages included, and catching taps on links to them and back and forward, with `{idx}` in each history entry and `pushed` on one a push opened; a back arrow goes one step back by them. On the **page tree** each entry also carries `up`, how many of its parents sit under it, which the router writes on every way in, and the arrow goes up a level. It frames the paths `hub/shell.mjs` `pageOf` answers with the shell, which `hub/shell.test.mjs` checks |
379
+ | `shell.js` | `AppShell`: the hub code check that shows New version with an owner-tapped Reload, the sidebar mounted once, one view at a time, asking before leaving an unsaved edit, `/`'s pick of a board, the hub's one stream and which left boards are unchanged, a framed page's URL and title copied to the top, a push tap from `hub/sw.js` or a framed page's view URL (`AppShell.open`) routed in place, and registering `hub/sw.js`, which opens the shell while the host is asleep |
380
+ | `view-settings.js` | Board Settings as a modal over its board, which shell.js mounts: repo and branch (Change uses the same list and GitHub link search as New board), | `view-settings.js` | Board Settings as a modal over its board, which shell.js mounts: repo and branch (Change uses the same list and GitHub link search as New board), ticket source, auto-merge, auto-build, reviewers, rules and Close board with its typed-name sheet |
381
+ | `view-questions.js` | Waiting on you as a view: a topic's questions, their takes framed with `?take=` in a fitting grid or a chooser above one full-width frame, which a choice switches in place through the frame's `PrototypeSwitcher.current`, and one Send, which stays while the topic has a question open and otherwise opens the next topic with one, or goes back to the board |
382
+ | `view-notes.js` | One item's notes as a view, from `GET /api/notes`; the server redirects `plan.html` to `notes.html` |
383
+ | `view-hub.js` | The **hub page** as a view, from `/_hub/api/hub`, refreshed every 30 seconds while it shows; the Usage card, whose Codex share posts `/_hub/settings/codex` and pause line posts `/_hub/settings/pause-at`, with Keep going and Stop banners posting `/_hub/settings/keep-going`, pending prompts of sessions with no board, Send now, then `view-hub-settings.js` below its status |
384
+ | `view-hub-settings.js` | Summary rows opening **hub settings** modals over the hub page, from `/_hub/api/settings`: Notifications with inline Snooze chips and a native Custom picker, Polling, Linear (the masked key, its workspace and Remove), Rules for every board and Closed boards with Reopen |
385
+ | `view-polling.js` | The editor in Hub’s Polling modal, from `/_hub/api/polling`: the day bar, whose taps pick a window without asking the hub, each window's edits, the overnight hold, the daily wakes and Send now |
386
+ | `ui-hub.js` | `HubUI`, the hub views' pure renderers: rows, tiles, the flash, the Now row, this device's row, pause banners and the Usage card with the pause line stepper and ticks |
387
+ | `hub-kit.js` | `HubKit`, what the hub views share: `live` (load, the header's time and ↻, the 30-second refresh), `forms` (posting a hub form for JSON, and saving a toggle or time on change) and `device` (this device's push subscription) |
388
+ | `view-frame.js` | Any other page as a frame view: the frame beside the board header with Open alone ↗, hiding that header for a page that draws its own, whose logo then toggles the sidebar; each load's URL and title to the shell, and taps on links to other views sent to it, a prototype's back arrow included, which the shell takes up the **page tree** before the page's own script can |
389
+ | `sidebar.css` | The sidebar's rail, its collapse, the drawer and switch mode, plus the shared New board dialog and the shortcuts sheet; linked after `colors.css` |
390
+ | `hub.css` | The hub views' CSS and the hub's down page's, every rule under `.hub-view`, the class on a hub view's root and on the down page's `<html>`; the shell links it after `style.css`, whose header the hub views share, and the down page as `/_hub/hub.css` |
391
+ | `ui-page-header.js` | `BoardUI.header`, every page's header: the logo (which toggles the sidebar), a back arrow where needed (beside a phone-only logo), the title, then the time and ↻ and the gear, or a page's own trail. On a page of its own, the back arrow's own script goes one step back, or to its href after a push; in the app shell `router.js` does that. Needs nothing else: `server.mjs` renders each page's `<!--header {…}-->` with it, such as a prototype's, and `hub/hub.mjs` the shell's first sidebar, whose head is one too (`ui-sidebar.js`), each in a vm context |
392
+ | `ui-header.js` | The header's time, or Paused until from the shell's pause state, and when it moves, the GitHub status line and its Hub links mark, the Hub links list, the setup checklist, when a refresh asks for a GitHub check, and which board header controls take taps |
393
+ | `ui-threads.js` | Thread messages, with link, status and agent lines, the open card's window over them with its Show more, and the reply form's Done |
394
+ | `ui-questions.js` | Owner question cards with their takes inline and diagrams, answer history, prototype status and the Waiting on you topic row; `BoardUI.nextQuestion`, the pure move-on rule shared by Send and a prototype pick, and `sentNotice`, the Sent line the board reads on return |
395
+ | `ui-markdown.js` | The Markdown subset `view-notes.js` renders a card's notes with |
396
+ | `ui-snapshot.js` | The last visit's stored shape and size cap, which threads preload, which controls wait for the fresh board, and which lines flash |
397
+ | `attach.js` | Picking and uploading attachments, each to the site of the board view around its composer |
398
+ | `prototype.html` | The one prototype page: the server fills in a recorded topic's title, header and takes; the stage that contains each take, and the pill's room below it |
399
+ | `prototype-kit.js` | The **kit** and the take loader: each switch ends the last take's cleanup, listeners and timers, renders the next into a fresh stage at the same scroll position, and turns links and forms in the stage into toasts; `prototype-kit.test.mjs` tests it |
400
+ | `prototype-switcher.js` | A prototype page's ‹ take › pill, its keys and `?variant=`, one take alone with `?take=`, `PrototypeSwitcher.current.go(key)` for an in-place switch from Waiting on you, and with `?q=` the pick and note that answer the card's question, then the next open question in its place as a Send moves on, or with none left the board, handed to the app shell when the page is framed in it, with the board’s Sent notice in that top window; uses the shell’s `BoardUI` or loads its question helpers when opened alone; `prototype-switcher.test.mjs` tests its controls and a pick through `mount`, while `ui.test.mjs` owns the move-on rule |
401
+ | `app-core.js` | The view's shared helpers: drafts, seen marks, hiding, and `steady`, which runs a repaint keeping the box being typed in, else the open card's title row, still on screen, and its focus and cursor; re-exports `dialogs.js` as `B.confirm` and `notice` |
402
+ | `dialogs.js` | `BoardDialogs.confirm` and `notice`, which every board page's dialogs go through, and `fitSheet`, the sheets’ shared phone keyboard handling, `repoSheet` for New board and Repo → Change, and `ticketsControl`, shared by New board and Board Settings; loaded on every page |
403
+ | `rules-editor.js` | `RulesEditor`, the rules list on Board Settings and the Hub page, with its own CSS; `ui.test.mjs` tests its pure parts |
404
+ | `app-threads.js` | Loading a thread, once at a time per card; an open card's thread, its reply box, quick replies and Done |
405
+ | `app-cards.js` | Cards, their open detail with the details list ending in Notes ↗ · Visit/Install ↗ · Review on GitHub ↗ when present, and the workflow row: a follow-up's Build, Merge, Hold, the **action button** and rightmost Serve/Build, each confirming; try requests and failure-thread links; the bump icon's confirmation; an ask's detail shows only its action button; the thread's Done confirmation and send |
406
+ | `app-lanes.js` | Lanes, the Answered, older and finished-wave folds, the wave bars and filter, and Plan a wave |
407
+ | `app-asks.js` | The + sheet and its press-n-twice shortcut, and bump, edit and delete |
408
+ | `app-needs.js` | The Needs you box, led by the board session's pending prompts |
409
+ | `ui-prompts.js` | A pending prompt's Needs you line: its details, Allow once, Always allow and Deny, or a question's options, Skip and Send |
410
+ | `prompts.js` | `PromptAnswers`, answering pending prompts from their lines on a board and the hub page: which taps send at once, Always allow's confirmation, and the picks a Send waits on |
411
+ | `app-banner.js` | The answer banner, its tap and its ✕ |
412
+ | `app-header.js` | The board's header controls and time, refresh, the offline state, the In Review header's GitHub line, the Hub links sheet and the setup checklist |
413
+ | `app-snapshot.js` | Reading and saving the last visit, also as the view unmounts, keeping every board's within three at the cap, holding the controls that send while it shows, and the flash when the fresh board lands |
414
+ | `app.js` | `BoardView`: each board's `B`, its state and per-board store, mount and unmount, and the view's markup for the app shell; rendering main's slim bar and the view from its parts, painting the last visit, live when the shell says nothing changed, loading the board with its open threads (taking over the loads `app.html` starts), preloading active cards' threads, the reload timers, and the change signal from the shell |
package/LICENSE ADDED
@@ -0,0 +1,57 @@
1
+ # The Prosperity Public License 3.0.0
2
+
3
+ Contributor: Love Kharod (https://github.com/lovek4d)
4
+
5
+ Source Code: https://github.com/lovek4d/kickflip
6
+
7
+ ## Purpose
8
+
9
+ This license allows you to use and share this software for noncommercial purposes for free and to try this software for commercial purposes for thirty days.
10
+
11
+ ## Agreement
12
+
13
+ In order to receive this license, you have to agree to its rules. Those rules are both obligations under that agreement and conditions to your license. Don't do anything with this software that triggers a rule you can't or won't follow.
14
+
15
+ ## Notices
16
+
17
+ Make sure everyone who gets a copy of any part of this software from you, with or without changes, also gets the text of this license and the contributor and source code lines above.
18
+
19
+ ## Commercial Trial
20
+
21
+ Limit your use of this software for commercial purposes to a thirty-day trial period. If you use this software for work, your company gets one trial period for all personnel, not one trial per person.
22
+
23
+ ## Contributions Back
24
+
25
+ Developing feedback, changes, or additions that you contribute back to the contributor on the terms of a standardized public software license such as [the Blue Oak Model License 1.0.0](https://blueoakcouncil.org/license/1.0.0), [the Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0.html), [the MIT license](https://spdx.org/licenses/MIT.html), or [the two-clause BSD license](https://spdx.org/licenses/BSD-2-Clause.html) doesn't count as use for a commercial purpose.
26
+
27
+ ## Personal Uses
28
+
29
+ Personal use for research, experiment, and testing for the benefit of public knowledge, personal study, private entertainment, hobby projects, amateur pursuits, or religious observance, without any anticipated commercial application, doesn't count as use for a commercial purpose.
30
+
31
+ ## Noncommercial Organizations
32
+
33
+ Use by any charitable organization, educational institution, public research organization, public safety or health organization, environmental protection organization, or government institution doesn't count as use for a commercial purpose regardless of the source of funding or obligations resulting from the funding.
34
+
35
+ ## Defense
36
+
37
+ Don't make any legal claim against anyone accusing this software, with or without changes, alone or with other technology, of infringing any patent.
38
+
39
+ ## Copyright
40
+
41
+ The contributor licenses you to do everything with this software that would otherwise infringe their copyright in it.
42
+
43
+ ## Patent
44
+
45
+ The contributor licenses you to do everything with this software that would otherwise infringe any patents they can license or become able to license.
46
+
47
+ ## Reliability
48
+
49
+ The contributor can't revoke this license.
50
+
51
+ ## Excuse
52
+
53
+ You're excused for unknowingly breaking [Notices](#notices) if you take all practical steps to comply within thirty days of learning you broke the rule.
54
+
55
+ ## No Liability
56
+
57
+ ***As far as the law allows, this software comes as is, without any warranty or condition, and the contributor won't be liable to anyone for any damages related to this software or this license, under any kind of legal claim.***
package/README.md CHANGED
@@ -1,3 +1,24 @@
1
- # Temporary Holding Version
1
+ # Kickflip
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Kickflip puts your Claude Code sessions' work on your phone. One hub on your host, a Mac or a Linux machine, serves pages over your Tailscale network: a sidebar of every site, and boards that show each session's work as cards, with the questions waiting on you and a push notification when something needs you. Sessions write to their boards, and you follow and steer them from your phone.
4
+
5
+ ## Install
6
+
7
+ Requires Node 22+.
8
+
9
+ ```sh
10
+ npm i -g @lovek4d/kickflip
11
+ kickflip install
12
+ ```
13
+
14
+ See [Install on a new host](SKILL.md#install-on-a-new-host) in SKILL.md for prerequisites, updates and development from a clone.
15
+
16
+ ## License
17
+
18
+ Kickflip is licensed under the [Prosperity Public License 3.0.0](LICENSE).
19
+
20
+ - **Free for noncommercial use:** personal use, hobby projects, study and research, and use by charities, schools and government.
21
+ - **Companies** get a 30-day trial. After that, using kickflip for work needs a commercial license from Love Kharod; get in touch through https://github.com/lovek4d.
22
+ - **Ever** (https://www.evercars.com) holds a free commercial license with no end date.
23
+
24
+ Contributing? See [CONTRIBUTING.md](CONTRIBUTING.md).