Skip to content

Add Snooze feature - #28

Merged
frenchie4111 merged 14 commits into
ness-dev:mainfrom
big-guy:snooze-worktrees
May 13, 2026
Merged

Add Snooze feature#28
frenchie4111 merged 14 commits into
ness-dev:mainfrom
big-guy:snooze-worktrees

Conversation

@big-guy

@big-guy big-guy commented May 8, 2026

Copy link
Copy Markdown
Collaborator

Snooze worktrees — hide a worktree from the active sidebar groups until you're ready to come back to it.

What it does

  • Moon button appears on hover in the worktree row (next to the trash icon, hidden on the main worktree).
    • Plain click → snooze for the user-configurable default duration (7 days out of the box).
    • ⌥-click → opens a calendar popover with month navigation, "Tomorrow / 1 week / 1 month / Never" presets, and a 12-month look-ahead cap.
  • Snoozed sidebar group appears between Active and Merged/Closed, default-collapsed, with the same group on the mobile worktree picker.
  • Auto wake-up in two ways:
    • Scheduled — a SnoozeTimer in the main process pins a setTimeout to the soonest wakeAt, with a 60s safety scan to handle laptop sleep.
    • User-driven — when the worktree transitions to processing (i.e. a prompt was submitted), the snooze entry is cleared.
  • Tooltip shows time-to-wake on hover: "Wakes 30m", "Wakes 5h", "Wakes Tomorrow", "Wakes Mar 15", "Wakes Never". The resting tooltip mentions the ⌥-click affordance.
  • Settings gets a "Default snooze duration" number input (min 1, default 7).
  • Persistence — snooze entries and default-days survive restarts via the existing config schema (no migration required; new optional fields).

Architecture notes

  • New snooze slice in src/shared/state/snooze.ts with set/clear events and a formatWakeAt helper. Added to AppState, the root reducer, and seeded from config.snooze in buildInitialAppState.
  • New main-process pieces:
    • src/main/snooze-timer.ts — rolling timer + safety scan, dispatches snooze/clear on wake.
    • IPC handlers snooze:snooze, snooze:unsnooze, config:setSnoozeDefaultDays.
    • Worktree-removal subscriber drops snooze entries for paths that no longer exist.
    • ActivityDeriver now clears the entry on first processing transition before the dedup gate.
  • Renderer plumbing: useSnooze() hook, snoozedPaths threaded through groupWorktrees / sortedWorktrees, SnoozeCalendar.tsx popover, Moon button on WorktreeTab, mobile picker grouping.

Tests

  • Reducer tests for snooze/set, snooze/clear, idempotency, MAX_WAKE.
  • formatWakeAt cases (Soon / minutes / hours / Tomorrow / date / Never).
  • worktree-sort tests for the snoozed group placement.
  • ActivityDeriver tests for auto-unsnooze on processing (and not on idle/waiting).
  • settings reducer test for snoozeDefaultDaysChanged.

Test plan

  • Click moon on a worktree → row moves to the Snoozed group; row tooltip shows "Wakes ".
  • ⌥-click moon → calendar popover opens; pick a future date → row snoozed to that date.
  • Pick "Never" → tooltip says "Wakes Never".
  • Click moon on a snoozed row → wakes immediately, returns to its prior group.
  • Submit a prompt in a snoozed worktree → it auto-wakes when status hits processing.
  • Snooze a worktree, restart Harness → still snoozed, timer resumes.
  • Settings → change default duration to 3 → plain-click snooze now lasts 3 days.
  • Mobile worktree picker → Snoozed group appears with the same membership.
  • Delete a snoozed worktree → entry is cleaned up (no orphan map keys after restart).

@big-guy

big-guy commented May 8, 2026

Copy link
Copy Markdown
Collaborator Author

This might change depending on naming outcome in #27

@frenchie4111

Copy link
Copy Markdown
Collaborator

My apologies I just landed a big refactor on main so you ended up with a bunch of merge conflicts - Claude can probably fix them for you

@big-guy
big-guy force-pushed the snooze-worktrees branch from d72bb03 to 12a1717 Compare May 8, 2026 19:05
@big-guy

big-guy commented May 8, 2026

Copy link
Copy Markdown
Collaborator Author

My apologies I just landed a big refactor on main so you ended up with a bunch of merge conflicts - Claude can probably fix them for you

No worries. Rebased

@big-guy
big-guy marked this pull request as ready for review May 8, 2026 19:07
big-guy and others added 14 commits May 11, 2026 22:19
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- use option-click (not shift-click) for the wake-time calendar to match
  other modifier-driven menu items
- sort the Snoozed group above Merged/Closed in the sidebar
- show the wake-up time in the snooze button tooltip ("Wakes 30m",
  "Wakes Tomorrow", "Wakes Mar 15", "Wakes Never")

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Surface the modifier in the resting Snooze tooltip so users can
discover the calendar pick-a-date affordance without relying on
the Settings explainer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replace "option-click" / "Option-click" with the ⌥ glyph in the
snooze button tooltip and Settings explainer, and shorten the
Settings description.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@big-guy
big-guy force-pushed the snooze-worktrees branch from 12a1717 to f94546d Compare May 11, 2026 23:00
@frenchie4111

Copy link
Copy Markdown
Collaborator

trying to merge this it had some conflicts with this big change I made to abstract the backend (which removed window.api and moved to useBackend()). I'll fix it real quick and merge

@frenchie4111
frenchie4111 merged commit ec77bd9 into ness-dev:main May 13, 2026
@big-guy
big-guy deleted the snooze-worktrees branch May 22, 2026 19:13
frenchie4111 added a commit that referenced this pull request Aug 19, 2026
…279)

The repo moved to `ness-dev/ness`. GitHub's redirect means nothing is
broken today, but references throughout the repo still point at the old
path. This repoints them so we stop depending on the redirect.

This needs to land **before v2.15.1**: `scripts/release.sh` generates
`release-notes/`, the `site/public/releases.html` entry and the release
PR body from these URLs, so releasing first would permanently commit
stale URLs into site content.

## What changed

- **App code** — `HARNESS_REPO_OWNER` / `HARNESS_REPO_NAME` in
`src/shared/constants.ts` (everything else in that file derives from
these, including the in-app Report Issue / Issues / Releases links and
the auto-star call).
- **`package.json`** `build.publish` — this bakes `app-update.yml`, so
it's the change that stops shipped clients relying on the redirect.
- **`src/main/ssh-bootstrap.ts`** — the install-script raw URL was
hardcoded rather than derived. Now built from the shared constants.
- **Scripts** — `release.sh` (7 refs) and `install-headless.sh`
(`OWNER`/`REPO` + comment).
- **Docs** — `README.md`, `CONTRIBUTING.md`, `CLAUDE.md`.
- **Site** — landing-page components and static-page chrome, plus
historical release-tag / contributor-PR links in `releases.html`.

## Deliberately not changed

- `@frenchie4111` in `CONTRIBUTING.md:9` — that's the maintainer's
GitHub **username**, not a repo path.
- `release-notes/` — the shipped release-note artifacts. Rewriting them
would edit history, and their URLs still resolve via the redirect.
- `HARNESS_SITE_URL`, `harness.mikelyons.org`, `.harness.json`,
`harness-server`, `HARNESS_*` env vars, the `appId`, and `package.json`
`name` (that one keys the macOS Safe Storage keychain item).

## Notes for the reviewer

Two things worth flagging, both different from how the task was
originally scoped:

1. **`release.sh` / `README.md` are not actually coupled on the repo
path.** The concern was that `release.sh`'s README-rewriting patterns
key on the old owner/repo string, so updating the README would silently
freeze the download links at v2.15.0. They don't — the patterns are
`(?:Harness|Ness)-\d+\.\d+\.\d+` and `releases/download/v\d+\.\d+\.\d+`,
neither of which contains the owner or repo. Verified both still match
the rewritten README (4 and 2 matches, unchanged).

2. **Three files beyond the original scope needed changes**, found by
grepping rather than working from the file list:
- `src/main/ssh-bootstrap.ts:204` — a functional hardcoded
`raw.githubusercontent.com` URL driving the SSH bootstrap flow.
- `site/` — 11 files, including live "star us / GitHub" chrome on
`getness.dev`.
- The clone snippets' `cd harness`, which is now `cd ness` since that's
the directory git creates.

## Verification

- `npm run typecheck` — clean
- `npx electron-vite build` — clean
- `bash -n` + `sh -n` + `shellcheck -S error` on both shell scripts —
clean
- Traced the install script's constructed URLs end to end: the releases
API resolves `2.15.0`, and both the tarball download and the raw
install-script URL return 200 natively under `ness-dev/ness`
- Confirmed historical links resolve on the new path before rewriting
them (v1.0.0, v1.1.1, v2.15.0, PRs #28/#29 — all 200)
- `git grep -n frenchie4111` — the only surviving hit outside
`release-notes/` is the bare `@frenchie4111` username

`npx vitest run` reports 10 failures in `git-ops-state`, `path-fix` and
`worktree-watcher.integration`. **These are pre-existing and unrelated**
— a clean baseline of `main` fails in the same three files (9 failures;
the 9-vs-10 difference is flake variance). All three are git/fs-timing
and login-shell-PATH tests, and they pass in isolation both with and
without this change. Root cause: `npm run typecheck` (`tsc -b`) emits
compiled test JS into `out/`, which vitest then picks up alongside the
sources — 217 test files instead of ~110. CI runs typecheck before
vitest too, so it sees the same duplication. Not fixed here; flagging it
as worth a separate look.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants