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
102 changes: 0 additions & 102 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ on:
branches: [main]
pull_request:
branches: [main]
# Desktop installers are expensive — run them on demand from the Actions tab.
workflow_dispatch:

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}
Expand All @@ -29,103 +27,3 @@ jobs:
- run: pnpm rebuild better-sqlite3
- run: pnpm -r typecheck
- run: pnpm test

desktop:
name: Desktop ${{ matrix.platform }}
needs: test
if: github.event_name == 'workflow_dispatch'
strategy:
fail-fast: false
matrix:
include:
- platform: ubuntu-22.04
args: --target x86_64-unknown-linux-gnu
rust_target: x86_64-unknown-linux-gnu
artifact: desktop-linux-x86_64
- platform: macos-latest
args: --target aarch64-apple-darwin
rust_target: aarch64-apple-darwin
artifact: desktop-macos-aarch64
- platform: windows-latest
args: --target x86_64-pc-windows-msvc
rust_target: x86_64-pc-windows-msvc
artifact: desktop-windows-x86_64
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v7
- uses: pnpm/action-setup@v6
with:
version: 10.33.3
- uses: actions/setup-node@v7
with:
node-version: 22
cache: pnpm
- name: Install Linux WebKit deps
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.rust_target }}
- uses: Swatinem/rust-cache@v2
with:
workspaces: apps/desktop/src-tauri -> target
- run: pnpm install --frozen-lockfile
- run: pnpm rebuild better-sqlite3
- name: Bundle API sidecar + Node
run: node apps/desktop/scripts/bundle-api-sidecar.mjs --node
env:
PRM_BUNDLE_NODE: "1"
- uses: tauri-apps/tauri-action@v0
id: tauri
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
projectPath: apps/desktop
args: ${{ matrix.args }}
includeUpdaterJson: false
- name: Smoke API sidecar
shell: bash
run: |
set -euo pipefail
SIDE="apps/desktop/src-tauri/sidecars"
UI="apps/desktop/src-tauri/ui-dist"
if [[ "${{ runner.os }}" == "Windows" ]]; then
NODE="$SIDE/node.exe"
else
NODE="$SIDE/node"
chmod +x "$NODE" || true
fi
test -f "$NODE"
test -f "$SIDE/prm-api.mjs"
test -f "$UI/index.html"
DATA="${RUNNER_TEMP:-/tmp}/prm-sidecar-smoke"
rm -rf "$DATA"
mkdir -p "$DATA"
PRM_DATA_DIR="$DATA" PRM_PORT=8799 PRM_LAUNCH_TOKEN=ci-smoke PRM_UI_DIR="$UI" \
"$NODE" "$SIDE/prm-api.mjs" >"$DATA/out.log" 2>&1 &
PID=$!
cleanup() { kill "$PID" 2>/dev/null || true; }
trap cleanup EXIT
for i in $(seq 1 50); do
if curl -sf "http://127.0.0.1:8799/api/health" >/dev/null; then
echo "sidecar healthy"
curl -sf "http://127.0.0.1:8799/api/workspace/status" -H "x-prm-launch-token: ci-smoke"
echo
curl -sf "http://127.0.0.1:8799/" | head -c 200
echo
exit 0
fi
sleep 0.2
done
echo "sidecar failed to become healthy" >&2
cat "$DATA/out.log" >&2 || true
exit 1
- name: Upload desktop installers
uses: actions/upload-artifact@v7
with:
name: ${{ matrix.artifact }}
path: apps/desktop/src-tauri/target/${{ matrix.rust_target }}/release/bundle/
if-no-files-found: error
retention-days: 30
168 changes: 168 additions & 0 deletions .github/workflows/desktop-installers.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
name: Desktop Installers

# Manual-only Tauri installer builds. Default matrix is Windows + Linux;
# choose "all" (or macOS alone) from the Actions UI when needed.
on:
workflow_dispatch:
inputs:
platforms:
description: Platforms to build
type: choice
options:
- windows-and-linux
- windows
- linux
- macos
- all
default: windows-and-linux

concurrency:
group: desktop-installers-${{ github.ref }}-${{ inputs.platforms }}
cancel-in-progress: true

jobs:
select-matrix:
name: Select platforms
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set.outputs.matrix }}
steps:
- id: set
shell: bash
run: |
set -euo pipefail
case "${{ inputs.platforms }}" in
windows-and-linux)
keys='["linux","windows"]'
;;
windows)
keys='["windows"]'
;;
linux)
keys='["linux"]'
;;
macos)
keys='["macos"]'
;;
all)
keys='["linux","macos","windows"]'
;;
*)
echo "Unknown platforms input: ${{ inputs.platforms }}" >&2
exit 1
;;
esac

# Full matrix definitions; jq filters to the selected OS keys.
all='[
{
"os_key": "linux",
"platform": "ubuntu-22.04",
"args": "--target x86_64-unknown-linux-gnu",
"rust_target": "x86_64-unknown-linux-gnu",
"artifact": "desktop-linux-x86_64"
},
{
"os_key": "macos",
"platform": "macos-latest",
"args": "--target aarch64-apple-darwin",
"rust_target": "aarch64-apple-darwin",
"artifact": "desktop-macos-aarch64"
},
{
"os_key": "windows",
"platform": "windows-latest",
"args": "--target x86_64-pc-windows-msvc",
"rust_target": "x86_64-pc-windows-msvc",
"artifact": "desktop-windows-x86_64"
}
]'
matrix="$(jq -c --argjson keys "$keys" '{ include: [.[] | select(.os_key as $k | $keys | index($k) != null)] }' <<<"$all")"
echo "matrix=$matrix" >> "$GITHUB_OUTPUT"
echo "Selected matrix: $matrix"

desktop:
name: Desktop ${{ matrix.platform }}
needs: select-matrix
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.select-matrix.outputs.matrix) }}
runs-on: ${{ matrix.platform }}
steps:
- uses: actions/checkout@v7
- uses: pnpm/action-setup@v6
with:
version: 10.33.3
- uses: actions/setup-node@v7
with:
node-version: 22
cache: pnpm
- name: Install Linux WebKit deps
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.rust_target }}
- uses: Swatinem/rust-cache@v2
with:
workspaces: apps/desktop/src-tauri -> target
- run: pnpm install --frozen-lockfile
- run: pnpm rebuild better-sqlite3
- name: Bundle API sidecar + Node
run: node apps/desktop/scripts/bundle-api-sidecar.mjs --node
env:
PRM_BUNDLE_NODE: "1"
- uses: tauri-apps/tauri-action@v0
id: tauri
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
projectPath: apps/desktop
args: ${{ matrix.args }}
includeUpdaterJson: false
- name: Smoke API sidecar
shell: bash
run: |
set -euo pipefail
SIDE="apps/desktop/src-tauri/sidecars"
UI="apps/desktop/src-tauri/ui-dist"
if [[ "${{ runner.os }}" == "Windows" ]]; then
NODE="$SIDE/node.exe"
else
NODE="$SIDE/node"
chmod +x "$NODE" || true
fi
test -f "$NODE"
test -f "$SIDE/prm-api.mjs"
test -f "$UI/index.html"
DATA="${RUNNER_TEMP:-/tmp}/prm-sidecar-smoke"
rm -rf "$DATA"
mkdir -p "$DATA"
PRM_DATA_DIR="$DATA" PRM_PORT=8799 PRM_LAUNCH_TOKEN=ci-smoke PRM_UI_DIR="$UI" \
"$NODE" "$SIDE/prm-api.mjs" >"$DATA/out.log" 2>&1 &
PID=$!
cleanup() { kill "$PID" 2>/dev/null || true; }
trap cleanup EXIT
for i in $(seq 1 50); do
if curl -sf "http://127.0.0.1:8799/api/health" >/dev/null; then
echo "sidecar healthy"
curl -sf "http://127.0.0.1:8799/api/workspace/status" -H "x-prm-launch-token: ci-smoke"
echo
curl -sf "http://127.0.0.1:8799/" | head -c 200
echo
exit 0
fi
sleep 0.2
done
echo "sidecar failed to become healthy" >&2
cat "$DATA/out.log" >&2 || true
exit 1
- name: Upload desktop installers
uses: actions/upload-artifact@v7
with:
name: ${{ matrix.artifact }}
path: apps/desktop/src-tauri/target/${{ matrix.rust_target }}/release/bundle/
if-no-files-found: error
retention-days: 30
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Performance Review Manager — a local, single-user ("solo EM") pnpm monorepo. S
- `@prm/api` (`apps/api`) — Hono + better-sqlite3 backend on `http://127.0.0.1:8787`. Run with `pnpm dev:api` (uses `tsx watch`, no strict typecheck).
- `@prm/ui` (`apps/ui`) — React + Vite frontend on `http://localhost:5173`, proxies `/api` → the API. Run with `pnpm dev:ui`.
- `pnpm dev` runs both in parallel — this is the primary dev entrypoint.
- `@prm/desktop` (`apps/desktop`) — Tauri shell. `desktop:dev`/`desktop:build` need Rust + system WebView libraries and are not validated in the headless cloud VM. Packaged desktop CI installers run only via Actions **Run workflow** (not on every merge).
- `@prm/desktop` (`apps/desktop`) — Tauri shell. `desktop:dev`/`desktop:build` need Rust + system WebView libraries and are not validated in the headless cloud VM. Packaged desktop installers run only via Actions → **Desktop Installers** → **Run workflow** (not on every merge; defaults to Windows + Linux).

### Package manager (important, non-obvious)
- This repo requires **pnpm 10.33.3**. The VM's default corepack resolves pnpm 11, which (a) rejects this committed lockfile via a `minimumReleaseAge` supply-chain policy and (b) ignores the `pnpm.onlyBuiltDependencies` field in `package.json` that is needed to build the `better-sqlite3` native module. The update script pins 10.33.3 via `corepack prepare [email protected] --activate`; keep using pnpm 10.x. If native `better-sqlite3` is missing, run `pnpm rebuild better-sqlite3`.
Expand Down
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Production `pnpm build` may still have packaging rough edges; typecheck + tests

## Desktop

Desktop installers are **not** built on every merge. Use Actions → **CI** → **Run workflow**. See `apps/desktop/README.md`.
Desktop installers are **not** built on every merge. Use Actions → **Desktop Installers** → **Run workflow** (defaults to Windows + Linux). See `apps/desktop/README.md`.

## Security

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ pnpm desktop:dev # native window (needs WebView + Rust)
pnpm desktop:build
```

Desktop installers are built on demand: Actions → **CI** → **Run workflow** (uploads `desktop-windows-x86_64`, `desktop-macos-aarch64`, `desktop-linux-x86_64`). Merges to `main` only run unit tests + typecheck. Details: [`apps/desktop/README.md`](./apps/desktop/README.md).
Desktop installers are built on demand: Actions → **Desktop Installers** → **Run workflow** (defaults to Windows + Linux; uploads `desktop-windows-x86_64` / `desktop-linux-x86_64`, optional macOS). Merges to `main` only run unit tests + typecheck. Details: [`apps/desktop/README.md`](./apps/desktop/README.md).

### Cursor MCP (bulk load)

Expand Down Expand Up @@ -244,7 +244,7 @@ Regenerate with `pnpm screenshots` (UI + API running; demo password `workbench`)
- **Sprint pulse** for date-window coaching
- **Eight accents × dark/light** appearance system (Coral, Amber, Magenta, Citrus plus Teal, Ocean, Forest, Slate)
- **AI**: digests, section draft, peer synthesize, chat, with citation allowlist
- **Desktop** packaging (Tauri); installers via Actions **Run workflow**
- **Desktop** packaging (Tauri); installers via Actions → **Desktop Installers** → **Run workflow**
- **Delete / recreate workspace** from Unlock or Settings

## Docs
Expand Down
2 changes: 1 addition & 1 deletion SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ What PRM does **not** claim today:
- Automatic refusal of Dropbox / iCloud / OneDrive data directories (warn when detected; sync folders remain risky)
- Cryptographic anonymity for peer feedback
- Tamper-proof legal custody of the activity log (hash-chained and HMAC-exportable, but the EM can re-sign after unlock)
- Signed auto-update for desktop installers (manual CI artifacts; verify checksums yourself)
- Signed auto-update for desktop installers (manual **Desktop Installers** artifacts; verify checksums yourself)

Details: [`docs/architecture/TRUST_MODEL.md`](./docs/architecture/TRUST_MODEL.md).

Expand Down
5 changes: 3 additions & 2 deletions apps/desktop/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Settings → **Check for updates** compares the packaged version (`get_app_versi

Packaged data lives in the OS app-data directory (for example `%APPDATA%\com.prm.workbench` on Windows). Sidecar stdout/stderr is appended to `desktop-api.log` there for debugging startup failures.

CI builds the desktop matrix (Ubuntu / macOS / Windows) only when you **manually run** the **CI** workflow from the Actions tab (`workflow_dispatch`). Merges and PRs still run unit tests only. Each green desktop job uploads an Actions artifact (retained 30 days):
CI (`ci.yml`) runs unit tests + typecheck only. Desktop installers are built on demand via the **Desktop Installers** workflow (`workflow_dispatch`). Default platform choice is **windows-and-linux**; the Actions UI also offers Windows-only, Linux-only, macOS, or all three. Each green job uploads an Actions artifact (retained 30 days):

| Artifact name | Typical contents |
|---------------|------------------|
Expand All @@ -59,8 +59,9 @@ CI builds the desktop matrix (Ubuntu / macOS / Windows) only when you **manually
Download from the run’s **Artifacts** section in the GitHub UI, or:

```bash
gh workflow run "Desktop Installers" -f platforms=windows-and-linux
gh run download <run-id> -n desktop-windows-x86_64
# also: desktop-macos-aarch64, desktop-linux-x86_64
# also: desktop-linux-x86_64, desktop-macos-aarch64
```

## Icons
Expand Down
2 changes: 1 addition & 1 deletion docs/CORE_LOOP_EVAL.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ upload role R&R → tag people → year-round dossier
| Promotion packets | Done | Next-role marks + AI case draft |
| Backfill + MCP bulk load | Done | Thin = ach+fb+docs+goals &lt; 2; MCP needs explicit unlock / `PRM_PASSWORD` |
| Settings → AI | Done | Private notes / meeting / document extract egress **enforced** on cloud paths |
| Desktop CSP / launch token / API sidecar | Done | CSP set; shell IPC removed; packaged Node + `prm-api.mjs` spawned by Tauri; desktop installers via Actions **Run workflow** (not every merge) |
| Desktop CSP / launch token / API sidecar | Done | CSP set; shell IPC removed; packaged Node + `prm-api.mjs` spawned by Tauri; desktop installers via Actions → **Desktop Installers** → **Run workflow** (not every merge) |

## Remaining gaps (honest)

Expand Down
2 changes: 1 addition & 1 deletion docs/DELIVERY.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Aligned to [TRUST_MODEL.md](./architecture/TRUST_MODEL.md).
- ActivityEvent hash chain + HMAC export
- Encrypted workspace backup/restore
- Synthetic **8-person** demo seed (rolling relative cycle dates)
- CI: unit tests on every `main` PR; desktop installers on manual `workflow_dispatch`
- CI: unit tests on every `main` PR; desktop installers on manual **Desktop Installers** `workflow_dispatch`

**Still open (do not claim done)**
- OS keychain for API keys
Expand Down
2 changes: 1 addition & 1 deletion docs/architecture/LOCAL_STACK.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Primary target: installable app on **Linux, Windows, macOS**. No Docker/Postgres

| Layer | Choice | Notes |
|-------|--------|-------|
| Shell | **Tauri 2** | dmg / msi|NSIS / AppImage|deb (manual CI `workflow_dispatch`) |
| Shell | **Tauri 2** | dmg / msi|NSIS / AppImage|deb (manual **Desktop Installers** `workflow_dispatch`) |
| UI | **React + TypeScript + Vite** | |
| Backend (shipped) | **Node Hono sidecar** on `127.0.0.1` + session token; desktop adds **per-launch capability token** | Prefer migrating privileged ops to Tauri IPC over time |
| Backend (target) | Tauri IPC commands for DB/FS/HTTP without a long-lived TCP sidecar | Aspirational |
Expand Down
2 changes: 1 addition & 1 deletion docs/architecture/TRUST_MODEL.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ No anonymity theater: imported peer text is attributed to the named peer for the
### Not shipped yet (do not market as done)
- OS keychain custody for API keys (keys ride the workspace secret today)
- SQLCipher / Rusqlite-in-Rust as the primary DB path (Node `better-sqlite3` + file seal is what runs)
- Signed auto-update / notarized installers (desktop artifacts are manual CI builds)
- Signed auto-update / notarized installers (desktop artifacts are manual **Desktop Installers** builds)
- Background jobs when the app is closed

See [LOCAL_STACK.md](./LOCAL_STACK.md) and [SYSTEM.md](./SYSTEM.md).