@lovek4d/kickflip 0.7.1 → 0.7.4
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.
- package/CONTEXT.md +25 -21
- package/SKILL.md +13 -9
- package/bin/event-lines.mjs +1 -1
- package/bin/install.mjs +3 -3
- package/bin/kickflip.mjs +1 -1
- package/bin/wait-events.mjs +2 -2
- package/board/bin/board.mjs +68 -50
- package/board/domain.mjs +7 -7
- package/board/finish.mjs +4 -4
- package/board/headless.mjs +8 -8
- package/board/hub-lines.mjs +1 -1
- package/board/onboarding.mjs +3 -3
- package/board/permissions.mjs +2 -2
- package/board/public/app-asks.js +2 -14
- package/board/public/app-banner.js +8 -3
- package/board/public/app-cards.js +7 -20
- package/board/public/app-core.js +2 -2
- package/board/public/app-lanes.js +7 -15
- package/board/public/app-reader.js +7 -3
- package/board/public/app-snapshot.js +1 -1
- package/board/public/app.js +1 -1
- package/board/public/attach.js +18 -2
- package/board/public/dialogs.js +10 -12
- package/board/public/hub.css +0 -3
- package/board/public/linear-key.js +1 -1
- package/board/public/sidebar.css +1 -1
- package/board/public/style.css +42 -36
- package/board/public/ui-cards.js +33 -27
- package/board/public/ui-enums.js +2 -2
- package/board/public/ui-links.js +0 -1
- package/board/public/ui-order.js +1 -1
- package/board/public/ui-questions.js +8 -6
- package/board/public/ui-sidebar.js +1 -1
- package/board/public/ui-threads.js +26 -22
- package/board/public/ui.js +15 -9
- package/board/public/view-hub-settings.js +25 -30
- package/board/public/view-hub.js +23 -5
- package/board/public/view-questions.js +1 -1
- package/board/public/view-settings.js +4 -4
- package/board/server.mjs +18 -18
- package/board/store.mjs +30 -27
- package/board/tickets.mjs +3 -3
- package/board/worktree.mjs +15 -15
- package/hub/board-repo.mjs +1 -1
- package/hub/github.mjs +3 -3
- package/hub/hub.mjs +11 -11
- package/hub/linear.mjs +1 -1
- package/hub/new-board.mjs +2 -2
- package/hub/pr-report.mjs +2 -2
- package/hub/readiness.mjs +1 -1
- package/hub/repos.mjs +3 -3
- package/hub/review-send.mjs +1 -1
- package/hub/schedule.mjs +1 -1
- package/hub/slack.mjs +1 -1
- package/hub/try.mjs +8 -6
- package/hub/views.mjs +1 -1
- package/package.json +1 -1
- package/shared/fake-gh.mjs +19 -4
- package/shared/gh.mjs +4 -0
- package/shared/owner-check.mjs +4 -3
- package/shared/service.mjs +12 -12
- package/starter/server.mjs +3 -3
package/CONTEXT.md
CHANGED
|
@@ -55,7 +55,7 @@ The levels back climbs in the app shell: a board, its Waiting on you, then a sep
|
|
|
55
55
|
_Avoid_: nav stack, breadcrumbs
|
|
56
56
|
|
|
57
57
|
**Hub page**:
|
|
58
|
-
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, **Health**, whose Hub row reads the **hub status
|
|
58
|
+
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, **Health**, whose Hub row reads the **hub status**, then its machine row, then the Keep awake toggle row, **Integrations**, then **Boards** and **Pages** with each site's health and each board's hub links and watcher, and on each board a Restart that stops its session, restarts its server and resumes the session ([a board restarts from its row on the Hub page](docs/adr/2026-11-02-1736-a-board-restarts-from-its-row-on-the-hub-page.md)), each session's remembered Always allow rules read-only under its board, polling status, then the **hub settings** summary rows ([a sidebar of every board replaces the hub home](docs/adr/2026-10-29-1639-a-sidebar-of-every-board-replaces-the-hub-home.md), [a background session's prompts and questions come to the phone](docs/adr/2026-10-30-1516-background-sessions-prompts-come-to-the-phone.md)).
|
|
59
59
|
_Avoid_: dashboard, console, hub home
|
|
60
60
|
|
|
61
61
|
**Hub status**:
|
|
@@ -63,7 +63,7 @@ The word on the Hub row of the sidebar and the hub page's Health card: **healthy
|
|
|
63
63
|
_Avoid_: broken, hub health
|
|
64
64
|
|
|
65
65
|
**Integration readiness**:
|
|
66
|
-
What each integration needs before its features can run: the Hub page’s Integrations section between Health and Boards lists GitHub, Codex, Linear, Slack, Tailscale and Notifications in that order. A row reading ready says what passed; one reading not ready is orange and says what is missing, how to fix it and which features wait. Slack is ready while Hub’s Slack token is saved and Slack hasn’t refused it; the hub shares the same readiness and words with feature gates ([integrations show readiness, and features wait until theirs is ready](docs/adr/2026-11-02-1118-integrations-show-readiness-and-features-wait.md)).
|
|
66
|
+
What each integration needs before its features can run: the Hub page’s Integrations section between Health and Boards lists GitHub, Codex, Linear, Slack, Tailscale and Notifications in that order. A row reading ready says what passed; one reading not ready is orange and says what is missing, how to fix it and which features wait. While GitHub's hourly limit holds gh's account, GitHub's row is red, "rate limited · resets at <time>", every kickflip gh call on the spent budget refuses at once with that time, and boards dim their PR badges ([gh refuses while GitHub's hourly limit holds](docs/adr/2026-11-09-1626-gh-refuses-while-githubs-hourly-limit-holds.md)). Slack is ready while Hub’s Slack token is saved and Slack hasn’t refused it; the hub shares the same readiness and words with feature gates ([integrations show readiness, and features wait until theirs is ready](docs/adr/2026-11-02-1118-integrations-show-readiness-and-features-wait.md)).
|
|
67
67
|
_Avoid_: integration health, availability
|
|
68
68
|
|
|
69
69
|
**Usage reading**:
|
|
@@ -149,7 +149,7 @@ The owner's presence, which sets the **Check cadence** and the moon: the host's
|
|
|
149
149
|
_Avoid_: at the Mac, active, online, desktop app active
|
|
150
150
|
|
|
151
151
|
**Check cadence**:
|
|
152
|
-
How often the hub checks: every minute while the owner is present (the Mac in use, or a board page tapped on any device) and every 5 while away, or every 5 and 15 while GitHub has under 1,000 points left. Coming back checks at once. Each check fetches and pulls every board's repo and reads every card PR in one GraphQL query, 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. While away, a moon sits right of the board title ([the hub checks every minute while you’re here and every 5 when away](docs/adr/2026-11-04-0226-the-hub-checks-every-minute-while-youre-here-and-every-5-when-away.md)).
|
|
152
|
+
How often the hub checks: every minute while the owner is present (the Mac in use, or a board page tapped on any device) and every 5 while away or in quiet hours ([boards read PR state from the hub's check](docs/adr/2026-11-09-1556-boards-read-pr-state-from-the-hubs-check.md)), or every 5 and 15 while GitHub has under 1,000 points left. Coming back checks at once. Each check fetches and pulls every board's repo and reads every card PR in one GraphQL query, 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. While away, a moon sits right of the board title ([the hub checks every minute while you’re here and every 5 when away](docs/adr/2026-11-04-0226-the-hub-checks-every-minute-while-youre-here-and-every-5-when-away.md)).
|
|
153
153
|
_Avoid_: polling interval, polling window, push schedule, cron
|
|
154
154
|
|
|
155
155
|
**Quiet hours**:
|
|
@@ -157,7 +157,7 @@ A start and end time, 10:00pm to 8:00am by default. With its hold on, pushes que
|
|
|
157
157
|
_Avoid_: overnight window, wake times
|
|
158
158
|
|
|
159
159
|
**Hub settings**:
|
|
160
|
-
The summary rows below the **hub page**’s health and status: **Pause hub** first, then Notifications, Quiet hours, Rules for every board and Closed boards. Each opens its controls in a centred modal with its own scroll area. Snooze and Pause hub share one duration picker: 1h, 3h, 6h, 1d, Forever and Custom. Keep awake, Linear and Slack are not summary rows:
|
|
160
|
+
The summary rows below the **hub page**’s health and status: **Pause hub** first, then Notifications, Quiet hours, Rules for every board and Closed boards. Each opens its controls in a centred modal with its own scroll area. Snooze and Pause hub share one duration picker: 1h, 3h, 6h, 1d, Forever and Custom. Keep awake, Linear and Slack are not summary rows: Keep awake is a toggle row in Health, under the machine row, and Linear's key and Slack's token open modals from an Add key or Change key button at the end of their Integrations rows ([Linear and Slack keys open in modals from their Integrations rows](docs/adr/2026-11-09-2230-linear-and-slack-keys-open-in-modals-from-their-integrations-rows.md)). Keep awake is an on/off toggle that has the hub run `caffeinate -s`, holding the Mac awake only on AC power, and is disabled on hosts other than macOS ([Keep awake is a toggle that holds the Mac awake only on AC power](docs/adr/2026-11-09-1200-keep-awake-is-a-toggle-that-holds-the-mac-awake-only-on-ac-power.md)). Old Hub Settings links open the matching modal over Hub ([hub settings open as modals](docs/adr/2026-11-01-1221-hub-settings-open-as-modals.md)).
|
|
161
161
|
_Avoid_: Hub Settings page, preferences, hub config
|
|
162
162
|
|
|
163
163
|
**Shared person**:
|
|
@@ -203,7 +203,7 @@ A button the owner taps on the board, sent as an event. The board's rule for tap
|
|
|
203
203
|
_Avoid_: action, click
|
|
204
204
|
|
|
205
205
|
**Wave**:
|
|
206
|
-
A named set of cards from one **item file**, in build order, with a goal and a short note. Only the owner asks for one, with the +, or Claude proposes one as a board question when it spots a grouping in Todo. `board.mjs wave create --card <key>[:<earlier keys>] --card …` makes each card blocked only by the earlier cards of the wave it would conflict with, and writes the note to `wave-<slug>.md` beside the item file; each card stays one PR and starts on its own as it unblocks, so cards that don't conflict build side by side ([a wave's cards wait only on the earlier cards they'd conflict with](docs/adr/2026-11-09-1321-a-waves-cards-wait-only-on-the-earlier-cards-they-conflict-with.md)). A wave made in steps reads its steps in order and keeps its links. Todo shows it as a **wave band**. It is open until `wave finish
|
|
206
|
+
A named set of cards from one **item file**, in build order, with a goal and a short note. Only the owner asks for one, with the +, or Claude proposes one as a board question when it spots a grouping in Todo. `board.mjs wave create --card <key>[:<earlier keys>] --card …` makes each card blocked only by the earlier cards of the wave it would conflict with, and writes the note to `wave-<slug>.md` beside the item file; each card stays one PR and starts on its own as it unblocks, so cards that don't conflict build side by side ([a wave's cards wait only on the earlier cards they'd conflict with](docs/adr/2026-11-09-1321-a-waves-cards-wait-only-on-the-earlier-cards-they-conflict-with.md)). A wave made in steps reads its steps in order and keeps its links. Todo shows it as a **wave band**. Another card can wait on the whole wave with `--deps wave:<slug>`: on every card it holds as it grows, until each is Done, its blocked-by line listing the wave's open cards ([a card can wait on a whole wave](docs/adr/2026-11-09-2330-a-card-can-wait-on-a-whole-wave.md)). A card leaves with `wave remove`, keeping its own links, and only once no open card of the wave waits on it ([a card leaves a wave only once no open card waits on it](docs/adr/2026-11-09-2345-a-card-leaves-a-wave-only-once-no-open-card-waits-on-it.md)). It is open until `wave finish`, which refuses while a card in it is open, or until all its cards are Done ([a wave the owner asks for is a lineup of cards](docs/adr/2026-11-02-1202-a-wave-the-owner-asks-for-is-a-lineup-of-cards.md); the names are [a lineup is called a wave, and the wave file an item file](docs/adr/2026-11-09-2100-a-lineup-is-called-a-wave-and-the-wave-file-an-item-file.md)).
|
|
207
207
|
_Avoid_: lineup (its old name), group, epic, batch, sprint
|
|
208
208
|
|
|
209
209
|
**Item file**:
|
|
@@ -223,7 +223,7 @@ Claude reviewing a board's repo, chosen by `hub/board-repo.mjs` `boardRepo`, wit
|
|
|
223
223
|
_Avoid_: improvements round, audit (for the whole review), bulb round
|
|
224
224
|
|
|
225
225
|
**Reviewers**:
|
|
226
|
-
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 item file copies them into its own `reviewers`. The board server writes both an add and a remove: an add picks a Slack member and a GitHub login from lists it fetches with the hub's Slack token and `gh`, and the server opens the DM ([the server adds reviewers from fetched lists](docs/adr/2026-11-05-2100-the-server-adds-reviewers-from-fetched-lists.md), extending [the board server removes reviewers itself](docs/adr/2026-11-04-0119-the-board-server-removes-reviewers-itself.md)). Kickflip owns the sent messages per PR, not per card, in each board's `data/pr-slack.json` `{<owner/repo#n>: {head, links: {<name>: {channel, ts, reactions}}, replies}}`: a card holds 1:m PRs, and each PR has 1:m reviewer DMs sharing one thread and one set of reactions. The card, Stale age, hub Slack link and digest read each reviewer's first link across the card's PRs, with an item's older `review_link_ts` and slack-bridge topic files only as a fallback ([Slack sends hang off PRs and go out from the hub tick](docs/adr/2026-11-07-0300-slack-sends-hang-off-prs-and-go-out-from-the-hub-tick.md)). The hub sends them as the owner, through the Slack user token pasted on Hub
|
|
226
|
+
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 item file copies them into its own `reviewers`. The board server writes both an add and a remove: an add picks a Slack member and a GitHub login from lists it fetches with the hub's Slack token and `gh`, and the server opens the DM ([the server adds reviewers from fetched lists](docs/adr/2026-11-05-2100-the-server-adds-reviewers-from-fetched-lists.md), extending [the board server removes reviewers itself](docs/adr/2026-11-04-0119-the-board-server-removes-reviewers-itself.md)). Kickflip owns the sent messages per PR, not per card, in each board's `data/pr-slack.json` `{<owner/repo#n>: {head, links: {<name>: {channel, ts, reactions}}, replies}}`: a card holds 1:m PRs, and each PR has 1:m reviewer DMs sharing one thread and one set of reactions. The card, Stale age, hub Slack link and digest read each reviewer's first link across the card's PRs, with an item's older `review_link_ts` and slack-bridge topic files only as a fallback ([Slack sends hang off PRs and go out from the hub tick](docs/adr/2026-11-07-0300-slack-sends-hang-off-prs-and-go-out-from-the-hub-tick.md)). The hub sends them as the owner, through the Slack user token pasted on Hub › Integrations › Slack and kept in `~/.claude/kickflip/secrets.json` beside Linear's key, opening the owner's DM with each reviewer, and records each message as it posts, so a failed one goes again next poll and a posted one never twice ([the hub sends reviewer Slack messages with its own token](docs/adr/2026-11-05-0141-the-hub-sends-reviewer-slack-messages-with-its-own-token.md)). Hub Slack identity lives in `~/.claude/kickflip/slack.json`, read or set by `board.mjs slack`; its timezone sets the weekday 09:00 to 17:00 digest window ([kickflip owns the reviewer record](docs/adr/2026-11-01-2347-kickflip-owns-the-reviewer-record.md)).
|
|
227
227
|
_Avoid_: approvers, watchers
|
|
228
228
|
|
|
229
229
|
**Finished item file**:
|
|
@@ -231,23 +231,27 @@ An item file with work items that are all in Done. Done shows it as one row that
|
|
|
231
231
|
_Avoid_: finished wave (a wave finishes with `wave finish`), closed wave, archived wave
|
|
232
232
|
|
|
233
233
|
**Work item**:
|
|
234
|
-
One unit of work in an **item file**, usually one PR. Its key is `<file>/<id>`. It reads as its ticket when the ticket is from the board's source (`#27`, `ENG-12
|
|
235
|
-
_Avoid_:
|
|
234
|
+
One unit of work in an **item file**, usually one PR. Its key is `<file>/<id>`. It reads as its ticket when the ticket is from the board's source (`#27`, `ENG-12`), else as its id ([a card is named by its board's ticket source](docs/adr/2026-11-02-2100-a-card-is-named-by-its-boards-ticket-source.md)). A new one is a **task** until it gets a ticket.
|
|
235
|
+
_Avoid_: ticket (the identity of its work in the board’s ticket source)
|
|
236
|
+
|
|
237
|
+
**Task**:
|
|
238
|
+
A work item with no ticket, keyed `T-<n>` (its board's next number), with a quiet Task badge in the ticket slot. Every new card starts as one. It gets a ticket from `--ticket <id>`, `--new-ticket`, or a yes to its grill's ticket question, which is asked only on a board with a **ticket source**; a board with none keeps every card a task ([a task is a card with no ticket, keyed T-<n>](docs/adr/2026-11-09-1500-a-task-is-a-card-with-no-ticket-keyed-t-n.md)).
|
|
239
|
+
_Avoid_: unticketed card
|
|
236
240
|
|
|
237
241
|
**Ticket**:
|
|
238
|
-
The identity of a work item in its board’s **ticket source**, shown as a badge that opens the
|
|
242
|
+
The identity of a work item in its board’s **ticket source**, shown as a badge that opens the 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, and a card filed from it is a **task** ([a task is a card with no ticket, keyed T-<n>](docs/adr/2026-11-09-1500-a-task-is-a-card-with-no-ticket-keyed-t-n.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 ([kickflip reaches Linear with a key on the Mac](docs/adr/2026-11-01-1436-kickflip-reaches-linear-with-a-key-on-the-mac.md)).
|
|
239
243
|
_Avoid_: card (the board’s work item), task
|
|
240
244
|
|
|
241
245
|
**Ticket source**:
|
|
242
|
-
The board’s one choice for
|
|
246
|
+
The board’s one choice for its cards’ tickets: none (the default: every card stays a **task**), GitHub issues in the board’s repo when it is on GitHub, or a Linear workspace, team and project, picked from dropdowns listed through Hub’s **Linear key**. New board and Board Settings share the choice ([a board has one ticket source](docs/adr/2026-11-01-1348-a-board-has-one-ticket-source.md)), and pick Linear only while that key works: tapping Linear without one opens the key’s modal over them. Board Settings saves each change at once, and Done waits for a save in flight ([a board picks Linear only while Hub's key works](docs/adr/2026-11-02-1712-a-board-picks-linear-only-while-hubs-key-works.md)). A GitHub source can name the gh account its issue calls run as. The hub board’s repo is its entry’s `github`, its folder’s origin, so it can file its cards as GitHub issues ([the hub board files its cards as GitHub issues](docs/adr/2026-11-02-1611-the-hub-board-files-its-cards-as-github-issues.md)).
|
|
243
247
|
_Avoid_: integration, tracker, GitHub project
|
|
244
248
|
|
|
245
249
|
**Linear key**:
|
|
246
|
-
The owner’s Linear personal API key, pasted in Hub
|
|
250
|
+
The owner’s Linear personal API key, pasted in Hub › Integrations and kept only on the host; pages learn only whether it is set, whether Linear refused it and its workspace. It saves and is checked with Linear on paste, with no Save button, and the hub remembers a refusal until a call with that key gets an answer ([a board picks Linear only while Hub's key works](docs/adr/2026-11-02-1712-a-board-picks-linear-only-while-hubs-key-works.md)). kickflip lists teams and projects, creates projects and issues, and moves tickets with it. The hub's poll uses the saved key's viewer as the owner and advances their Linear tickets from PR state, aggregating every carrying card across boards; without a key the poll moves none ([the hub's poll moves the owner's Linear tickets](docs/adr/2026-11-02-0023-the-hubs-poll-moves-the-owners-linear-tickets.md)). Without a key, no board can pick Linear, and removing the key moves every Linear board to no ticket source ([a task is a card with no ticket, keyed T-<n>](docs/adr/2026-11-09-1500-a-task-is-a-card-with-no-ticket-keyed-t-n.md)).
|
|
247
251
|
_Avoid_: Linear token, API secret
|
|
248
252
|
|
|
249
253
|
**Internal item**:
|
|
250
|
-
A work item about the sites themselves rather than the product. Its legacy ref `S<n>` reads `INT-<n
|
|
254
|
+
A work item about the sites themselves rather than the product. Its legacy ref `S<n>` reads `INT-<n>`.
|
|
251
255
|
_Avoid_: meta task, chore
|
|
252
256
|
|
|
253
257
|
**Badge**:
|
|
@@ -283,7 +287,7 @@ Similar cards that should be cross-referenced, shown on both cards. The session
|
|
|
283
287
|
_Avoid_: sibling, link (alone)
|
|
284
288
|
|
|
285
289
|
**Split**:
|
|
286
|
-
A card a grill broke into a **wave** of smaller cards: the grill planner proposes it, when the code shows separable parts, as one question, Split as proposed or Keep as one card, and on Split the orchestrator files the cards when the card stages, as Children of the original, which closes Done "Split into …"; a card already in a wave splits into it at its place ([a grill can propose splitting its card into a wave](docs/adr/2026-11-07-1216-a-grill-can-propose-splitting-its-card-into-a-wave.md)). Wave triage's splits are of a wave's cards after filing.
|
|
290
|
+
A card a grill broke into a **wave** of smaller cards: the grill planner proposes it, when the code shows separable parts, as one question, Split as proposed or Keep as one card, and on Split the orchestrator files the cards when the card stages, as Children of the original, which closes Done "Split into …"; a card already in a wave splits into it at its place ([a grill can propose splitting its card into a wave](docs/adr/2026-11-07-1216-a-grill-can-propose-splitting-its-card-into-a-wave.md)). The close, `item set --done --split-into <wave:slug | keys>`, moves every open card that waited on the original onto the wave or cards it split into ([a card can wait on a whole wave](docs/adr/2026-11-09-2330-a-card-can-wait-on-a-whole-wave.md)). Wave triage's splits are of a wave's cards after filing.
|
|
287
291
|
_Avoid_: breakdown, epic
|
|
288
292
|
|
|
289
293
|
**Plan waves**:
|
|
@@ -295,7 +299,7 @@ _Avoid_: backlog item, todo
|
|
|
295
299
|
_Avoid_: history, old cards
|
|
296
300
|
|
|
297
301
|
**Lane**:
|
|
298
|
-
Where a card sits (In Review, In Progress, Todo, Done
|
|
302
|
+
Where a card sits (In Review, In Progress, Todo, Done), set by who acts next ([the lane follows who acts next, and a card waiting on the owner says Action needed](docs/adr/2026-10-29-1639-the-lane-follows-who-acts-next.md)). In Progress means a builder runs on the card: a running build agent, a **checking** item, or a started card between runs; a ready PR whose CI is red, or a red draft on a board that **skips the draft**, waits in In Review reading "Fix the failing CI" until a fix builder starts ([In Progress means a builder runs](docs/adr/2026-11-09-1804-in-progress-means-a-builder-runs.md)). A work card stores its **lane** (todo, progress, review, done, with `lane_at`), which one writer moves with a "Moved to <lane>: <why>" thread line; a card has no status, and held, blocked, merged and dropped are facts beside the lane ([a card stores its lane, and facts replace statuses](docs/adr/2026-11-07-1400-a-card-stores-its-lane-and-facts-replace-statuses.md)). A work card that has left Todo never drops back to it: held, blocked or **bumped out**, its build takes a **card pause** In Progress ([a card pause stops its builder at its next stopping point](docs/adr/2026-11-09-1300-a-card-pause-stops-its-builder-at-its-next-stopping-point.md), [a card moves to In Progress only once building starts](docs/adr/2026-11-03-0202-a-card-moves-to-in-progress-only-once-building-starts.md)). A running grill or prototype agent shows its status line but leaves the card where it is. In Review means finished work waits on a person: the reviewers, or the owner's review of a draft ("Review draft PR #n", whatever its CI), Merge, manual steps or the Done of an ask (see **Action needed**), or of a reopened card. An owner message or a new question on a Done card, merged or not, reopens it into In Review: "Your reply waits on the orchestrator" until Claude answers, then the owner's Done, its next step "Tap Done once it is settled" ([a message or question on a Done card reopens it until the owner's Done](docs/adr/2026-11-02-1952-a-message-or-question-on-a-done-card-reopens-it.md), [a merged card reopens on a message, and its PR badge shows the PR's state](docs/adr/2026-11-03-1843-a-merged-card-reopens-and-its-pr-badge-shows-the-prs-state.md), [a card waiting only to be closed has no Action needed](docs/adr/2026-11-03-0137-a-card-waiting-only-to-be-closed-has-no-action-needed.md)). A reopened card goes to In Progress only once a builder starts on it ([In Progress means a builder runs](docs/adr/2026-11-09-1804-in-progress-means-a-builder-runs.md)). A card waiting only on those moves there from In Progress; one Claude still works on stays in In Progress. A card with several PRs, in any repos, sits where its least-ready PR puts it, shows a PR badge for each, and is Done once every PR has merged or closed with at least one merged ([a card holds several PRs across repos](docs/adr/2026-11-03-1904-a-card-holds-several-prs-across-repos.md)). Todo holds work that hasn't started building: the New cards, which wait on Claude ([the + makes a New card, which Claude files, folds or answers](docs/adr/2026-10-29-1639-the-plus-makes-a-new-card.md)), and every work card with no build yet, where its **Implement** shows. Discussion keeps a card there: open questions or a prototype pick, with Action needed, answered or resolved questions, grill and prototype agents, a working mark on a message in its thread and a prototype recorded for it ([a card moves to In Progress only once building starts](docs/adr/2026-11-03-0202-a-card-moves-to-in-progress-only-once-building-starts.md)). A started card that gets a question keeps its lane ([an unstarted card waits on its questions in Todo, and its one action button follows what it waits on](docs/adr/2026-10-30-1309-an-unstarted-card-waits-on-its-questions-in-todo.md)). Each lane has one fixed order, with no sort menu ([each lane has one fixed order](docs/adr/2026-11-03-0223-each-lane-has-one-fixed-order.md)): every lane but Done lists its bumped cards first, the earliest bump on top ([a bump marks the priority through every lane](docs/adr/2026-11-06-1700-a-bump-marks-the-priority-through-every-lane.md)); Todo then lists the rest oldest made first, so a new card joins the bottom ([new Todo cards join the bottom, so old ones leave first](docs/adr/2026-11-05-2345-new-todo-cards-join-the-bottom.md)); In Progress, after its bumped cards, by when each came into the lane, its reopening or its latest build agent's start, the newest arrival on top; In Review, after its bumped cards, by when the PR was first sent to a reviewer, or a reopened card's reopening, the newest on top; Done by when each card closed, or for an answered ask when Claude last answered, the most recent on top. Done shows 10 cards, then a Show more, the thread's, that adds 10 and says how many are left; a card the owner has open stays shown ([answered asks fold into the one Done list, the kind shown in the ticket slot](docs/adr/2026-11-09-1430-answered-asks-fold-into-the-one-done-list.md)). Every card, a New card included, ends with one muted line holding the time its lane sorts by: the time only today, the day too before today ("10/31, 2:49 PM"), and the year too past 365 days. A card with no such time has no time on that line.
|
|
299
303
|
_Avoid_: column, status (status is the item's own field)
|
|
300
304
|
|
|
301
305
|
**Checking**:
|
|
@@ -340,7 +344,7 @@ _Avoid_: diagram (one kind of visual), prototype (takes for a decision)
|
|
|
340
344
|
|
|
341
345
|
**Action button**:
|
|
342
346
|
The open card's one workflow button, which `board/domain.mjs` picks as the card's `actionButton`: **Needs you** (`openQuestions`) while it has open questions or a prototype pick, opening the first; otherwise none; an ask waiting on the owner's Done has it in its thread box, beside Reply. A grill or prototype is asked for from the thread's fire and eye (**request**), and the card reads a grey "preheating" while a grill or prototype request waits for its planner (not yet replied to, no questions posted on the card after it, and no planner of its kind started after it finished), then a planner line "grilling" or "prototyping" in its lane's colour once that planner runs ([a card's status word comes from a signal the board checks](docs/adr/2026-11-05-0410-a-card-status-word-comes-from-a-signal-the-board-checks.md)) ([a new card is ready, grilling or prototyping, and reads ready in green when it is](docs/adr/2026-11-03-0516-a-new-card-is-ready-grilling-or-prototyping.md)). A Done card has none ([an unstarted card waits on its questions in Todo, and its one action button follows what it waits on](docs/adr/2026-10-30-1309-an-unstarted-card-waits-on-its-questions-in-todo.md)). It reaches the prototypes too, as each is a question on the card with a View prototypes link, Done cards included; there is no Prototypes button ([the Needs you button reaches the prototypes, and the Prototypes button is dropped](docs/adr/2026-11-09-1100-needs-you-button-reaches-the-prototypes.md)).
|
|
343
|
-
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 Questions orange (a pick makes Prototypes orange too),
|
|
347
|
+
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 Questions orange (a pick makes Prototypes orange too), 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 Questions stay plain. A reopened card waiting only on the owner's Done has its thread's Done primary, in the accent color ([a card waiting only to be closed has no Action needed](docs/adr/2026-11-03-0137-a-card-waiting-only-to-be-closed-has-no-action-needed.md)).
|
|
344
348
|
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.
|
|
345
349
|
_Avoid_: Open items, Action needed (the badge)
|
|
346
350
|
|
|
@@ -371,11 +375,11 @@ The open card's link to its PR, on every card with an open PR, last on the detai
|
|
|
371
375
|
_Avoid_: Review #<n> on GitHub, PR link
|
|
372
376
|
|
|
373
377
|
**Install**:
|
|
374
|
-
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 "Serving…" with an inert spinner; a failed build reads "Serve failed ↗" and opens its thread. The open card's badge sits between Notes and Review on GitHub. The workflow button starts a Serve, or Retry after failure, while Auto serve is off and the card's branch has changes ([only the owner or Auto serve starts a card run](docs/adr/2026-11-02-1806-only-the-owner-or-auto-build-starts-a-card-run.md)). Main’s red Build failed button opens a bottom sheet with the last 40 lines of `data/try/main.log` and Retry at the recorded failed tip. Retry shows readiness refusals in the sheet, reads Building… in orange while running, refreshes the log after failure and closes with a toast when ready. On a local board, a refresh icon right of main's timestamp pulls its branch from origin now, as every poll does, and turns red with the reason when a pull can't fast-forward ([a local board's Main pulls from origin on its own](docs/adr/2026-11-03-1914-a-local-boards-main-pulls-from-origin-on-its-own.md)). A Done card has no try badge ([a board's build script serves or builds each card and main](docs/adr/2026-11-01-1846-a-boards-build-script-serves-or-builds-each-card-and-main.md)).
|
|
378
|
+
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 "Serving…" with an inert spinner; a failed build reads "Serve failed ↗" and opens its thread. The open card's badge sits between Notes and Review on GitHub. The workflow button starts a Serve, or Retry after failure, while Auto serve is off and the card's branch has changes ([only the owner or Auto serve starts a card run](docs/adr/2026-11-02-1806-only-the-owner-or-auto-build-starts-a-card-run.md)). While main re-serves, its old server keeps serving until the new one answers: the bar names the old commit and keeps Visit to it, with a Serving… chip left of Visit, or a Failed button there when the new build failed ([a Main re-serve keeps the old server up until the new one answers](docs/adr/2026-11-09-1815-a-main-re-serve-keeps-the-old-server-up-until-the-new-one-answers.md)). Main’s red Build failed button opens a bottom sheet with the last 40 lines of `data/try/main.log` and Retry at the recorded failed tip. Retry shows readiness refusals in the sheet, reads Building… in orange while running, refreshes the log after failure and closes with a toast when ready. On a local board, a refresh icon right of main's timestamp pulls its branch from origin now, as every poll does, and turns red with the reason when a pull can't fast-forward; a tap or click always pulls again, on any device, and opens the reason when that pull fails ([a local board's Main pulls from origin on its own](docs/adr/2026-11-03-1914-a-local-boards-main-pulls-from-origin-on-its-own.md)). A Done card has no try badge ([a board's build script serves or builds each card and main](docs/adr/2026-11-01-1846-a-boards-build-script-serves-or-builds-each-card-and-main.md)).
|
|
375
379
|
_Avoid_: download, TestFlight (a different channel), older build
|
|
376
380
|
|
|
377
381
|
**Build script**:
|
|
378
|
-
The repo's reviewed, executable `.kickflip/build`, or `.kickflip/build-<target>` when Board Settings' Build target names one, run by the hub from each card's worktree and main's hub-owned detached
|
|
382
|
+
The repo's reviewed, executable `.kickflip/build`, or `.kickflip/build-<target>` when Board Settings' Build target names one, run by the hub from each card's worktree and the one of main's two alternating hub-owned detached worktrees that its old server isn't using. One repo can hold several targets, such as `build-web` and `build-ios`; each board runs one, and main builds the same target as its cards. The hub passes the key (or `main`) and worktree, plus `KICKFLIP_BOARD`. Websites serve at their root on their own Tailscale HTTPS port, without a path prefix or `KICKFLIP_BASE` ([a try build serves on its own Tailscale port](docs/adr/2026-11-01-2254-a-try-build-serves-on-its-own-tailscale-port.md)). 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: the board's `build_target` picks among the repo's `.kickflip/build-*` scripts, and with none the hub runs `.kickflip/build` ([a board's build script serves or builds each card and main](docs/adr/2026-11-01-1846-a-boards-build-script-serves-or-builds-each-card-and-main.md), [Board Settings picks which build script a board runs](docs/adr/2026-11-05-1823-board-settings-picks-which-build-script-a-board-runs.md)).
|
|
379
383
|
_Avoid_: install script, session install runner
|
|
380
384
|
|
|
381
385
|
**Serve**:
|
|
@@ -423,7 +427,7 @@ A work card the owner paused with its Hold button, which opens the shared durati
|
|
|
423
427
|
_Avoid_: paused, frozen, on hold (as a status name)
|
|
424
428
|
|
|
425
429
|
**Blocked**:
|
|
426
|
-
A work card that waits on something concrete before it can start: other cards (its deps, until each reaches Done), or a condition an agent can check, which the orchestrator clears with `item set --blocked-until ""` once it holds, either with a reason. Its time line reads "blocked by #53" or "blocked until <condition>", then ", <reason>". A time is a hold, never a block ([nothing waiting on the orchestrator is missed](docs/adr/2026-11-05-2358-nothing-waiting-on-the-orchestrator-is-missed.md)). Blocked In Progress, its build takes a **card pause** and the card stays In Progress ([a card pause stops its builder at its next stopping point](docs/adr/2026-11-09-1300-a-card-pause-stops-its-builder-at-its-next-stopping-point.md)); blocked In Review, it stays but can't land, its Merge disabled ([a block past Todo bumps a build and holds a land](docs/adr/2026-11-06-0102-a-block-past-todo-bumps-a-build-and-holds-a-land.md)).
|
|
430
|
+
A work card that waits on something concrete before it can start: other cards (its deps, until each reaches Done), or a condition an agent can check, which the orchestrator clears with `item set --blocked-until ""` once it holds, either with a reason. Its time line reads "blocked by #53" or "blocked until <condition>", then ", <reason>". A time is a hold, never a block ([nothing waiting on the orchestrator is missed](docs/adr/2026-11-05-2358-nothing-waiting-on-the-orchestrator-is-missed.md)). A block only keeps a card from In Progress: a blocked card in Todo is grilled, prototyped, staged and set ready as any other, after the unblocked arrivals take the planner slots. A ready card whose block ends waits in Orchestrator review for a **re-check** (`recheck_at`): the orchestrator re-reads its notes and answered questions against what landed, settles a stale one with `question settle <id> --note` or sends the card back to grilling, and releases it with `item set --phase ready --text "<what it re-checked>"` ([a block only keeps a card from In Progress](docs/adr/2026-11-09-1939-a-block-only-keeps-a-card-from-in-progress.md)). Blocked In Progress, its build takes a **card pause** and the card stays In Progress ([a card pause stops its builder at its next stopping point](docs/adr/2026-11-09-1300-a-card-pause-stops-its-builder-at-its-next-stopping-point.md)); blocked In Review, it stays but can't land, its Merge disabled ([a block past Todo bumps a build and holds a land](docs/adr/2026-11-06-0102-a-block-past-todo-bumps-a-build-and-holds-a-land.md)).
|
|
427
431
|
_Avoid_: held (a pause, not a dependency), waits on (who the card waits for)
|
|
428
432
|
|
|
429
433
|
**Pick-up time**:
|
|
@@ -460,8 +464,7 @@ _Avoid_: copy, checkout, live folder (for the builder's)
|
|
|
460
464
|
### Talking with the owner
|
|
461
465
|
|
|
462
466
|
**New task**:
|
|
463
|
-
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>`.
|
|
464
|
-
_Avoid_: ask (in what the owner reads), message, prompt, Waiting for Claude strip
|
|
467
|
+
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>`. From the moment it arrives it is a task card, keyed `T-<n>` and titled from its first line, whose thread opens with the owner's message: a **New card** at the top of Todo, a New mark in its ticket slot, with no Action needed, until Claude sorts it one of three ways ([the + makes a New card, which Claude files, folds or answers](docs/adr/2026-10-29-1639-the-plus-makes-a-new-card.md), [every ask is a task card from the moment it arrives](docs/adr/2026-11-09-1900-every-ask-is-a-task-card-from-the-moment-it-arrives.md)): sets its **card state** (`item set --phase`); merges it into the card it's about, which drops the New card, shows its thread in the other card's and names that card in a banner (`item relate-ask`, undone by `ask unfold`); or answers it (`reply <key>`), which puts it in In Review waiting for the owner's Done, with no Action needed. Deleting the message of a card still New removes the card. An ask never draws as its own row: the board server files one from before asks arrived as cards as its card, an answered one closed in Done at its answer time ([old asks migrate into cards, and the ask-only records retire](docs/adr/2026-11-09-2000-old-asks-migrate-into-cards-and-the-ask-only-records-retire.md)). An answer that leaves a question open on it keeps it in Todo with Action needed, never in Done, until the owner answers and Claude replies, or it is closed; it stays in Todo while Claude takes up the answer ([an unstarted card waits on its questions in Todo, and its one action button follows what it waits on](docs/adr/2026-10-30-1309-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 its unstarted card is dropped, leaving only the ask's thread line linking to it. Folding is within one board; a move is between boards._Avoid_: ask (in what the owner reads), message, prompt, Waiting for Claude strip
|
|
465
468
|
|
|
466
469
|
**Thread**:
|
|
467
470
|
The conversation on one work item: Claude's reports, builders' status lines, the board's status lines, the owner's messages, mirrored GitHub comments, and answers to the item's questions. Every line the board (Kickflip) writes is a status line, a mark and at most 100 characters, red when it is a fault, which alone of them counts as unread; only the orchestrator's own words make a message titled Orchestrator, at most 2000 characters ([the board writes status lines, and only the orchestrator writes messages](docs/adr/2026-11-09-1200-the-board-writes-status-lines-and-only-the-orchestrator-writes-messages.md)). An In Review card's reply form sends with Reply then Done, which closes the card; a Todo card closes from Close, right of Hold in its actions, and an In Progress card has neither, only Send ([Done shows only in In Review, beside Reply](docs/adr/2026-11-05-1918-done-shows-only-in-in-review-beside-reply.md)).
|
|
@@ -565,6 +568,7 @@ Refs in text that Claude or the owner wrote are linked by the renderer, never by
|
|
|
565
568
|
| `../hub/try.mjs` | Hub-owned card and main runs, async git tick, branch fetches, proxy lookup, idle cleanup and atomic try state |
|
|
566
569
|
| `../build-script.mjs` | Fixed build script file, ownership, permissions and executable checks |
|
|
567
570
|
| `../shared/usage-gate.mjs` | The pause line, Keep going, Codex handoff and file reader shared by the hub, CLI, hooks, watcher and builder start |
|
|
571
|
+
| `../shared/gh.mjs` | The one gh runner, holding GitHub's hourly limit gate (`gh-limit.json`) by account and budget, which every kickflip gh call goes through |
|
|
568
572
|
| `../shared/colors.css` | Every color, served as `colors.css` and linked before `style.css`; the hub serves it as `/_hub/colors.css` |
|
|
569
573
|
| `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 |
|
|
570
574
|
| `ui-links.js` | Linking refs, Linear ids, PRs and URLs in written text; load it right after `ui.js` |
|
|
@@ -582,7 +586,7 @@ Refs in text that Claude or the owner wrote are linked by the renderer, never by
|
|
|
582
586
|
| `view-questions.js` | Waiting on you as a view: a topic's questions, a prototype question's View prototypes link to its viewer, each question's eye, which asks for a prototype of that question, 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 |
|
|
583
587
|
| `view-notes.js` | One item's notes, or with `?wave=` a wave's goal, order and note, as a view, from `GET /api/notes`; the server redirects `plan.html` to `notes.html` |
|
|
584
588
|
| `view-hub.js` | The **hub page** as a view, from `/_hub/api/hub`, refreshed every 30 seconds while it shows; the Usage card, whose Claude model posts `/_hub/settings/claude-model`, 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 |
|
|
585
|
-
| `view-hub-settings.js` |
|
|
589
|
+
| `view-hub-settings.js` | Summary rows opening **hub settings** modals over the page, and Linear’s and Slack’s key modals (`linear-key.js`), which open from the Integrations rows, from `/_hub/api/settings`: Notifications with Snooze, Pause hub with their one duration picker (`durationPicker`: chips and a native Custom picker), Quiet hours, Rules for every board and Closed boards with Reopen |
|
|
586
590
|
| `linear-key.js` | `LinearKey`, Hub’s Linear key slot: the masked key or why it doesn’t work, the link to where keys are made, saving on paste, change or Enter with Remove, and the same slot as a modal over Board Settings or New board, which the Tickets control opens when Linear is tapped without a working key |
|
|
587
591
|
| `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 |
|
|
588
592
|
| `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) |
|