Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 51 additions & 8 deletions .github/workflows/release-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ name: Release Build

on:
push:
branches:
- main
tags:
- 'v*'

Expand All @@ -14,23 +16,62 @@ concurrency:
cancel-in-progress: false

jobs:
verify-tag-on-main:
verify-release-source:
runs-on: ubuntu-24.04
outputs:
should_release: ${{ steps.resolve.outputs.should_release }}
release_tag: ${{ steps.resolve.outputs.release_tag }}
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0
submodules: recursive

- name: Verify tag commit is on main
- name: Resolve release trigger and tag
id: resolve
run: |
set -euo pipefail
git fetch origin main
git merge-base --is-ancestor "${GITHUB_SHA}" "origin/main"

ref_type="${GITHUB_REF_TYPE}"
ref_name="${GITHUB_REF_NAME}"
event_name="${GITHUB_EVENT_NAME}"

if [ "$ref_type" = "tag" ]; then
if ! git merge-base --is-ancestor "${GITHUB_SHA}" "origin/main"; then
echo "Tag commit is not on origin/main; skipping release"
echo "should_release=false" >> "$GITHUB_OUTPUT"
exit 0
fi

echo "Tag push on main history detected: $ref_name"
echo "should_release=true" >> "$GITHUB_OUTPUT"
echo "release_tag=$ref_name" >> "$GITHUB_OUTPUT"
exit 0
fi

if [ "$event_name" = "push" ] && [ "$ref_name" = "main" ]; then
changed_files="$(git diff --name-only "${{ github.event.before }}" "${GITHUB_SHA}")"
if echo "$changed_files" | grep -q '^apps/threshold/package.json$'; then
version="$(node -p "require('./apps/threshold/package.json').version")"
echo "Release candidate merged to main; using tag v$version"
echo "should_release=true" >> "$GITHUB_OUTPUT"
echo "release_tag=v$version" >> "$GITHUB_OUTPUT"
else
echo "Push to main is not a release merge; skipping release"
echo "should_release=false" >> "$GITHUB_OUTPUT"
fi
exit 0
fi

echo "Unsupported release trigger context; skipping"
echo "should_release=false" >> "$GITHUB_OUTPUT"

build-desktop:
runs-on: ubuntu-24.04
needs: verify-tag-on-main
needs: verify-release-source
if: needs.verify-release-source.outputs.should_release == 'true'
container:
image: ghcr.io/${{ github.repository }}/ci-base:latest
credentials:
Expand Down Expand Up @@ -75,7 +116,8 @@ jobs:

build-android:
runs-on: ubuntu-24.04
needs: verify-tag-on-main
needs: verify-release-source
if: needs.verify-release-source.outputs.should_release == 'true'
container:
image: ghcr.io/${{ github.repository }}/ci-base:latest
credentials:
Expand Down Expand Up @@ -250,7 +292,8 @@ jobs:

publish-release:
runs-on: ubuntu-24.04
needs: [build-desktop, build-android]
needs: [verify-release-source, build-desktop, build-android]
if: needs.verify-release-source.outputs.should_release == 'true'
steps:
- name: Download desktop artefacts
uses: actions/download-artifact@v8
Expand All @@ -267,8 +310,8 @@ jobs:
- name: Publish GitHub release
uses: softprops/action-gh-release@v2.5.0
with:
tag_name: ${{ github.ref_name }}
name: Threshold ${{ github.ref_name }}
tag_name: ${{ needs.verify-release-source.outputs.release_tag }}
name: Threshold ${{ needs.verify-release-source.outputs.release_tag }}
generate_release_notes: true
files: |
release-assets/desktop/**/*
Expand Down
86 changes: 86 additions & 0 deletions RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,92 @@ This document tracks all releases of the Threshold application.

---

## Version 0.1.9

**Release Date:** March 2, 2026
**Status:** Released

> [!NOTE]
> This release delivers a full alarm ringing experience on Wear OS, cross-device dismiss and snooze coordination, and a ground-up rewrite of the release TUI using Ink.

### ✨ New Features

**Wear OS Ringing Screen**

- Full-screen Compose ringing UI with Material You gradient, breathing ring animations, and threshold indicator
- Foreground service with vibration, alarm audio via MediaPlayer, and partial wake lock
- Bidirectional control: dismissing or snoozing on either device dismisses both via event bus routing
- Disconnected fallback scheduling: watch fires alarms independently via local `AlarmManager.setAlarmClock()` when phone is out of Bluetooth range
- Offline fallback reconciliation for repeating alarms while connectivity remains unavailable
- Snooze duration sync from phone settings to watch persistence via DataItem payload
- Ring deduplication to prevent duplicate ringing when multiple messages arrive
- "Test Watch Ring" button in phone settings and "Test Ring" in watch settings
- Three design iteration mockups (soft-glow, Material+Liminal, standard buttons)

**Cross-Device Ringing Coordination**

- Phone ringing screen closes when watch stops or snoozes the active alarm
- Guard against duplicate close attempts when local actions and backend events race

**Unified Release TUI**

- Replaced the monolithic `update-release-version.mjs` (1,400+ lines) with a screen-based Ink (React for CLI) TUI in `tools/release-tui/`
- Full-screen alternate-screen mode with reactive terminal resize, pinned footer, and single-key hotkeys
- 10 screens: Version Bump, Custom Version, Review, Tag Conflict, Build Offer, Build Progress, Pre-flight Failure, Done, Release Log, and Help
- CI/non-interactive mode via `--ci` flags with `--bump`, `--redo`, `--build`, `--dry-run`, `--no-commit`, `--no-web-sync`
- Detailed specification document and 10 SVG screen mockups

### 🐛 Bug Fixes

- Fixed phone ringing UI not closing when watch dismisses or snoozes the alarm
- Fixed native alarm replay hydration: initialise Rust time-format and snooze state before marking alarm pipeline ready
- Fixed offline fallback reconciliation so repeating fallback alarms continue scheduling
- Fixed watch ringing action labels and time bloom presentation
- Fixed watch 24-hour fallback when phone time format is unknown
- Fixed ringing task reopening via watch recents
- Fixed phone and watch alarm stop/snooze state synchronisation
- Fixed phone ringing route alarms not reporting as fired for wear sync
- Resolved CI failures in Rust tests and Mermaid lint warnings
- Fixed release TUI reactive terminal resize and detail panel divider width
- Fixed release TUI repo root detection when invoked from a subpackage

### 🛠️ Build and Release

- Added tag-driven signed release build workflow for GitHub Actions
- Refreshed GitHub Actions versions across existing workflows
- Documented release build pipeline and signing setup
- Added commander CLI parsing to release TUI
- Added `tools/*` to `pnpm-workspace.yaml` and wired `pnpm version:release` to the new TUI

### 📚 Documentation

- Added Wear OS ringing implementation docs, work summary, and mockups
- Added notification action reliability write-up and blog doc links
- Resolved Mermaid lint warnings in sequence diagrams
- Added release TUI redesign spec and screen mockups
- Documented release build pipeline and signing infrastructure

### 📝 Technical Details

**Major PRs Merged (selected):**

- [#177](https://github.com/liminal-hq/threshold/pull/177) - Add Wear OS ringing screen with fallback scheduling and snooze sync
- [#181](https://github.com/liminal-hq/threshold/pull/181) - Close phone ringing screen when watch stops or snoozes alarm
- [#180](https://github.com/liminal-hq/threshold/pull/180) - Unified release TUI with Ink, replacing monolithic version script
- [#179](https://github.com/liminal-hq/threshold/pull/179) - Update notification plugin lockfile to rebased branch head

**Issues Resolved:**

- [#170](https://github.com/liminal-hq/threshold/issues/170)–[#176](https://github.com/liminal-hq/threshold/issues/176) - Wear OS ringing screen feature set

**Commit/Contributor Summary (`0.1.8` → `0.1.9`):**

- **Commits:** 45
- **Merged PRs:** 4
- **Contributors:** Scott Morris

---

## Version 0.1.8

**Release Date:** February 22, 2026
Expand Down
4 changes: 2 additions & 2 deletions apps/threshold-wear/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ android {
applicationId = "ca.liminalhq.threshold"
minSdk = 26
targetSdk = 34
versionCode = 1000001008
versionName = "0.1.8"
versionCode = 1000001009
versionName = "0.1.9"
}

signingConfigs {
Expand Down
2 changes: 1 addition & 1 deletion apps/threshold/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "threshold",
"private": true,
"version": "0.1.8",
"version": "0.1.9",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
2 changes: 1 addition & 1 deletion apps/threshold/src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://schema.tauri.app/config/2",
"productName": "Threshold",
"version": "0.1.8",
"version": "0.1.9",
"identifier": "ca.liminalhq.threshold",
"build": {
"beforeDevCommand": "pnpm dev",
Expand Down