Skip to content

Major Update: macOS Sequoia (15+) Support, Enhanced App Onboarding, and Tool Execution Security#7

Merged
f merged 9 commits into
f:mainfrom
emSoumik:feat/update-macos-app-and-release-ci
Mar 26, 2026
Merged

Major Update: macOS Sequoia (15+) Support, Enhanced App Onboarding, and Tool Execution Security#7
f merged 9 commits into
f:mainfrom
emSoumik:feat/update-macos-app-and-release-ci

Conversation

@emSoumik

@emSoumik emSoumik commented Mar 26, 2026

Copy link
Copy Markdown
Contributor

New Features & Changes Summary

This release focuses on hardening the security of MCP tool execution and streamlining the macOS onboarding experience with an accessibility-first model.


1. macOS App Permission Architecture (Major Refactor)

The app now prioritizes an Accessibility-first model, which is the actual entitlement required for UI automation, key injection, and AppleScript tasks.

New Components:

  • AccessibilityPermissionView.swift: Dedicated UI component for requesting Accessibility permissions with clear messaging and user-friendly prompts.
  • SetupView.swift: New onboarding flow view that prioritizes Accessibility-first permissions instead of Full Disk Access (FDA).

image

UI Changes and Permission Flow :

  • Dynamic Versioning: The About section now dynamically pulls the app version from the bundle (CFBundleShortVersionString), removing the need for hardcoded version strings in the UI.

  • Model Shift: Moved from an FDA-first assumption to an Accessibility-first model.
  • Live State Refresh: Added automatic permission state refresh whenever the macOS app regains focus (NSApp.applicationDidBecomeActive).
  • Enhanced Validation: Implemented native AXIsProcessTrusted() validation to verify automation access directly via the Accessibility API.

2. Cloud Backend & Permission Service

Security Enhancements:

  • Permission Service: Core backend utility (src/permission-service.js) for HMAC-signed tool approval, pattern-based whitelisting, and session state management.
  • Sandbox Integration: The terminal log now explicitly shows sandbox=os for risky tools executed under /usr/bin/sandbox-exec.
  • Testing Infrastructure: Added full suites for token TTL, validation logic, MCP server access policy, loop guards, and sandbox commands.

Modified Files:

  • src/app.js, src/agents.js, src/mcp-server.js: Integrated permission service gateways and removed unused variable bindings for a clean runtime state.

3. Release Workflow Stability (CI/CD)

Release Pipeline Updates:

  • Stable Runner: Switched GitHub Actions runner from macos-26 to macos-15 for improved stability.
  • Pinned Toolchain: Explicitly set developer path to /Applications/Xcode_16.4.app/Contents/Developer to avoid environment drift.
  • Resilient Homebrew Logic: Added a guard to Homebrew tap updates; steps now gracefully skip if TAP_REPO_TOKEN_HOMEBREW is missing, preventing CI failures in forks.

4. Documentation & Examples

  • README.md: Aligned permission requirements with the new Accessibility-first model.
  • docs/getting-started.md: Updated setup instructions for first-time users.
  • docs/macos-app.md: Refreshed architecture documentation with the new setup flow details.
  • docs/index.md: Fixed markdown formatting and reference consistency.

Final Validation

  • All 212 test cases passing.
  • Auto-Updating Version: Verified that the About section version pulls dynamically from Bundle.main.appVersion.
  • npm run lint and npm run lint:md exit without errors.

Replaces the previous Full Disk Access-centric onboarding with an
accessibility-first flow. The app now asks for Accessibility permission
before anything else, which is the actual entitlement required for
automation tasks (AppleScript, key injection, UI scripting).

Changes:
- Add dedicated AccessibilityPermissionView with step-by-step grant UI
- Add SetupView to sequence onboarding steps in a single coherent flow
- Refresh permission state on app-foreground (NSApp.applicationDidBecomeActive)
- Update GateService with cleaner permission state machine and state management
- Update SettingsView to reflect actual runtime permission state
- Update AboutView: The version info now dynamically pulls from the app
  bundle (CFBundleShortVersionString), ensuring the UI auto-updates whenever
  a new version is released without manual changes
- Update Info.plist, and project settings for correct entitlements
- Add src/permission-service.js: HMAC-signed approval tokens, per-session
  pattern whitelist, and safe/risky tool classification
- Add sandbox-exec integration in mcp-server.js: risky tools run inside
  macOS sandbox-exec when available; falls back gracefully when unavailable
- Add four new test files covering permission service, access policy, loop
  guards, and sandbox command behavior (97 + 40 + 57 + 18 test cases)

Technical summary:
Refactors macOS onboarding and permission checks to prioritize Accessibility
for automation tasks. Introduces dynamic versioning in the About section that
auto-updates with the bundle version.
The previous workflow targeted a rolling runner label that drifted to an
incompatible image, causing intermittent build failures unrelated to code.

Changes (.github/workflows/release.yml):
- Switch GitHub Actions runner from macos-latest / macos-26 to macos-15
- Explicitly select /Applications/Xcode_16.4.app/Contents/Developer via
  xcode-select to pin the toolchain and avoid implicit Xcode version changes
- Make the Homebrew tap update step conditional on HOMEBREW_TOKEN availability
- DMG upload and version-bump behavior unchanged

Technical summary:
Hardens release automation against runner drift. The runner and Xcode version
are now pinned to a known-good combination (macos-15 + Xcode 16.4).
Brings the repository to a clean lint state for both JS and Markdown so
every future PR starts from a green baseline.

Changes:
- Remove unused variable bindings and dead imports in src/agents.js,
  src/app.js, and example agent scripts
- Expand src/mcp-server.js with full run_command sandbox plumbing
- Fix markdown list-indentation and reference-link warnings across docs/
- Align docs wording with updated macOS permission behavior
- Add superpowers/ to .gitignore
- Change Screen Recording to Accessibility in README tool list
- and permissions notes
- Update sandbox policy string in GateService.swift
- to emphasize macOS sandbox-exec limitations
- Update getting-started.md to require Accessibility
- permission and guide through a Setup View with
- access mode selection
- Add a Setup section to macOS app docs
- and update the macOS Xcode requirement to 16+ (was 26+)
Fix a missing closing brace for the body computed property in
PopoverContent struct. The compiler was interpreting private member
variables as being in local scope, causing "Attribute private can only
be used in a non-local scope" errors across five properties.

Root cause: PopoverContent.body was unclosed, causing the following
private members (statusText, statusColor, modeChipTitle, etc.) to be
interpretted as local declarations inside the body closure.

Resolution: Added the missing closing brace after the onChange modifier
to properly terminate the body computed variable scope.
…pattern

Fix log clearing functionality that broke due to direct array mutation
not triggering Combine notifications.

Problem:
LogView.swift directly mutated the logs array with service.logs.removeAll(),
which does not trigger @published notifications. Once the array was cleared
this way, the reactive binding between the model and view became inconsistent,
making logs impossible to view properly.

Solution:
1. Add clearLogs() method to GateService that properly reassigns the array:
   logs = [] (reassignment triggers @published notification)
2. Update LogsView to call service.clearLogs() instead of mutating directly

This ensures that when users clear logs via the UI, the @published property
change is properly broadcast to all SwiftUI subscribers, maintaining reactive
consistency across the app.

Pattern note: Always reassign @published properties rather than mutating them
in-place to ensure Combine sends update notifications.
Copilot AI review requested due to automatic review settings March 26, 2026 08:01
@emSoumik emSoumik changed the title Major Update: Enhanced macOS App Onboarding and Tool Execution Security Major Update: macOS Sequoia (15+) Support, Enhanced App Onboarding, and Tool Execution Security Mar 26, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Hardens MCP tool execution with access modes, per-session approvals, command sandboxing, and loop guards, while refactoring the macOS menu bar app onboarding to an Accessibility-first setup and adding dynamic version display.

Changes:

  • Added server-side permission service with HMAC-based approvals + session whitelisting, plus MCP access policy modes (full/limited/sandbox) and sandbox-exec wrapping.
  • Added loop-guard for run_command, terminal preview logging, and a new network_speed tool.
  • Refactored macOS app UX: Setup view + permissions UI, access-mode selection, permission polling, and dynamic app version display; updated docs and release workflow.

Reviewed changes

Copilot reviewed 26 out of 28 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
test/permission-service.test.js Adds unit tests for risky tool classification, session whitelist behavior, and token validation/TTL.
test/mcp-server-sandbox-command.test.js Tests sandbox command wrapping/escaping behavior.
test/mcp-server-loop-guard.test.js Tests run_command loop suppression logic.
test/mcp-server-access-policy.test.js Tests access policy decisions across permission modes.
src/permission-service.js Introduces HMAC-based approval and per-session whitelisting utilities.
src/mcp-server.js Implements permission modes, access policy evaluation, sandbox wrapping, loop guard, approvals flow, and new network_speed tool.
src/app.js Minor runtime cleanup and changed notification text sent to agent.
src/agents.js Makes node_modules symlink creation more robust across platforms and fixes URL path handling.
package.json Adds node --test scripts.
examples/agents/screentime.24h.js Refactors exec usage (but introduces logic/type issues).
examples/agents/battery.30m.js Adjusts fs imports.
docs/macos-app.md Updates macOS app docs for new setup flow and Xcode requirement.
docs/index.md Fixes YAML/markdown formatting for the docs landing page.
docs/getting-started.md Updates onboarding instructions to Accessibility-first setup and setup view flow.
clients/Poke macOS Gate/Poke macOS Gate/SetupView.swift Adds new onboarding flow (access mode + permissions steps).
clients/Poke macOS Gate/Poke macOS Gate/SettingsView.swift Adds access mode selection and embeds Accessibility permission UI.
clients/Poke macOS Gate/Poke macOS Gate/Poke_macOS_GateApp.swift Adds Setup window, dynamic about version string, and terminal preview UI wiring.
clients/Poke macOS Gate/Poke macOS Gate/PermissionRowView.swift Adds reusable permission row component.
clients/Poke macOS Gate/Poke macOS Gate/LogsView.swift Routes “clear logs” through GateService API.
clients/Poke macOS Gate/Poke macOS Gate/Info.plist Adds CFBundleIdentifier entry.
clients/Poke macOS Gate/Poke macOS Gate/GateService.swift Adds permission mode + setup persistence, system permission checks/polling, and terminal preview parsing.
clients/Poke macOS Gate/Poke macOS Gate/AccessibilityPermissionView.swift Adds dedicated Accessibility permission prompting/status UI.
clients/Poke macOS Gate/Poke macOS Gate/AboutView.swift Adds dynamic app version from bundle.
clients/Poke macOS Gate/Poke macOS Gate.xcodeproj/project.pbxproj Updates deployment target, versioning, signing/team settings, and build options.
README.md Rewrites onboarding/docs to match new permission model and app UX.
.gitignore Ignores superpowers/.
.github/workflows/release.yml Stabilizes CI runner + pins Xcode path + makes tap update optional in forks.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/mcp-server.js
Comment on lines +289 to +314
export function evaluateAccessPolicy(toolName, cleanArgs, mode = PERMISSION_MODE) {
if (mode === "full") return null;

if (mode === "limited") {
if (SAFE_TOOL_NAMES.has(toolName)) return null;
if (toolName === "run_command") {
return validateRunCommandAgainstAllowlist(cleanArgs.command, LIMITED_RUN_COMMANDS);
}
if (toolName === "write_file" || toolName === "take_screenshot") {
return "This tool is disabled in Limited Permissions mode.";
}
return "This tool is not permitted in Limited Permissions mode.";
}

if (SAFE_TOOL_NAMES.has(toolName)) return null;

if (toolName === "run_command") {
return validateRunCommandAgainstAllowlist(cleanArgs.command, SANDBOX_RUN_COMMANDS);
}

if (toolName === "write_file" || toolName === "take_screenshot") {
return "This tool is disabled in Sandbox mode.";
}

return "This tool is not permitted in Sandbox mode.";
}

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new "network_speed" tool is treated as a SAFE tool (bypasses approval and mode restrictions), but it executes a shell script via runCommand and forces permissionMode: "full" (unsandboxed). This is an access-policy bypass in both Limited and Sandbox modes. Make "network_speed" either (a) a risky tool requiring the same approval + mode policy checks, or (b) implement it without invoking a shell (e.g., direct HTTP requests) and, if still using runCommand, ensure it honors PERMISSION_MODE (apply OS sandbox when in sandbox mode) and is denied/limited appropriately in limited mode.

Copilot uses AI. Check for mistakes.
Comment thread src/mcp-server.js

const cmd = parts.join(" && ");

return runCommand(cmd, homedir(), { permissionMode: "full" }).then((result) => {

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new "network_speed" tool is treated as a SAFE tool (bypasses approval and mode restrictions), but it executes a shell script via runCommand and forces permissionMode: "full" (unsandboxed). This is an access-policy bypass in both Limited and Sandbox modes. Make "network_speed" either (a) a risky tool requiring the same approval + mode policy checks, or (b) implement it without invoking a shell (e.g., direct HTTP requests) and, if still using runCommand, ensure it honors PERMISSION_MODE (apply OS sandbox when in sandbox mode) and is denied/limited appropriately in limited mode.

Suggested change
return runCommand(cmd, homedir(), { permissionMode: "full" }).then((result) => {
return runCommand(cmd, homedir(), { permissionMode: PERMISSION_MODE }).then((result) => {

Copilot uses AI. Check for mistakes.
Comment thread src/permission-service.js
Comment on lines +76 to +97
validateApprovalToken(sessionId, token, toolName, toolArgs) {
const record = this.pendingApprovals.get(token);
if (!record) return false;
if (record.consumed) return false;

if (nowMs(this.now) > record.expiresAt) {
this.pendingApprovals.delete(token);
return false;
}

const argsHash = hashArgs(toolArgs);
if (
record.sessionId !== sessionId ||
record.toolName !== toolName ||
record.argsHash !== argsHash
) {
return false;
}

record.consumed = true;
return true;
}

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consumed approvals are never removed from pendingApprovals (only marked consumed: true). Over time this can cause unbounded in-memory growth for long-running servers (especially if agents request approval frequently). Consider deleting the token record on successful validation (instead of keeping it consumed), and adding periodic pruning of expired tokens (since tokens that are never validated won’t be deleted).

Copilot uses AI. Check for mistakes.
Comment thread src/mcp-server.js
Comment on lines +401 to +418
function buildSandboxProfile() {
const userHome = homedir();
return [
"(version 1)",
"(deny default)",
"(import \"system.sb\")",
"(allow process-exec)",
"(allow process-fork)",
"(allow file-read*)",
"(allow network-outbound)",
"(allow sysctl-read)",
"(allow file-write*",
` (subpath "${userHome}/Downloads")`,
" (subpath \"/private/tmp\")",
" (subpath \"/tmp\")",
")",
].join("\n");
}

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sandbox profile enables (allow file-read*) without any path constraints, which largely defeats using OS sandboxing as a meaningful containment boundary (commands can read arbitrary files). If the goal is to reduce blast radius in Sandbox mode, constrain reads to specific subpaths (e.g., user home, project/work directories, Downloads, tmp) and explicitly allow only what's needed by the allowlisted commands. If broad reads are required, document that sandbox mode primarily constrains writes/exec/network rather than read access.

Copilot uses AI. Check for mistakes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot open a new pull request to apply changes based on this feedback

Comment on lines +197 to +202
stopPermissionPolling()
permissionPollingTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
Task { @MainActor [weak self] in
self?.refreshSystemPermissions()
}
}

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Permission polling every 1s is likely to run continuously for the lifetime of the menu bar app (and it’s started from multiple places). This can cause unnecessary wakeups and battery impact. Prefer an event-driven refresh (e.g., refresh on NSApp.applicationDidBecomeActive / foreground transitions) and/or a slower polling interval only while the Setup/Settings UI is visible.

Suggested change
stopPermissionPolling()
permissionPollingTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
Task { @MainActor [weak self] in
self?.refreshSystemPermissions()
}
}
// Ensure any existing timer is stopped before starting a new one.
stopPermissionPolling()
// Refresh immediately so UI reflects the latest permission state.
refreshSystemPermissions()
// If there are no missing permissions, there is nothing to poll for.
guard !missingSystemPermissions.isEmpty else { return }
// Poll less frequently and stop automatically once all permissions are granted,
// to avoid continuous high-frequency wakeups for the lifetime of the app.
let timer = Timer.scheduledTimer(withTimeInterval: 5.0, repeats: true) { [weak self] _ in
Task { @MainActor [weak self] in
guard let self = self else { return }
self.refreshSystemPermissions()
// Stop polling once all required permissions have been granted.
if self.missingSystemPermissions.isEmpty {
self.stopPermissionPolling()
}
}
}
// Allow some flexibility for the system to coalesce timer firings and save power.
timer.tolerance = 1.0
permissionPollingTimer = timer

Copilot uses AI. Check for mistakes.
Comment on lines +20 to +23
execSync(`defaults read com.apple.ScreenTimeAgent 2>/dev/null || echo "{}"`, {
encoding: "utf-8",
timeout: 10000,
}).trim();

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In getScreenTime(), the defaults read ... output is now discarded (previously stored in result), which likely breaks any later parsing/logic that depended on it. In getActiveApps(), the function now returns a trimmed string instead of an array (it previously returned result.split(\", \")), creating a type mismatch with the catch { return []; } path and any callers expecting an array.

Copilot uses AI. Check for mistakes.
Comment on lines +45 to 48
return execSync(`osascript -e '${script}'`, {
encoding: "utf-8",
timeout: 10000,
}).trim();

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In getScreenTime(), the defaults read ... output is now discarded (previously stored in result), which likely breaks any later parsing/logic that depended on it. In getActiveApps(), the function now returns a trimmed string instead of an array (it previously returned result.split(\", \")), creating a type mismatch with the catch { return []; } path and any callers expecting an array.

Copilot uses AI. Check for mistakes.
Comment thread src/app.js
`You can now run commands, read and write files, list directories, take screenshots, and check system info on my machine. ` +
`Just use the tools whenever I ask you to do something on my computer.` +
`Now reply me in my language "now I am connected to your computer".`
`Now reply me with "now I am connected to your computer" but everytime write those replies in most creativev fun way.`

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct spelling of 'creativev' to 'creative' (and consider rewriting this sentence for clarity, since it changes agent behavior in a way unrelated to onboarding/security).

Copilot uses AI. Check for mistakes.
<plist version="1.0">
<dict>
<key>CFBundleIdentifier</key>
<string>dev.fka.Poke-macOS-Gate</string>

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardcoding CFBundleIdentifier in Info.plist can drift from Xcode’s PRODUCT_BUNDLE_IDENTIFIER and makes multi-target/config builds harder. Prefer setting CFBundleIdentifier to $(PRODUCT_BUNDLE_IDENTIFIER) and keeping the identifier value in the project build settings.

Suggested change
<string>dev.fka.Poke-macOS-Gate</string>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>

Copilot uses AI. Check for mistakes.
Comment thread README.md Outdated
### Building from source

Requires macOS 15+ and Xcode 26+.
Want to customize it? You'll need macOS 15+ and Xcode 15+:

Copilot AI Mar 26, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Docs are inconsistent about the required Xcode version: README says Xcode 15+, but docs/macos-app.md says Xcode 16+. Align these requirements (and ideally tie them to the deployment target / toolchain actually used in CI, e.g. Xcode 16.4 in the release workflow).

Suggested change
Want to customize it? You'll need macOS 15+ and Xcode 15+:
Want to customize it? You'll need macOS 15+ and Xcode 16+:

Copilot uses AI. Check for mistakes.
@emSoumik

Copy link
Copy Markdown
Contributor Author

@copilot open a new pull request to apply changes based on the comments in this thread

@f

f commented Mar 26, 2026

Copy link
Copy Markdown
Owner

@emSoumik I've released 0.1.9 which includes "Start Poke Gate at login" feature. Can you update your PR with latest update?

@f

f commented Mar 26, 2026

Copy link
Copy Markdown
Owner

Also if we want to make it available for macOS 15x, we need to create 2 macOS build for 26 and 15 both. Otherwise it'll create the app in 15 design which look bad on macOS Liquid Glass.

@emSoumik emSoumik force-pushed the feat/update-macos-app-and-release-ci branch from fad8623 to dca8065 Compare March 26, 2026 10:15
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = RJA7656U34;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 4KW2PB5PGF;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you remove this line or emppty? since we're distributing it without notarization we can skip it.

CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = RJA7656U34;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 4KW2PB5PGF;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just like above.

MACOSX_DEPLOYMENT_TARGET = 26.0;
MARKETING_VERSION = 0.1.9;
MACOSX_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 0.1.8;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0.1.9

MACOSX_DEPLOYMENT_TARGET = 26.0;
MARKETING_VERSION = 0.1.9;
MACOSX_DEPLOYMENT_TARGET = 15.0;
MARKETING_VERSION = 0.1.8;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0.1.9

Comment thread README.md Outdated
---

Run Poke Gate on your Mac, then message Poke from iMessage, Telegram, or SMS to run commands, read files, take screenshots, and more — all on your machine.
## Just ask Poke from your phone

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it doesn't have to be a phone.

Comment thread README.md Outdated
## Quick Start

### 1. Get an API key
Visit [poke.com/kitchen/api-keys](https://poke.com/kitchen/api-keys) and grab one.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

api key is not needed. it works with npx poke login. it used to be a requirement before.

Comment thread README.md Outdated
Poke Gate on your Mac runs it locally
Result comes back to your chat

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do not remove marmaid.

@f

f commented Mar 26, 2026

Copy link
Copy Markdown
Owner

Feels like README changes are unnecessary. Can you discard README file changes? I'll update it later.

@emSoumik

emSoumik commented Mar 26, 2026

Copy link
Copy Markdown
Contributor Author

Feels like README changes are unnecessary. Can you discard README file changes? I'll update it later.

Done! Commit 3e24208 reverts the README to the upstream version and drops those changes as requested.

Split release workflow into verify + release stages. Verify now runs on both
macOS-15 and macOS-26 to catch compatibility issues early. Release stage uses
Xcode 26 with dynamic runner selection.

### Verification Stage
- New `verify` job: builds on macOS-15 and macOS-26 in parallel
- Validates Xcode 26 availability with fallback error handling
- Early failure prevents release if build fails

### Release Stage
- Migrated from hardcoded `Xcode_16.4` to dynamic `Xcode_26` selection
- Added `.app` glob pattern to future-proof against minor version updates
- Depends on verify job passing
Per review feedback, README changes are unnecessary to this PR. Reverting
to upstream/main version to keep focus on core functionality updates: macOS
app UI refactoring and CI/CD pipeline modernization.
@emSoumik

Copy link
Copy Markdown
Contributor Author

Also if we want to make it available for macOS 15x, we need to create 2 macOS build for 26 and 15 both. Otherwise it'll create the app in 15 design which look bad on macOS Liquid Glass.

Addressed this in the 229d6c5

This does not add two separate app builds. Instead, it keeps one app with a version-aware UI path:

  • On macOS 26, the app is built and released from the macos-26 lane, so it can use the newer Liquid Glass-era system styling.
  • On macOS 15, the same app still runs because the deployment target stays at 15.0, but the UI falls back to the simpler macOS 15-friendly chrome.

So the fix here is not "ship two downloads." It is "ship one adaptive app":

  • build and release with the newer toolchain,
  • detect the OS at runtime,
  • use the lighter native styling on macOS 26,
  • fall back to the older surface treatment on macOS 15.

I also updated CI so we verify the app on both macos-15 and macos-26, while only publishing once from the macOS 26 release lane. That gives us macOS 15 compatibility without locking the app into the older visual style everywhere.

@f f merged commit f0371c2 into f:main Mar 26, 2026
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.

3 participants