Skip to content

TIDY-453 - Swift apps in monorepo#549

Open
thyaravind wants to merge 1 commit intodevfrom
TIDY-453
Open

TIDY-453 - Swift apps in monorepo#549
thyaravind wants to merge 1 commit intodevfrom
TIDY-453

Conversation

@thyaravind
Copy link
Copy Markdown
Member

@thyaravind thyaravind commented Apr 24, 2026

Summary by cubic

Adds the Memotron and Nucleum SwiftUI apps (iOS + macOS) to the monorepo with whisper.cpp integration for on-device transcription. Implements TIDY-453 and adds small web updates for native embedding and product resource cleanup.

  • New Features

    • Added ios/memotron and ios/nucleum projects with iOS and macOS targets, SwiftUI apps, entitlements, and app icons.
    • Integrated whisper.cpp via local vendor at ios/vendor/whisper.cpp and whisper.xcframework with Objective‑C++ wrapper and bridging header.
    • Registered URL schemes: memotron: and nucleum:.
    • Web updates: gate PosthogTelemetry behind VITE_NATIVE_EMBED, switched Login.svelte to $state, minor calendar panel fix, and trimmed Product.NUCLEUS browse/resources (removed combination).
    • .gitignore now ignores iOS build artifacts and local vendor directories.
  • Migration

    • Run: npm run setup:ios:whisper (builds local whisper.cpp and XCFramework).
    • In Xcode, ensure whisper.xcframework is added and set to “Embed & Sign”.
    • For native-embedded web, set VITE_NATIVE_EMBED="true" to disable Posthog in the client.

Written for commit 23ec91c. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Native iOS and macOS apps for Nucleum, Memotron, and Pointron with full feature parity
    • Audio transcription capability powered by Whisper
    • iOS home screen widgets for session and targets tracking
    • Custom URL scheme support for deep linking and app integration
    • Background audio support for iOS applications
  • Bug Fixes

    • Fixed calendar rendering logic for display selection
    • Updated resource icons for consistency
    • Improved resource browsing configuration
  • Documentation

    • Added Whisper integration guides for iOS setup and Core ML acceleration

@linear
Copy link
Copy Markdown

linear Bot commented Apr 24, 2026

@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 24, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
memotron Ready Ready Preview, Comment Apr 24, 2026 4:06am
nucleus Error Error Apr 24, 2026 4:06am
pointron Ready Ready Preview, Comment Apr 24, 2026 4:06am

Copy link
Copy Markdown
Contributor

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Sorry @thyaravind, your pull request is larger than the review limit of 150000 diff characters

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 24, 2026

📝 Walkthrough

Walkthrough

This pull request introduces comprehensive iOS native support across three new native applications (Nucleum, Memotron, Pointron) with a shared NucleumNativeKit framework, complete Xcode project configurations, app lifecycle management, audio transcription integration via Whisper C++, Core Data persistence, WidgetKit extensions, and build automation tooling for iOS web bundle generation.

Changes

Cohort / File(s) Summary
Web Client Refactoring
client/components/account/Login.svelte, client/components/calendar/Calendar.svelte, client/components/flux/resourceStores/resource.utils.ts, client/layout/layers/BaseLayer.svelte, client/products/product.config.ts
Minor updates: Svelte state migration in Login, Calendar rendering correction (Bird/Classic swap), icon mapping change (event → calendar-blank), conditional PosthogTelemetry wrapping, and Product.NUCLEUS/MEMOTRON resource configuration cleanup.
iOS Xcode Project Infrastructure
ios/*/Nucleum.xcodeproj/*, ios/*/Memotron.xcodeproj/*, ios/*/Pointron.xcodeproj/*
Three complete Xcode project configurations (project.pbxproj, workspace, schemes) defining build targets, frameworks, resource bundling, code signing, and entitlements for iOS and macOS variants of each app.
iOS Nucleum/Memotron App Files
ios/nucleum/Nucleum/*, ios/memotron/Memotron/*, ios/nucleum/Nucleum for Mac/*, ios/memotron/Memotron for Mac/*
App entry points (NucleumApp/MemotronApp, ContentView, MacContent), entitlements, Info.plist URL scheme registration, asset catalogs, and Whisper C++ integration (bridging headers, wrapper interfaces).
iOS Pointron App & Widgets
ios/pointron/Pointron/*, ios/pointron/PointronWidgetExtension/*
App structure with focus-session tracking, WidgetKit extensions (CurrentSessionWidget, TargetsWidget, LiveActivity), widget data models, app-group data sharing, and SurrealDB integration for session/target data.
Shared NucleumNativeKit Framework
ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/*.swift
Core services and utilities: AIService (audio transcription with Whisper.cpp and Core ML), AppStore (message routing, subscription management, location services), JobManager (background job orchestration), Core Data stack, and platform-agnostic Swift models/protocols.
iOS UI Components
ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/*.swift
SwiftUI views for iOS/macOS: BaseView/BaseViewForMac (web integration and messaging), Modal/Offline/SafariViewModal, AudioTranscriptionView, LoadingOverlay, CameraTestView, and platform-specific authentication views.
Web View Integration
ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/WebView/*.swift
Custom WKWebView implementations (WebViewTwo, WebViewForMac) with custom URL scheme handling, JavaScript message bridging, keyboard management, and coordination between native and web layers.
Build Automation Tools
tools/ios/build-web-bundle.mjs, tools/ios/setup-whisper.mjs, package.json
New npm scripts and Node.js build utilities for iOS web bundle generation (build:ios:*) and Whisper.cpp xcframework setup (setup:ios:whisper).

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Poem

🐰 Hop, hop, hop! iOS dreams take flight,
Three apps bloom under Xcode's light,
Transcription whispers through native ground,
Web and Swift in harmony found,
A robust framework, widgets so bright! 🍎

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch TIDY-453

@typo-app
Copy link
Copy Markdown
Contributor

typo-app Bot commented Apr 24, 2026

Oops! Your Typo free trial has ended. If you'd like to continue receiving code reviews, please ask the admin to check out our special pricing plan.

@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
3 Security Hotspots
7.2% Duplication on New Code (required ≤ 3%)
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 23ec91ca61

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +60 to +61
if (!(await exists(xcframeworkPath))) {
await run("./build-xcframework.sh", [], { cwd: whisperDir });
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Rebuild whisper framework after checking out a new ref

After git checkout ref, the script only runs build-xcframework.sh when build-apple/whisper.xcframework is missing. If developers rerun this with a different WHISPER_CPP_REF, the existing xcframework is reused even though sources changed, so native builds silently link stale binaries from the old commit. This breaks reproducibility and can ship/test against the wrong whisper version.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

40 issues found across 258 files

Note: This PR contains a large number of files. cubic only reviews up to 75 files per PR, so some files may not have been reviewed. cubic prioritises the most important files to review.

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/SafariWebview.swift">

<violation number="1" location="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/SafariWebview.swift:55">
P1: Force-unwrapping `URL(string: urlString)` can crash when `urlString` is empty or invalid (it is currently initialized to an empty string).</violation>

<violation number="2" location="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/SafariWebview.swift:60">
P1: Custom agent: **Flag Security Vulnerabilities**

Authentication callback/error details are logged directly, exposing sensitive sign-in artifacts.</violation>

<violation number="3" location="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/SafariWebview.swift:87">
P2: Starting `ASWebAuthenticationSession` inside `updateUIViewController` unconditionally can launch duplicate authentication flows on SwiftUI updates.</violation>
</file>

<file name="ios/nucleum/Nucleum/local/LocalConfig.swift">

<violation number="1" location="ios/nucleum/Nucleum/local/LocalConfig.swift:13">
P1: `appDataUrl` is initialized with an empty URL string, so it is always `nil` and will crash when force-unwrapped in `fetchAppData()`.</violation>
</file>

<file name="ios/memotron/Memotron for Mac/Memotron_for_MacApp.swift">

<violation number="1" location="ios/memotron/Memotron for Mac/Memotron_for_MacApp.swift:103">
P3: Remove the unused draggable window/delegate types; they are not wired into the app and add dead code maintenance overhead.</violation>
</file>

<file name="ios/nucleum/Nucleum/Info.plist">

<violation number="1" location="ios/nucleum/Nucleum/Info.plist:21">
P1: `NSAllowsArbitraryLoads` is enabled globally, which disables ATS and permits insecure network traffic across the app.</violation>
</file>

<file name="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Job.swift">

<violation number="1" location="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Job.swift:44">
P2: `JobResult` is public, but its properties are internal by default, so external modules cannot read any of its data.</violation>
</file>

<file name="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/MacAppDelegate.swift">

<violation number="1" location="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/MacAppDelegate.swift:20">
P2: Handle all incoming URLs in `application(_:open:)` instead of only the first one.</violation>

<violation number="2" location="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/MacAppDelegate.swift:22">
P1: Avoid logging full callback URLs; redact or remove these prints to prevent sensitive data exposure in logs.</violation>
</file>

<file name="ios/nucleum/Nucleum for Mac/Nucleum_for_MacApp.swift">

<violation number="1" location="ios/nucleum/Nucleum for Mac/Nucleum_for_MacApp.swift:103">
P3: `AppDelegateTwo` and its custom window flow are dead code because the app is configured to use `MacAppDelegate`, not this delegate.</violation>

<violation number="2" location="ios/nucleum/Nucleum for Mac/Nucleum_for_MacApp.swift:146">
P2: Custom agent: **Check System Design and Architectural Patterns**

Adapter code manually invokes `windowDidLoad()`, coupling view-layer code to controller lifecycle internals and violating framework lifecycle ownership.</violation>
</file>

<file name="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/Modal.swift">

<violation number="1" location="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/Modal.swift:31">
P1: Avoid force-unwrapping the constructed URL; invalid popup paths can cause a runtime crash when opening the modal.</violation>
</file>

<file name="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/WebView/WebViewCommon.swift">

<violation number="1" location="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/WebView/WebViewCommon.swift:25">
P2: Avoid force-unwrapping `request.url`; handle nil with a guarded failure to prevent a runtime crash.</violation>

<violation number="2" location="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/WebView/WebViewCommon.swift:44">
P1: Validate and constrain the requested path before reading from disk to prevent directory traversal outside the bundled web root.</violation>

<violation number="3" location="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/WebView/WebViewCommon.swift:118">
P1: Custom agent: **Flag Security Vulnerabilities**

Public URL creation does not enforce HTTPS, allowing insecure HTTP URLs to be loaded.</violation>
</file>

<file name="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/WebView/CustomWebView.swift">

<violation number="1" location="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/WebView/CustomWebView.swift:31">
P1: Custom agent: **Flag Security Vulnerabilities**

WebView loads unvalidated URLs without enforcing HTTPS, allowing insecure HTTP requests.</violation>

<violation number="2" location="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/WebView/CustomWebView.swift:35">
P2: Avoid unconditional reloads in `updateUIView`; only call `load` when the target URL actually changed.</violation>
</file>

<file name="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/CoreDataStack.swift">

<violation number="1" location="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/CoreDataStack.swift:14">
P1: Do not swallow persistent store load failures; fail fast or propagate the error so the app does not run with an unusable Core Data stack.</violation>

<violation number="2" location="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/CoreDataStack.swift:107">
P2: `save()` currently hides write failures from callers; propagate or surface the error instead of only logging it.</violation>
</file>

<file name="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/elements/SimpleWidgetText.swift">

<violation number="1" location="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/elements/SimpleWidgetText.swift:4">
P2: Do not use `@State` for initializer inputs; this can prevent the text/color from updating correctly when parent values change.</violation>
</file>

<file name="ios/memotron/Memotron/ContentView.swift">

<violation number="1" location="ios/memotron/Memotron/ContentView.swift:19">
P2: The transcription debugger UI is unreachable because `isShowTranscriptionDebugger` is always false in this view.</violation>
</file>

<file name="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/LoadingOverlay.swift">

<violation number="1" location="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/LoadingOverlay.swift:18">
P2: `isSheet` is declared as `@State` but never mutated, making the `isSheet ? "loading..." : ...` branch effectively dead in this view. Use a regular stored property (or remove the condition) so the value can be provided by the parent.</violation>
</file>

<file name="ios/nucleum/Nucleum/whisper/WhisperCppWrapper.h">

<violation number="1" location="ios/nucleum/Nucleum/whisper/WhisperCppWrapper.h:15">
P2: Initializer nullability is incorrect: the implementation can return `nil`, but the header declares a nonnull return under `NS_ASSUME_NONNULL`.</violation>

<violation number="2" location="ios/nucleum/Nucleum/whisper/WhisperCppWrapper.h:21">
P1: Transcription method nullability is wrong: methods can return `nil` and accept `nil` error pointers, but the header declares them as nonnull.</violation>
</file>

<file name="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Extensions.swift">

<violation number="1" location="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Extensions.swift:99">
P2: Require exactly 3 JWT segments before decoding; current check accepts malformed token formats.</violation>
</file>

<file name="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/Offline.swift">

<violation number="1" location="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/Offline.swift:22">
P2: Inject `AppStore` into the SwiftUI preview; otherwise the preview crashes due to missing `EnvironmentObject`.</violation>
</file>

<file name="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/CameraTestView.swift">

<violation number="1" location="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/CameraTestView.swift:131">
P2: `AVCaptureSession.startRunning()` is being called on the main thread, which can block UI rendering during camera startup.</violation>
</file>

<file name="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/elements/SimpleText.swift">

<violation number="1" location="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/elements/SimpleText.swift:11">
P1: Do not use `@State` for initializer-provided display props; this can desynchronize the view from parent updates.</violation>

<violation number="2" location="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/elements/SimpleText.swift:14">
P2: Custom agent: **Check System Design and Architectural Patterns**

`SimpleText` is tightly coupled to global `AppStore` state, creating improper layering for a reusable UI element.</violation>

<violation number="3" location="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/elements/SimpleText.swift:34">
P2: Inject `AppStore` in `#Preview`; otherwise the preview will crash due to missing `@EnvironmentObject`.</violation>
</file>

<file name="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/SafariViewModal.swift">

<violation number="1" location="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/SafariViewModal.swift:16">
P1: Force-unwrapping the URL can crash when `inAppSafariUrl` is empty or malformed.</violation>

<violation number="2" location="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/SafariViewModal.swift:16">
P2: Custom agent: **Flag Security Vulnerabilities**

SafariView is created from a runtime URL string without enforcing HTTPS, allowing insecure HTTP destinations and violating TLS-only security requirements.</violation>
</file>

<file name="ios/nucleum/Nucleum/ContentView.swift">

<violation number="1" location="ios/nucleum/Nucleum/ContentView.swift:14">
P2: `isShowTranscriptionDebugger` is never mutated, so this branch is unreachable and the transcription debugger UI cannot be opened.</violation>
</file>

<file name="ios/memotron/Memotron/WhisperCppWrapper.h">

<violation number="1" location="ios/memotron/Memotron/WhisperCppWrapper.h:12">
P2: This adds a second header for the same `WhisperCppWrapper` interface, duplicating an existing class declaration and creating API drift risk.</violation>

<violation number="2" location="ios/memotron/Memotron/WhisperCppWrapper.h:15">
P2: Mark this initializer as `nullable` because the implementation can return `nil` on failure.</violation>

<violation number="3" location="ios/memotron/Memotron/WhisperCppWrapper.h:21">
P1: The return type should be nullable; this method returns `nil` on multiple error paths.</violation>
</file>

<file name="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Models.swift">

<violation number="1" location="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Models.swift:71">
P1: Custom agent: **Flag Security Vulnerabilities**

Unvalidated raw URL strings are accepted and propagated to network request execution without enforcing HTTPS/TLS.</violation>
</file>

<file name="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/WebAuthenticationViewForMac.swift">

<violation number="1" location="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/WebAuthenticationViewForMac.swift:36">
P1: Custom agent: **Flag Security Vulnerabilities**

Authentication flow starts with an unvalidated URL scheme, allowing insecure `http` auth endpoints instead of enforcing HTTPS/TLS.</violation>
</file>

<file name="ios/memotron/Memotron/Info.plist">

<violation number="1" location="ios/memotron/Memotron/Info.plist:21">
P1: Custom agent: **Flag Security Vulnerabilities**

Global ATS disablement (`NSAllowsArbitraryLoads=true`) permits insecure HTTP traffic and weakens transport security app-wide.</violation>
</file>

<file name="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Logger.swift">

<violation number="1" location="ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Logger.swift:93">
P2: Custom agent: **Check System Design and Architectural Patterns**

Logger core path is tightly coupled to network persistence and platform metadata collection, violating separation of concerns.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

isPresented = true
}
.sheet(isPresented: $isPresented) {
WebAuthenticationView(url: URL(string: urlString)!, callbackURLScheme: "tauri") {
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Apr 24, 2026

Choose a reason for hiding this comment

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

P1: Force-unwrapping URL(string: urlString) can crash when urlString is empty or invalid (it is currently initialized to an empty string).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/SafariWebview.swift, line 55:

<comment>Force-unwrapping `URL(string: urlString)` can crash when `urlString` is empty or invalid (it is currently initialized to an empty string).</comment>

<file context>
@@ -0,0 +1,106 @@
+      isPresented = true
+    }
+    .sheet(isPresented: $isPresented) {
+      WebAuthenticationView(url: URL(string: urlString)!, callbackURLScheme: "tauri") {
+        callbackURL, error in
+        isPresented = false
</file context>
Fix with Cubic

struct LocalConfig {
static let defaultAppName = "Nucleum"
static let appGroup = "group.io.nucleum"
static let appDataUrl = URL(string: "")
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Apr 24, 2026

Choose a reason for hiding this comment

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

P1: appDataUrl is initialized with an empty URL string, so it is always nil and will crash when force-unwrapped in fetchAppData().

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At ios/nucleum/Nucleum/local/LocalConfig.swift, line 13:

<comment>`appDataUrl` is initialized with an empty URL string, so it is always `nil` and will crash when force-unwrapped in `fetchAppData()`.</comment>

<file context>
@@ -0,0 +1,40 @@
+struct LocalConfig {
+  static let defaultAppName = "Nucleum"
+  static let appGroup = "group.io.nucleum"
+  static let appDataUrl = URL(string: "")
+  static let urlScheme = "nucleum"
+
</file context>
Fix with Cubic

<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Apr 24, 2026

Choose a reason for hiding this comment

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

P1: NSAllowsArbitraryLoads is enabled globally, which disables ATS and permits insecure network traffic across the app.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At ios/nucleum/Nucleum/Info.plist, line 21:

<comment>`NSAllowsArbitraryLoads` is enabled globally, which disables ATS and permits insecure network traffic across the app.</comment>

<file context>
@@ -0,0 +1,28 @@
+	<key>NSAppTransportSecurity</key>
+	<dict>
+		<key>NSAllowsArbitraryLoads</key>
+		<true/>
+	</dict>
+	<key>UIBackgroundModes</key>
</file context>
Suggested change
<true/>
<false/>
Fix with Cubic

func application(_ application: NSApplication, open urls: [URL]) {
guard let url = urls.first else { return }
// Handle the custom URL
print("URL received in application(_:open:): \(url)")
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Apr 24, 2026

Choose a reason for hiding this comment

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

P1: Avoid logging full callback URLs; redact or remove these prints to prevent sensitive data exposure in logs.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/MacAppDelegate.swift, line 22:

<comment>Avoid logging full callback URLs; redact or remove these prints to prevent sensitive data exposure in logs.</comment>

<file context>
@@ -0,0 +1,55 @@
+  func application(_ application: NSApplication, open urls: [URL]) {
+    guard let url = urls.first else { return }
+    // Handle the custom URL
+    print("URL received in application(_:open:): \(url)")
+    handleURL(url)
+  }
</file context>
Fix with Cubic

WebViewTwo(
urlType: .customProtocolUrl,
refreshId: refreshId,
url: URL(string: LocalConfig.appUrl + "/" + appStore.popup.path)!,
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Apr 24, 2026

Choose a reason for hiding this comment

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

P1: Avoid force-unwrapping the constructed URL; invalid popup paths can cause a runtime crash when opening the modal.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/Modal.swift, line 31:

<comment>Avoid force-unwrapping the constructed URL; invalid popup paths can cause a runtime crash when opening the modal.</comment>

<file context>
@@ -0,0 +1,59 @@
+          WebViewTwo(
+            urlType: .customProtocolUrl,
+            refreshId: refreshId,
+            url: URL(string: LocalConfig.appUrl + "/" + appStore.popup.path)!,
+            params: [
+              "isSheet": "true", "spath": appStore.popup.path,
</file context>
Fix with Cubic

ZStack {
Color.white.opacity(0)
VStack {
SafariView(url: URL(string: appStore.inAppSafariUrl)!)
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Apr 24, 2026

Choose a reason for hiding this comment

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

P2: Custom agent: Flag Security Vulnerabilities

SafariView is created from a runtime URL string without enforcing HTTPS, allowing insecure HTTP destinations and violating TLS-only security requirements.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/SafariViewModal.swift, line 16:

<comment>SafariView is created from a runtime URL string without enforcing HTTPS, allowing insecure HTTP destinations and violating TLS-only security requirements.</comment>

<file context>
@@ -0,0 +1,25 @@
+    ZStack {
+      Color.white.opacity(0)
+      VStack {
+        SafariView(url: URL(string: appStore.inAppSafariUrl)!)
+      }
+      //   .background(appStore.bg)
</file context>
Fix with Cubic

@State var content: String
@State var fontSize: CGFloat
@State var color: Color?
@EnvironmentObject var appStore: AppStore
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Apr 24, 2026

Choose a reason for hiding this comment

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

P2: Custom agent: Check System Design and Architectural Patterns

SimpleText is tightly coupled to global AppStore state, creating improper layering for a reusable UI element.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/elements/SimpleText.swift, line 14:

<comment>`SimpleText` is tightly coupled to global `AppStore` state, creating improper layering for a reusable UI element.</comment>

<file context>
@@ -0,0 +1,35 @@
+  @State var content: String
+  @State var fontSize: CGFloat
+  @State var color: Color?
+  @EnvironmentObject var appStore: AppStore
+  init(_ content: String, color: Color? = nil, fontSize: CGFloat = 16) {
+    self.content = content
</file context>
Fix with Cubic

@@ -0,0 +1,121 @@
//
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Apr 24, 2026

Choose a reason for hiding this comment

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

P2: Custom agent: Check System Design and Architectural Patterns

Logger core path is tightly coupled to network persistence and platform metadata collection, violating separation of concerns.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Logger.swift, line 93:

<comment>Logger core path is tightly coupled to network persistence and platform metadata collection, violating separation of concerns.</comment>

<file context>
@@ -0,0 +1,121 @@
+    let osVersion = ProcessInfo.processInfo.operatingSystemVersion
+    #if DEBUG
+    if context?.isSaveToServer == true || context == nil {
+      Utils.performSurrealCall(
+        "insert into debugLog {message: $message, timestamp: $timestamp, src: $src, type: $type, app: $app, os: $os, osVersion: $osVersion, device: $device}",
+        [
</file context>
Fix with Cubic

// }
// }
// }
class AppDelegateTwo: NSObject, NSApplicationDelegate, NSWindowDelegate {
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Apr 24, 2026

Choose a reason for hiding this comment

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

P3: Remove the unused draggable window/delegate types; they are not wired into the app and add dead code maintenance overhead.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At ios/memotron/Memotron for Mac/Memotron_for_MacApp.swift, line 103:

<comment>Remove the unused draggable window/delegate types; they are not wired into the app and add dead code maintenance overhead.</comment>

<file context>
@@ -0,0 +1,153 @@
+//     }
+//   }
+// }
+class AppDelegateTwo: NSObject, NSApplicationDelegate, NSWindowDelegate {
+  var window: DraggableWindow!
+  var windowController: DraggableWindowController!
</file context>
Fix with Cubic

// }
// }
// }
class AppDelegateTwo: NSObject, NSApplicationDelegate, NSWindowDelegate {
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Apr 24, 2026

Choose a reason for hiding this comment

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

P3: AppDelegateTwo and its custom window flow are dead code because the app is configured to use MacAppDelegate, not this delegate.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At ios/nucleum/Nucleum for Mac/Nucleum_for_MacApp.swift, line 103:

<comment>`AppDelegateTwo` and its custom window flow are dead code because the app is configured to use `MacAppDelegate`, not this delegate.</comment>

<file context>
@@ -0,0 +1,153 @@
+//     }
+//   }
+// }
+class AppDelegateTwo: NSObject, NSApplicationDelegate, NSWindowDelegate {
+  var window: DraggableWindow!
+  var windowController: DraggableWindowController!
</file context>
Fix with Cubic

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 22

Note

Due to the large number of review comments, Critical severity comments were prioritized as inline comments.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
ios/pointron/Pointron for Mac/Pointron_for_Mac.entitlements (1)

1-17: ⚠️ Potential issue | 🔴 Critical

Missing com.apple.security.application-groups required for widget data sharing.

The macOS entitlements file declares no app group, but the app code (Target.swift, TargetWidgetModel.swift, CurrentSession.swift) consistently uses UserDefaults(suiteName: LocalConfig.appGroup) to share session and color data with the widget extension. The iOS versions (Pointron.entitlements and PointronWidgetExtensionExtension.entitlements) both declare group.io.pointron. Add the matching app group to this file to enable widget functionality on macOS:

Required addition
<key>com.apple.security.application-groups</key>
<array>
	<string>group.io.pointron</string>
</array>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ios/pointron/Pointron` for Mac/Pointron_for_Mac.entitlements around lines 1 -
17, The macOS entitlements file is missing the app group used by the app and
widget; add the com.apple.security.application-groups key with an array
containing "group.io.pointron" so UserDefaults(suiteName: LocalConfig.appGroup)
used in Target.swift, TargetWidgetModel.swift and CurrentSession.swift can
access the shared container; ensure the key and array are added alongside the
existing com.apple.security.* keys in the entitlements plist.
ios/pointron/Pointron.xcodeproj/xcshareddata/xcschemes/Pointron 1.xcscheme (1)

1-79: ⚠️ Potential issue | 🟡 Minor

Likely accidental duplicate scheme — rename or remove.

The filename Pointron 1.xcscheme follows Xcode's automatic "Copy N" naming convention, and the contents are nearly identical to Pointron dev.xcscheme (only missing the surrealUrl env var). Shared schemes appear in every developer's Xcode scheme picker, so an unnamed duplicate is likely to cause confusion about which one to select for builds/CI.

Please confirm this file is intentional; if not, delete it. If it is intentional (e.g., a "prod-like" local scheme), give it a descriptive filename such as Pointron prod.xcscheme or Pointron local.xcscheme so its purpose is clear.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ios/pointron/Pointron.xcodeproj/xcshareddata/xcschemes/Pointron` 1.xcscheme
around lines 1 - 79, This is likely an accidental duplicate scheme file named
"Pointron 1.xcscheme" (references BlueprintIdentifier "22FFA8812AD3E45000103446"
/ BuildableName "Pointron.app") that duplicates the existing Pointron
dev.xcscheme; either delete this file if not intentional, or rename the file to
a descriptive shared-scheme name (e.g., Pointron prod.xcscheme or Pointron
local.xcscheme) and ensure its contents match the intended purpose (add the
missing surrealUrl env var from Pointron dev.xcscheme if needed) so
developers/CI aren’t confused by an unnamed duplicate.
♻️ Duplicate comments (1)
ios/nucleum/Nucleum/whisper/WhisperCppWrapper.h (1)

1-7: ⚠️ Potential issue | 🟡 Minor

Header comment says Memotron in the Nucleum target.

Lines 3 and 5 still reference Memotron — misleading for anyone reading this in the Nucleum project. Fix alongside the de-duplication suggestion raised on ios/memotron/Memotron/whisper/WhisperCppWrapper.h.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ios/nucleum/Nucleum/whisper/WhisperCppWrapper.h` around lines 1 - 7, Header
comment in WhisperCppWrapper.h incorrectly references "Memotron" and should be
updated for the Nucleum target and de-duplicated with
ios/memotron/Memotron/whisper/WhisperCppWrapper.h; open WhisperCppWrapper.h and
replace the file header lines that mention "Memotron" (the top comment block in
WhisperCppWrapper.h) with the correct project name "Nucleum" and ensure the
header block matches the intended Nucleum wording, and apply the same
normalization to the duplicate header in ios/memotron/.../WhisperCppWrapper.h so
both files have consistent, target-appropriate comments.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 41b50e64-4154-47e9-9ee6-4ced34cbd02d

📥 Commits

Reviewing files that changed from the base of the PR and between 73a8da6 and 23ec91c.

⛔ Files ignored due to path filters (134)
  • ios/memotron/Memotron for Mac/Assets.xcassets/AppIcon.appiconset/16.png is excluded by !**/*.png
  • ios/memotron/Memotron for Mac/Assets.xcassets/AppIcon.appiconset/Memotron icon 512 px 1x 1.png is excluded by !**/*.png
  • ios/memotron/Memotron for Mac/Assets.xcassets/AppIcon.appiconset/Memotron icon 512 px 1x.png is excluded by !**/*.png
  • ios/memotron/Memotron for Mac/Assets.xcassets/AppIcon.appiconset/Memotron icon 512 px 2x.png is excluded by !**/*.png
  • ios/memotron/Memotron for Mac/Assets.xcassets/AppIcon.appiconset/Memotron icon 512 px 32pt 1.png is excluded by !**/*.png
  • ios/memotron/Memotron for Mac/Assets.xcassets/AppIcon.appiconset/Memotron icon 512 px 32pt.png is excluded by !**/*.png
  • ios/memotron/Memotron for Mac/Assets.xcassets/AppIcon.appiconset/Memotron icon 512 px 64pt.png is excluded by !**/*.png
  • ios/memotron/Memotron for Mac/Assets.xcassets/AppIcon.appiconset/Memotron icon 512 px fourth.png is excluded by !**/*.png
  • ios/memotron/Memotron for Mac/Assets.xcassets/AppIcon.appiconset/Memotron icon 512 px half copy.png is excluded by !**/*.png
  • ios/memotron/Memotron for Mac/Assets.xcassets/AppIcon.appiconset/Memotron icon 512 px half.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/100.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/102.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/1024.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/114.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/120.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/128.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/144.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/152.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/16.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/167.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/172.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/180.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/196.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/20.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/216.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/256.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/29.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/32.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/40.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/48.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/50.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/512.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/55.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/57.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/58.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/60.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/64.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/66.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/72.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/76.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/80.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/87.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/88.png is excluded by !**/*.png
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/92.png is excluded by !**/*.png
  • ios/nucleum/Nucleum for Mac/Assets.xcassets/AppIcon.appiconset/16.png is excluded by !**/*.png
  • ios/nucleum/Nucleum for Mac/Assets.xcassets/AppIcon.appiconset/Memotron icon 512 px 1x 1.png is excluded by !**/*.png
  • ios/nucleum/Nucleum for Mac/Assets.xcassets/AppIcon.appiconset/Memotron icon 512 px 1x.png is excluded by !**/*.png
  • ios/nucleum/Nucleum for Mac/Assets.xcassets/AppIcon.appiconset/Memotron icon 512 px 2x.png is excluded by !**/*.png
  • ios/nucleum/Nucleum for Mac/Assets.xcassets/AppIcon.appiconset/Memotron icon 512 px 32pt 1.png is excluded by !**/*.png
  • ios/nucleum/Nucleum for Mac/Assets.xcassets/AppIcon.appiconset/Memotron icon 512 px 32pt.png is excluded by !**/*.png
  • ios/nucleum/Nucleum for Mac/Assets.xcassets/AppIcon.appiconset/Memotron icon 512 px 64pt.png is excluded by !**/*.png
  • ios/nucleum/Nucleum for Mac/Assets.xcassets/AppIcon.appiconset/Memotron icon 512 px fourth.png is excluded by !**/*.png
  • ios/nucleum/Nucleum for Mac/Assets.xcassets/AppIcon.appiconset/Memotron icon 512 px half copy.png is excluded by !**/*.png
  • ios/nucleum/Nucleum for Mac/Assets.xcassets/AppIcon.appiconset/Memotron icon 512 px half.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/100.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/102.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/1024.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/114.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/120.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/128.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/144.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/152.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/16.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/167.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/172.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/180.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/196.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/20.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/216.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/256.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/29.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/32.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/40.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/48.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/50.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/512.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/55.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/57.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/58.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/60.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/64.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/66.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/72.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/76.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/80.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/87.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/88.png is excluded by !**/*.png
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/92.png is excluded by !**/*.png
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Sounds/dingding.mp3 is excluded by !**/*.mp3
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Sounds/ping.wav is excluded by !**/*.wav
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Sounds/upchime.mp3 is excluded by !**/*.mp3
  • ios/pointron/Pointron for Mac/Assets.xcassets/AppIcon.appiconset/Pointron mac icon 128 px (1).png is excluded by !**/*.png
  • ios/pointron/Pointron for Mac/Assets.xcassets/AppIcon.appiconset/Pointron mac icon 128 px.png is excluded by !**/*.png
  • ios/pointron/Pointron for Mac/Assets.xcassets/AppIcon.appiconset/Pointron mac icon 16 px (1).png is excluded by !**/*.png
  • ios/pointron/Pointron for Mac/Assets.xcassets/AppIcon.appiconset/Pointron mac icon 16 px.png is excluded by !**/*.png
  • ios/pointron/Pointron for Mac/Assets.xcassets/AppIcon.appiconset/Pointron mac icon 256 px 1.png is excluded by !**/*.png
  • ios/pointron/Pointron for Mac/Assets.xcassets/AppIcon.appiconset/Pointron mac icon 256 px.png is excluded by !**/*.png
  • ios/pointron/Pointron for Mac/Assets.xcassets/AppIcon.appiconset/Pointron mac icon 32 px (1).png is excluded by !**/*.png
  • ios/pointron/Pointron for Mac/Assets.xcassets/AppIcon.appiconset/Pointron mac icon 32 px.png is excluded by !**/*.png
  • ios/pointron/Pointron for Mac/Assets.xcassets/AppIcon.appiconset/Pointron mac icon 512 px 1.png is excluded by !**/*.png
  • ios/pointron/Pointron for Mac/Assets.xcassets/AppIcon.appiconset/Pointron mac icon 512 px.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/100.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/1024.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/114.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/120.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/128.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/144.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/152.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/16.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/167.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/172.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/180.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/196.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/20.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/216.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/256.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/29.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/32.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/40.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/48.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/50.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/512.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/55.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/57.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/58.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/60.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/64.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/66.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/72.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/76.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/80.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/87.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/88.png is excluded by !**/*.png
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/92.png is excluded by !**/*.png
📒 Files selected for processing (124)
  • .gitignore
  • client/components/account/Login.svelte
  • client/components/calendar/Calendar.svelte
  • client/components/flux/resourceStores/resource.utils.ts
  • client/layout/layers/BaseLayer.svelte
  • client/products/product.config.ts
  • ios/memotron/Memotron for Mac/Assets.xcassets/AppIcon.appiconset/Contents.json
  • ios/memotron/Memotron for Mac/ContentView.swift
  • ios/memotron/Memotron for Mac/Memotron_for_Mac.entitlements
  • ios/memotron/Memotron for Mac/Memotron_for_MacApp.swift
  • ios/memotron/Memotron for Mac/Preview Content/Preview Assets.xcassets/Contents.json
  • ios/memotron/Memotron-for-Mac-Info.plist
  • ios/memotron/Memotron.xcodeproj/project.pbxproj
  • ios/memotron/Memotron.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  • ios/memotron/Memotron.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
  • ios/memotron/Memotron.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
  • ios/memotron/Memotron/Assets.xcassets/AppIcon.appiconset/Contents.json
  • ios/memotron/Memotron/ContentView.swift
  • ios/memotron/Memotron/Info.plist
  • ios/memotron/Memotron/Memotron.entitlements
  • ios/memotron/Memotron/MemotronApp.swift
  • ios/memotron/Memotron/Preview Content/Preview Assets.xcassets/Contents.json
  • ios/memotron/Memotron/WhisperCppWrapper.h
  • ios/memotron/Memotron/local/LocalConfig.swift
  • ios/memotron/Memotron/whisper/Memotron-Bridging-Header.h
  • ios/memotron/Memotron/whisper/README.md
  • ios/memotron/Memotron/whisper/WhisperCppWrapper.h
  • ios/memotron/Memotron/whisper/WhisperCppWrapper.mm
  • ios/nucleum/Nucleum for Mac/Assets.xcassets/AppIcon.appiconset/Contents.json
  • ios/nucleum/Nucleum for Mac/ContentView.swift
  • ios/nucleum/Nucleum for Mac/Nucleum_for_Mac.entitlements
  • ios/nucleum/Nucleum for Mac/Nucleum_for_MacApp.swift
  • ios/nucleum/Nucleum for Mac/Preview Content/Preview Assets.xcassets/Contents.json
  • ios/nucleum/Nucleum-for-Mac-Info.plist
  • ios/nucleum/Nucleum.xcodeproj/project.pbxproj
  • ios/nucleum/Nucleum.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  • ios/nucleum/Nucleum.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
  • ios/nucleum/Nucleum.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
  • ios/nucleum/Nucleum/Assets.xcassets/AppIcon.appiconset/Contents.json
  • ios/nucleum/Nucleum/ContentView.swift
  • ios/nucleum/Nucleum/Info.plist
  • ios/nucleum/Nucleum/Nucleum.entitlements
  • ios/nucleum/Nucleum/NucleumApp.swift
  • ios/nucleum/Nucleum/Preview Content/Preview Assets.xcassets/Contents.json
  • ios/nucleum/Nucleum/WhisperCppWrapper.h
  • ios/nucleum/Nucleum/local/LocalConfig.swift
  • ios/nucleum/Nucleum/whisper/Nucleum-Bridging-Header.h
  • ios/nucleum/Nucleum/whisper/README.md
  • ios/nucleum/Nucleum/whisper/WhisperCppWrapper.h
  • ios/nucleum/Nucleum/whisper/WhisperCppWrapper.mm
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/AIService.swift
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/AIServiceProtocols.swift
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/AppStore.swift
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/CoreDataStack.swift
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Defaults.swift
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Extensions.swift
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Job.swift
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/JobManager.swift
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Logger.swift
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/MacAppDelegate.swift
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/MacContent.swift
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Models.swift
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/README.md
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/StoreManager.swift
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Utils.swift
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/AudioTranscriptionView.swift
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/BaseView.swift
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/BaseViewForMac.swift
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/CameraTestView.swift
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/LoadingOverlay.swift
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/Modal.swift
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/Offline.swift
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/SafariViewModal.swift
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/SafariWebview.swift
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/WebAuthenticationViewForMac.swift
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/elements/SimpleText.swift
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/Views/elements/SimpleWidgetText.swift
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/WebView/CustomWebView.swift
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/WebView/WebViewCommon.swift
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/WebView/WebViewForMac.swift
  • ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/WebView/WebViewTwo.swift
  • ios/pointron/Pointron for Mac/Assets.xcassets/AppIcon.appiconset/Contents.json
  • ios/pointron/Pointron for Mac/ContentView.swift
  • ios/pointron/Pointron for Mac/Pointron_for_Mac.entitlements
  • ios/pointron/Pointron for Mac/Pointron_for_MacApp.swift
  • ios/pointron/Pointron for Mac/Preview Content/Preview Assets.xcassets/Contents.json
  • ios/pointron/Pointron-for-Mac-Info.plist
  • ios/pointron/Pointron.xcodeproj/project.pbxproj
  • ios/pointron/Pointron.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  • ios/pointron/Pointron.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
  • ios/pointron/Pointron.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
  • ios/pointron/Pointron.xcodeproj/xcshareddata/xcschemes/Pointron 1.xcscheme
  • ios/pointron/Pointron.xcodeproj/xcshareddata/xcschemes/Pointron dev.xcscheme
  • ios/pointron/Pointron.xcodeproj/xcshareddata/xcschemes/Pointron for Mac.xcscheme
  • ios/pointron/Pointron.xcodeproj/xcshareddata/xcschemes/Pointron-release.xcscheme
  • ios/pointron/Pointron.xcodeproj/xcshareddata/xcschemes/Pointron.xcscheme
  • ios/pointron/Pointron.xcodeproj/xcshareddata/xcschemes/PointronWidgetExtensionExtension.xcscheme
  • ios/pointron/Pointron/Assets.xcassets/AppIcon.appiconset/Contents.json
  • ios/pointron/Pointron/ContentView.swift
  • ios/pointron/Pointron/Info.plist
  • ios/pointron/Pointron/Pointron.entitlements
  • ios/pointron/Pointron/PointronApp.swift
  • ios/pointron/Pointron/Preview Content/Preview Assets.xcassets/Contents.json
  • ios/pointron/Pointron/local/LocalConfig.swift
  • ios/pointron/Pointron/local/Models/CurrentSession.swift
  • ios/pointron/Pointron/local/Models/SurrealModels.swift
  • ios/pointron/Pointron/local/Models/Target.swift
  • ios/pointron/Pointron/local/Models/TargetWidgetModel.swift
  • ios/pointron/PointronWidgetExtension/AppIntent.swift
  • ios/pointron/PointronWidgetExtension/Assets.xcassets/AccentColor.colorset/Contents.json
  • ios/pointron/PointronWidgetExtension/Assets.xcassets/AppIcon.appiconset/Contents.json
  • ios/pointron/PointronWidgetExtension/Assets.xcassets/Contents.json
  • ios/pointron/PointronWidgetExtension/Assets.xcassets/WidgetBackground.colorset/Contents.json
  • ios/pointron/PointronWidgetExtension/Info.plist
  • ios/pointron/PointronWidgetExtension/PointronWidgetExtension.swift
  • ios/pointron/PointronWidgetExtension/PointronWidgetExtensionBundle.swift
  • ios/pointron/PointronWidgetExtension/PointronWidgetExtensionLiveActivity.swift
  • ios/pointron/PointronWidgetExtension/Widgets/CurrentSessionWidget.swift
  • ios/pointron/PointronWidgetExtension/Widgets/TargetsWidget.swift
  • ios/pointron/PointronWidgetExtensionExtension.entitlements
  • ios/pointron/sheet/index.html
  • package.json
  • tools/ios/build-web-bundle.mjs
  • tools/ios/setup-whisper.mjs

Comment on lines +11 to +22
<key>com.apple.security.device.audio-input</key>
<true/>
<key>com.apple.security.device.camera</key>
<true/>
<key>com.apple.security.files.downloads.read-write</key>
<true/>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.personal-information.location</key>
<true/>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify usage description keys are present in the Memotron mac Info.plist.
fd -t f -i 'Memotron-for-Mac-Info.plist' -x sh -c 'echo "--- {} ---"; cat {}'
fd -t f -i 'Info.plist' -E 'Tests' -E 'UITests' -x grep -lE 'Memotron|memotron' {} \; 2>/dev/null | xargs -I{} sh -c 'echo "=== {} ==="; grep -E "NSMicrophoneUsageDescription|NSCameraUsageDescription|NSLocation.*UsageDescription" {} || echo "(none)"'

Repository: 21nCo/nucleum

Length of output: 687


Add required NSUsageDescription keys to the macOS Info.plist for declared privacy entitlements.

The entitlements file declares device.audio-input, device.camera, and personal-information.location, but the corresponding usage-description strings are missing from the Info.plist files (NSMicrophoneUsageDescription, NSCameraUsageDescription, NSLocationUsageDescription). Without these keys, the app will crash on macOS when the first privacy prompt is triggered.

Add the missing keys to ios/memotron/Memotron-for-Mac-Info.plist (or the actively used Info.plist for the macOS target):

  • NSMicrophoneUsageDescription
  • NSCameraUsageDescription
  • NSLocationUsageDescription or NSLocationWhenInUseUsageDescription
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ios/memotron/Memotron` for Mac/Memotron_for_Mac.entitlements around lines 11
- 22, The entitlements declare com.apple.security.device.audio-input,
com.apple.security.device.camera and
com.apple.security.personal-information.location but the macOS Info.plist is
missing the corresponding usage-description keys; open the Info.plist for the
macOS target and add NSMicrophoneUsageDescription, NSCameraUsageDescription, and
either NSLocationUsageDescription or NSLocationWhenInUseUsageDescription with
meaningful non-empty user-facing strings describing why the app needs
microphone, camera and location access so the system prompts will not crash the
app.

Comment on lines +11 to +22
<key>com.apple.security.device.audio-input</key>
<true/>
<key>com.apple.security.device.camera</key>
<true/>
<key>com.apple.security.files.downloads.read-write</key>
<true/>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
<key>com.apple.security.personal-information.location</key>
<true/>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
fd -t f -i 'Nucleum-for-Mac-Info.plist' -x sh -c 'echo "--- {} ---"; cat {}'
fd -t f -i 'Info.plist' -x grep -lE 'Nucleum|nucleum' {} \; 2>/dev/null | xargs -I{} sh -c 'echo "=== {} ==="; grep -E "NSMicrophoneUsageDescription|NSCameraUsageDescription|NSLocation.*UsageDescription" {} || echo "(none)"'

Repository: 21nCo/nucleum

Length of output: 679


Add missing NSMicrophoneUsageDescription, NSCameraUsageDescription, and NSLocation*UsageDescription keys to macOS Info.plist.

The Nucleum for Mac entitlements file declares audio-input, camera, and location access permissions, but the corresponding usage descriptions are absent from Nucleum-for-Mac-Info.plist. Without these keys, the app will crash when attempting to access the microphone, camera, or location data. Add the required entitlement descriptions to the Info.plist file.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ios/nucleum/Nucleum` for Mac/Nucleum_for_Mac.entitlements around lines 11 -
22, Your entitlements declare com.apple.security.device.audio-input,
com.apple.security.device.camera, and
com.apple.security.personal-information.location but the Info.plist is missing
the corresponding usage description keys; add NSMicrophoneUsageDescription and
NSCameraUsageDescription and add the appropriate location usage keys (e.g.,
NSLocationWhenInUseUsageDescription and, if you request always access,
NSLocationAlwaysAndWhenInUseUsageDescription) to Nucleum-for-Mac-Info.plist with
clear user-facing strings describing why the app needs microphone, camera, and
location access so macOS won't crash when those features are requested.

Comment on lines +14 to +17
init() {
// Initialize the AI service on the shared AppStore instance
AppStore.shared.setAIServiceProvider(AIService.shared)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect both singletons' initializers to confirm they are lightweight and main-thread-safe.
fd -t f -e swift -HI 'AppStore.swift'
fd -t f -e swift -HI 'AIService.swift'
ast-grep --pattern $'class AppStore: $_ {
  $$$
}'
ast-grep --pattern $'class AIService: $_ {
  $$$
}'

Repository: 21nCo/nucleum

Length of output: 198


🏁 Script executed:

cat -n ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/AppStore.swift

Repository: 21nCo/nucleum

Length of output: 50369


🏁 Script executed:

cat -n ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/AIService.swift

Repository: 21nCo/nucleum

Length of output: 50370


Avoid heavy initialization work in AIService.shared during app startup.

AIService.init() calls downloadModelIfNeeded(), which initiates a network download of the Whisper model from HuggingFace if not cached (ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/AIService.swift, lines 190-233). This download initiation happens on the main thread during app launch, causing noticeable startup delays on slower connections.

Defer model downloads to lazy initialization only when transcription is actually requested, rather than eagerly during AIService.shared creation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ios/nucleum/Nucleum` for Mac/Nucleum_for_MacApp.swift around lines 14 - 17,
The app is creating AIService.shared in App startup which triggers
AIService.init() and calls downloadModelIfNeeded(), causing network downloads on
the main thread; change AIService to defer heavy work by making
downloadModelIfNeeded() lazy and only invoked when transcription is requested.
Modify AIService so its init() does not start downloads (remove or guard calls
to downloadModelIfNeeded()), add a lazy initializer or explicit method (e.g.,
prepareModelIfNeeded or ensureModelAvailable) that runs downloadModelIfNeeded()
off the background queue, and update callers (transcription entry points) to
call that preparation method before starting transcription; keep
AppStore.shared.setAIServiceProvider(AIService.shared) as a lightweight
registration.

Comment on lines +194 to +206
} else if (bits_per_sample == 24) {
// Handle 24-bit audio (3 bytes per sample)
for (int i = 0; i < n_samples; i++) {
float sum = 0.0f;
for (int j = 0; j < num_channels; j++) {
// Read 3 bytes and convert to a 24-bit integer
int sample_idx = dataPos + (i * num_channels + j) * 3;
int32_t sample = (bytes[sample_idx] << 8) | (bytes[sample_idx + 1] << 16) | (bytes[sample_idx + 2] << 24);
// Convert to float and normalize
sum += sample / 8388608.0f; // 2^23
}
pcmf32[i] = sum / num_channels;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

In little-endian 24-bit signed PCM WAV, what is the correct normalization factor to map samples to the range [-1.0, 1.0]?

💡 Result:

The correct normalization factor for 24-bit signed PCM WAV samples to map to the range [-1.0, 1.0] is 2^23, or 8388608. Divide the integer sample value by 8388608.0 to get a normalized float in approximately [-1.0, 0.9999999]. This treats the minimum value (-8388608) as -1.0 and the maximum (8388607) as nearly +1.0, consistent with two's complement representation and common audio library practices like libsndfile (which normalizes all PCM widths to [-1.0, 1.0]) and wavio (explicitly / 223).

Citations:


🏁 Script executed:

#!/bin/bash
# 1. Find and examine the WhisperCppWrapper.mm file in nucleum
find . -name "WhisperCppWrapper.mm" -type f

Repository: 21nCo/nucleum

Length of output: 161


🏁 Script executed:

#!/bin/bash
# 2. Once located, check lines 194-206 of nucleum's WhisperCppWrapper.mm
cat -n ios/nucleum/Nucleum/whisper/WhisperCppWrapper.mm | sed -n '190,210p'

Repository: 21nCo/nucleum

Length of output: 1183


🏁 Script executed:

#!/bin/bash
# 3. Check if identical code exists in memotron
cat -n ios/memotron/Memotron/whisper/WhisperCppWrapper.mm | sed -n '190,210p'

Repository: 21nCo/nucleum

Length of output: 1183


🏁 Script executed:

#!/bin/bash
# 4. Search for bounds checks around dataPos and dataSize in nucleum file
rg -n "dataPos|dataSize|bounds|OOB" ios/nucleum/Nucleum/whisper/WhisperCppWrapper.mm

Repository: 21nCo/nucleum

Length of output: 829


🏁 Script executed:

#!/bin/bash
# 5. Check for division-by-zero guards related to num_channels or bits_per_sample
rg -n -B2 -A2 "num_channels|bits_per_sample" ios/nucleum/Nucleum/whisper/WhisperCppWrapper.mm | head -80

Repository: 21nCo/nucleum

Length of output: 3783


24-bit PCM samples are scaled 256× too large, degrading Whisper recognition.

(bytes[sample_idx] << 8) | (bytes[sample_idx + 1] << 16) | (bytes[sample_idx + 2] << 24) places the little-endian 24-bit value in bits [8–31] instead of [0–22], multiplying it by 256 before normalization. Dividing by 8388608 (2^23) then yields samples in ~[-256, 256] instead of [-1, 1]. Whisper's mel-spectrogram frontend will clip/distort these out-of-range values, severely degrading recognition quality.

Additionally, shifting a uint8_t left by 24 is undefined behavior when the top bit is set (the value is integer-promoted to int before shifting into the sign bit). Cast to uint32_t first.

The identical bug exists in ios/memotron/Memotron/whisper/WhisperCppWrapper.mm at the same lines and requires the same fix.

🐛 Proposed fix
 } else if (bits_per_sample == 24) {
     // Handle 24-bit audio (3 bytes per sample)
     for (int i = 0; i < n_samples; i++) {
         float sum = 0.0f;
         for (int j = 0; j < num_channels; j++) {
             // Read 3 bytes and convert to a 24-bit integer
             int sample_idx = dataPos + (i * num_channels + j) * 3;
-            int32_t sample = (bytes[sample_idx] << 8) | (bytes[sample_idx + 1] << 16) | (bytes[sample_idx + 2] << 24);
-            // Convert to float and normalize
-            sum += sample / 8388608.0f; // 2^23
+            // Little-endian 24-bit signed PCM → place in upper 24 bits of int32
+            // for proper sign extension, then arithmetic-shift back by 8.
+            int32_t packed = (int32_t)((uint32_t)bytes[sample_idx]       << 8
+                                     | (uint32_t)bytes[sample_idx + 1]   << 16
+                                     | (uint32_t)bytes[sample_idx + 2]   << 24);
+            int32_t sample = packed >> 8; // arithmetic shift, preserves sign
+            sum += sample / 8388608.0f; // 2^23
         }
         pcmf32[i] = sum / num_channels;
     }
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
} else if (bits_per_sample == 24) {
// Handle 24-bit audio (3 bytes per sample)
for (int i = 0; i < n_samples; i++) {
float sum = 0.0f;
for (int j = 0; j < num_channels; j++) {
// Read 3 bytes and convert to a 24-bit integer
int sample_idx = dataPos + (i * num_channels + j) * 3;
int32_t sample = (bytes[sample_idx] << 8) | (bytes[sample_idx + 1] << 16) | (bytes[sample_idx + 2] << 24);
// Convert to float and normalize
sum += sample / 8388608.0f; // 2^23
}
pcmf32[i] = sum / num_channels;
}
} else if (bits_per_sample == 24) {
// Handle 24-bit audio (3 bytes per sample)
for (int i = 0; i < n_samples; i++) {
float sum = 0.0f;
for (int j = 0; j < num_channels; j++) {
// Read 3 bytes and convert to a 24-bit integer
int sample_idx = dataPos + (i * num_channels + j) * 3;
// Little-endian 24-bit signed PCM → place in upper 24 bits of int32
// for proper sign extension, then arithmetic-shift back by 8.
int32_t packed = (int32_t)((uint32_t)bytes[sample_idx] << 8
| (uint32_t)bytes[sample_idx + 1] << 16
| (uint32_t)bytes[sample_idx + 2] << 24);
int32_t sample = packed >> 8; // arithmetic shift, preserves sign
sum += sample / 8388608.0f; // 2^23
}
pcmf32[i] = sum / num_channels;
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ios/nucleum/Nucleum/whisper/WhisperCppWrapper.mm` around lines 194 - 206, The
24-bit PCM handling in the bits_per_sample == 24 branch incorrectly assembles
bytes (shifting into bits 8–31) and risks UB by shifting uint8_t; fix by
assembling the little-endian 24-bit sample as a uint32_t using
(uint32_t)bytes[sample_idx] | ((uint32_t)bytes[sample_idx+1] << 8) |
((uint32_t)bytes[sample_idx+2] << 16), then sign-extend the 24-bit value to a
signed 32-bit (e.g., if (val & 0x800000) val |= 0xFF000000), convert to float
and normalize by dividing by 8388608.0f before mixing into pcmf32; apply the
same change in the corresponding
ios/memotron/Memotron/whisper/WhisperCppWrapper.mm file.

Comment on lines +583 to +623
} else if value.keys.contains("purchase") {
Log.info("purchase request: \(value)")
if let purchaseData = value["purchase"] as? [String: Any],
let productId = purchaseData["productId"] as? String
{
// Optional parameters
let offerToken = purchaseData["offerToken"] as? String
let nonce = purchaseData["nonce"] as? String
handlePurchase(productId: productId, nonce: nonce!, offerToken: offerToken)
} else if let purchaseString = value["purchase"] as? String {
// Try to parse the purchase data as a JSON string
do {
if let jsonData = purchaseString.data(using: .utf8),
let purchaseDict = try JSONSerialization.jsonObject(with: jsonData) as? [String: Any],
let productId = purchaseDict["productId"] as? String
{
// Optional parameters
let offerToken = purchaseDict["offerToken"] as? String
let nonce = purchaseDict["nonce"] as? String
handlePurchase(productId: productId, nonce: nonce!, offerToken: offerToken)
} else {
Log.error(
message: "Invalid purchase request: missing or invalid productId in JSON string")
self.sendMessageToApp(message: [
"type": "PURCHASE_ERROR",
"error": "Invalid purchase request: missing or invalid productId in JSON string",
])
}
} catch {
Log.error(message: "Failed to parse purchase JSON string: \(error.localizedDescription)")
self.sendMessageToApp(message: [
"type": "PURCHASE_ERROR",
"error": "Failed to parse purchase JSON string: \(error.localizedDescription)",
])
}
} else {
Log.error(message: "Invalid purchase request: missing productId")
self.sendMessageToApp(message: [
"type": "PURCHASE_ERROR", "error": "Invalid purchase request: missing productId",
])
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Force-unwrap of nonce will crash on any purchase request missing a nonce.

In both branches (Line 591 and Line 602), nonce is obtained as an optional (as? String) and then force-unwrapped into handlePurchase(productId:nonce:offerToken:). A web payload that provides productId but omits nonce — which is very possible for restore/non-nonce flows — will crash the app. offerToken is correctly treated as optional; nonce should be too, or the code should validate+reject the request before calling.

🐛 Proposed fix
       if let purchaseData = value["purchase"] as? [String: Any],
         let productId = purchaseData["productId"] as? String
       {
-        // Optional parameters
         let offerToken = purchaseData["offerToken"] as? String
-        let nonce = purchaseData["nonce"] as? String
-        handlePurchase(productId: productId, nonce: nonce!, offerToken: offerToken)
+        guard let nonce = purchaseData["nonce"] as? String else {
+          Log.error(message: "Missing nonce for purchase")
+          self.sendMessageToApp(message: [
+            "type": "PURCHASE_ERROR",
+            "error": "Missing nonce",
+          ])
+          return
+        }
+        handlePurchase(productId: productId, nonce: nonce, offerToken: offerToken)
       } else if let purchaseString = value["purchase"] as? String {
         ...
-            let nonce = purchaseDict["nonce"] as? String
-            handlePurchase(productId: productId, nonce: nonce!, offerToken: offerToken)
+            guard let nonce = purchaseDict["nonce"] as? String else { /* send error & return */ }
+            handlePurchase(productId: productId, nonce: nonce, offerToken: offerToken)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ios/packages/NucleumNativeKit/Sources/NucleumNativeKit/AppStore.swift` around
lines 583 - 623, The code force-unwraps nonce before calling
handlePurchase(productId:nonce:offerToken:) which will crash when nonce is
absent; update both branches that call handlePurchase (the dictionary branch
handling purchaseData and the JSON string branch handling purchaseDict) to treat
nonce as an optional: either validate and return a PURCHASE_ERROR if nonce is
required, or pass nonce (which may be nil) into handlePurchase without using !,
mirroring how offerToken is handled, and adjust any handlePurchase
signature/logic if it must accept an optional nonce.

Comment on lines +11 to +141
struct Targets: Decodable {
var isSessionRunning: Bool;
var goalName: String?;
var color: String?;
var start: Date?;
var end: Date?;
var todayFocus: Float?;
var isLoggedIn: Bool?;
var isFocusing: Bool;
static let defaultEntry = CurrentSessionEntry(date: Date(), session: CurrentSession(isSessionRunning: false, isFocusing: false),colors: resolveColors());

static func resolveColors() -> ColorSchemeColors {
let sharedDefaults = UserDefaults(suiteName: LocalConfig.appGroup);
var colors: ColorSchemeColors = defaultScheme.colors;
if let schemeString = sharedDefaults?.string(forKey: "scheme") {
let jsonData = Data(schemeString.utf8)
let decoder = JSONDecoder()
do {
let scheme = try decoder.decode(ColorScheme.self, from: jsonData)
colors = scheme.colors;
} catch {
// logs.append("error in decoding scheme: \(error.localizedDescription), schemeString: \(schemeString)");
}
}
return colors;
}

static func refreshSessionInformation(completion:@escaping (CurrentSession?, Error?) -> Void) {
let sharedDefaults = UserDefaults(suiteName: LocalConfig.appGroup);
var logs: [String] = [];
do {
var userId: String? = nil;
if let user = sharedDefaults?.string(forKey: "userId") {
userId = user;
} else{
logs.append("userId not found. User not logged in")
}
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
dateFormatter.timeZone = TimeZone(secondsFromGMT: 0)
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(dateFormatter)
let elapsed = resolveBackgroundElapsed();
logs.append("background elapsed: \(elapsed)")
if(elapsed > 5 * 60){
if let sessionDataString = sharedDefaults?.string(forKey: "sessionData") {
let jsonData = Data(sessionDataString.utf8)
do {
var sessionData = try decoder.decode(CurrentSession.self, from: jsonData)
sessionData.todayFocus = (sessionData.todayFocus ?? 0) / (60 * 60)
completion(sessionData, nil);
saveLogs(logs)
return;
} catch {
logs.append("error in decoding sessiondata: \(error.localizedDescription), sessionDataString: \(sessionDataString)")
}
}
}
var request = URLRequest(url: URL(string: "https://\(LocalConfig.surrealUrl)")!);
request.httpMethod = "POST";
var surrealToken: String? = nil;
if let token = sharedDefaults?.string(forKey: "surrealToken") {
surrealToken = token;
}
if(surrealToken == nil || userId == nil){
logs.append("surrealToken and userId not found")
completion(nil, nil);
saveLogs(logs);
return;
}
request.addValue("Bearer \(surrealToken!)", forHTTPHeaderField: "Authorization")
request.httpBody = "USE database \(userId!); return fn::pointron::iOS::currentSessionWidget();".data(using: .utf8)
request.addValue("text/plain", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
// if let headers = request.allHTTPHeaderFields {
// for (headerField, value) in headers {
// print("\(headerField): \(value)")
// }
// } else {
// print("No headers set for this request.")
// }
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
if error != nil {
print("URL session error: \(error)")
logs.append("error in data task: \(error)");
saveLogs(logs);
completion(nil, error);
return;
} else {
// Print status code
if let httpResponse = response as? HTTPURLResponse {
logs.append("Status Code: \(httpResponse.statusCode)")
}
// Print response body
if let data = data {
let responseBody = String(data: data, encoding: .utf8) ?? "Couldn't decode data"
logs.append("Response Body: \(responseBody)")
}
if let unwrappedData = data {
do {
logs.append("JSON unwrappedData: \(unwrappedData)");
sharedDefaults?.set(logs, forKey: "widgetLogs")
let response = try decoder.decode([SurrealResult].self, from: unwrappedData)
if let currentSessions = response[1].result {
logs.append("parsed currentsessions: \(currentSessions.count)");
saveLogs(logs);
completion(currentSessions[0], nil);
return;
}
} catch {
logs.append("parse error in Json: \(error)");
saveLogs(logs);
completion(nil, error);
return;
}
} else {
logs.append("Unable to unwrap data: \(error)");
saveLogs(logs)
completion(nil, error);
return;
}
}
}
task.resume()
}
catch {
completion(nil, error)
}
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find callers/definitions of the refresh APIs to determine which are live.
rg -nP -C2 '\bTargets\.(refreshSessionInformation|defaultEntry|resolveColors)\b'
rg -nP -C2 '\bCurrentSession\.(refreshSessionInformation|defaultEntry)\b'
fd -e swift CurrentSession

Repository: 21nCo/nucleum

Length of output: 2275


🏁 Script executed:

cat -n ios/pointron/Pointron/local/Models/CurrentSession.swift

Repository: 21nCo/nucleum

Length of output: 9123


🏁 Script executed:

# Search for any usage of Targets struct
rg -n 'Targets\s*[({]|Targets\.' --type swift

Repository: 21nCo/nucleum

Length of output: 39


🏁 Script executed:

# Search for imports of Target.swift or references to it
rg -n 'import.*Target|from.*Target' --type swift

Repository: 21nCo/nucleum

Length of output: 39


🏁 Script executed:

# Find TargetWidgetModel.swift and check the duplication claim
fd -e swift TargetWidgetModel

Repository: 21nCo/nucleum

Length of output: 116


🏁 Script executed:

cat -n ios/pointron/Pointron/local/Models/TargetWidgetModel.swift

Repository: 21nCo/nucleum

Length of output: 6633


🏁 Script executed:

# Search for any actual usage of the Targets struct
rg -n 'Targets\(' --type swift

Repository: 21nCo/nucleum

Length of output: 39


🏁 Script executed:

# Search for imports of Target.swift
rg -n 'Target\.swift|from "Target"|import.*Target' --type swift

Repository: 21nCo/nucleum

Length of output: 202


🏁 Script executed:

# Final confirmation: search for any actual instantiation or use of Targets struct
rg -n '\bTargets\b' --type swift -C 2

Repository: 21nCo/nucleum

Length of output: 2323


Delete the Targets struct—it's unused dead code that duplicates CurrentSession.

The Targets struct in this file is not called anywhere in the codebase. The active widget code uses CurrentSession.refreshSessionInformation() and CurrentSession.defaultEntry instead. Meanwhile, Targets has identical fields and static methods to CurrentSession (same isSessionRunning, goalName, color, start, end, todayFocus, isFocusing, defaultEntry, refreshSessionInformation(), and resolveColors()). This entire struct should be removed; the separate TargetWidgetModel is the correct model for the targets widget and is not a duplicate of this code.

🧰 Tools
🪛 GitHub Check: SonarCloud Code Analysis

[warning] 99-99: Remove this redundant return.

See more on https://sonarcloud.io/project/issues?id=21nCo_nucleum&issues=AZ29p3dzksIvWbIGOHQx&open=AZ29p3dzksIvWbIGOHQx&pullRequest=549


[warning] 56-56: Merge this if statement with the nested one.

See more on https://sonarcloud.io/project/issues?id=21nCo_nucleum&issues=AZ29p3dzksIvWbIGOHQw&open=AZ29p3dzksIvWbIGOHQw&pullRequest=549

🪛 SwiftLint (0.63.2)

[Warning] 12-12: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 13-13: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 14-14: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 15-15: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 16-16: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 17-17: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 18-18: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 19-19: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 20-20: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 23-23: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 24-24: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 30-30: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 35-35: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 39-39: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 40-40: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 42-42: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 44-44: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 54-54: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 62-62: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 64-64: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 70-70: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 71-71: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 72-72: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 74-74: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 78-78: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 79-79: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 80-80: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 96-96: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 97-97: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 98-98: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 99-99: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 112-112: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 116-116: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 117-117: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 118-118: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 119-119: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 122-122: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 123-123: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 124-124: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 125-125: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 128-128: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 130-130: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 131-131: Lines should not have trailing semicolons

(trailing_semicolon)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ios/pointron/Pointron/local/Models/Target.swift` around lines 11 - 141,
Remove the unused duplicate Targets struct entirely (the struct named Targets
plus its static methods resolveColors() and refreshSessionInformation() and the
static let defaultEntry) since CurrentSession already provides
CurrentSession.refreshSessionInformation() and CurrentSession.defaultEntry;
delete the Targets declaration and its helper code, run a quick project-wide
search to ensure no remaining references to Targets remain, and keep
TargetWidgetModel as the widget model.

Comment on lines +110 to +132
if let unwrappedData = data {
do {
logs.append("JSON unwrappedData: \(unwrappedData)");
sharedDefaults?.set(logs, forKey: "widgetLogs")
let response = try decoder.decode([SurrealResult].self, from: unwrappedData)
if let currentSessions = response[1].result {
logs.append("parsed currentsessions: \(currentSessions.count)");
saveLogs(logs);
completion(currentSessions[0], nil);
return;
}
} catch {
logs.append("parse error in Json: \(error)");
saveLogs(logs);
completion(nil, error);
return;
}
} else {
logs.append("Unable to unwrap data: \(error)");
saveLogs(logs)
completion(nil, error);
return;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Same critical bug as in TargetWidgetModel.swift: unbounded index access and a path where completion is never invoked.

response[1].result (line 115), currentSessions[0] (line 118), and the missing else branch on if let currentSessions = response[1].result reproduce the exact issue flagged in TargetWidgetModel.swift. Please apply the same guarded fix here (bounds-check response, use currentSessions.first, and call completion(nil, nil) in the fall-through).

🧰 Tools
🪛 SwiftLint (0.63.2)

[Warning] 112-112: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 116-116: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 117-117: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 118-118: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 119-119: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 122-122: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 123-123: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 124-124: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 125-125: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 128-128: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 130-130: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 131-131: Lines should not have trailing semicolons

(trailing_semicolon)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ios/pointron/Pointron/local/Models/Target.swift` around lines 110 - 132, The
parse block currently accesses response[1] and currentSessions[0] unsafely and
can skip calling completion; fix by first verifying response.count > 1 before
using response[1], use currentSessions.first (or check currentSessions.isEmpty)
instead of currentSessions[0], and add an explicit else/fall-through that calls
completion(nil, nil) when no sessions are found; also ensure the unwrappedData
else branch does not reference an undefined error variable and calls
completion(nil, nil) or passes a real error to completion consistently; update
the logic around decoder.decode, response[1].result, saveLogs, and completion to
reflect these guarded checks.

Comment on lines +95 to +117
if let unwrappedData = data {
do {
logs.append("JSON unwrappedData: \(unwrappedData)");
sharedDefaults?.set(logs, forKey: "widgetLogs")
let response = try decoder.decode([TargetsSurrealResult].self, from: unwrappedData)
if let currentSessions = response[1].result {
logs.append("parsed currentsessions: \(currentSessions.count)");
saveLogs(logs);
completion(currentSessions[0], nil);
return;
}
} catch {
logs.append("parse error in Json: \(error)");
saveLogs(logs);
completion(nil, error);
return;
}
} else {
logs.append("Unable to unwrap data: \(error)");
saveLogs(logs)
completion(nil, error);
return;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical: unbounded index access and a code path that never calls completion.

Three related bugs in the success branch:

  1. response[1] (line 99) crashes if the server returns fewer than two results.
  2. currentSessions[0] (line 103) crashes if .result is an empty array.
  3. If response[1].result is nil, the if let is skipped and control falls through to the end of the closure with completion never called — the widget timeline provider will hang until it's killed.

Same issues exist in the sibling Target.swift — fix them in both.

🐛 Suggested fix
-                        let response = try decoder.decode([TargetsSurrealResult].self, from: unwrappedData)
-                        if let currentSessions = response[1].result {
-                            logs.append("parsed currentsessions: \(currentSessions.count)");
-                            saveLogs(logs);
-                            completion(currentSessions[0], nil);
-                            return;
-                        }
+                        let response = try decoder.decode([TargetsSurrealResult].self, from: unwrappedData)
+                        guard response.count > 1,
+                              let currentSessions = response[1].result,
+                              let first = currentSessions.first else {
+                            logs.append("Unexpected SurrealDB response shape; results missing")
+                            saveLogs(logs)
+                            completion(nil, nil)
+                            return
+                        }
+                        logs.append("parsed currentsessions: \(currentSessions.count)")
+                        saveLogs(logs)
+                        completion(first, nil)
+                        return
🧰 Tools
🪛 SwiftLint (0.63.2)

[Warning] 97-97: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 101-101: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 102-102: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 103-103: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 104-104: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 107-107: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 108-108: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 109-109: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 110-110: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 113-113: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 115-115: Lines should not have trailing semicolons

(trailing_semicolon)


[Warning] 116-116: Lines should not have trailing semicolons

(trailing_semicolon)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ios/pointron/Pointron/local/Models/TargetWidgetModel.swift` around lines 95 -
117, The success-path unwrapping uses unsafe indexing (response[1]) and assumes
non-empty result (currentSessions[0]) and can skip calling completion; fix by
validating array bounds and optionals: check response.count > 1 before accessing
response[1], safely unwrap response[1].result into currentSessions and ensure
currentSessions.count > 0 before using [0], and if any of those validations fail
call completion(nil, some appropriate Error) and saveLogs; apply the same
defensive checks and guaranteed completion invocation to the sibling
Target.swift, referencing the decoder.decode([TargetsSurrealResult].self,
from:), response, currentSessions, logs, saveLogs and completion symbols.

Comment on lines +8 to +22
import SwiftUI

@main
struct PointronApp: App {
init() {
// Request permission for notifications
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) {
success, error in
if success {
print("Notifications permission granted")
} else if let error = error {
print(error.localizedDescription)
}
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

Does Swift UserNotifications module need explicit import to use UNUserNotificationCenter, or is it re-exported by SwiftUI?

💡 Result:

No, the Swift UserNotifications module requires an explicit import to use UNUserNotificationCenter. It is not re-exported by SwiftUI. All authoritative sources, including Apple documentation and SwiftUI tutorials, show that UserNotifications must be explicitly imported even in SwiftUI views. For example: - Apple docs for UNUserNotificationCenter do not mention re-exporting. - SwiftUI examples consistently use import SwiftUI followed by import UserNotifications before using UNUserNotificationCenter.current [2,3,5,9,21,28]. - No sources indicate SwiftUI re-exports UserNotifications; it re-exports Combine via @_exported, but not UserNotifications [23]. Without import UserNotifications, attempting to use UNUserNotificationCenter results in "Use of undeclared type" errors [2].

Citations:


Add import UserNotifications — code will not compile without it.

UNUserNotificationCenter is not re-exported by SwiftUI and requires an explicit import from the UserNotifications framework. Attempting to use it without the import results in a "Use of undeclared type" compilation error.

Fix
 import SwiftUI
+import UserNotifications

Also consider using OSLog/Logger instead of print for production logging, and move the notification permission request out of App.init() (e.g., to onAppear or a dedicated initialization method) so the UI controls when the OS prompt appears.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ios/pointron/Pointron/PointronApp.swift` around lines 8 - 22, Add the missing
import for the UserNotifications framework and move the notification permission
request out of PointronApp.init(): add "import UserNotifications" at the top,
and relocate the UNUserNotificationCenter.current().requestAuthorization(...)
call from the PointronApp init to a lifecycle-aware spot (e.g., onAppear of the
root view or a dedicated initializer method) so the OS prompt is shown at the
right time; while here, replace raw print calls inside that callback with a
Logger/OSLog-based logger to avoid using print in production and reference
PointronApp and UNUserNotificationCenter when applying the changes.

Comment on lines +3 to +7
{
"idiom" : "universal",
"platform" : "ios",
"size" : "1024x1024"
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check for actual image files in the AppIcon.appiconset directory

echo "=== Checking for icon image files ==="
fd -e png -e jpg . "ios/pointron/PointronWidgetExtension/Assets.xcassets/AppIcon.appiconset"

Repository: 21nCo/nucleum

Length of output: 95


🏁 Script executed:

cat "ios/pointron/PointronWidgetExtension/Assets.xcassets/AppIcon.appiconset/Contents.json"

Repository: 21nCo/nucleum

Length of output: 234


🏁 Script executed:

ls -la "ios/pointron/PointronWidgetExtension/Assets.xcassets/AppIcon.appiconset/"

Repository: 21nCo/nucleum

Length of output: 228


Add the app icon image file to the asset catalog.

The AppIcon.appiconset directory is missing the actual image file. The Contents.json defines a 1024x1024 universal app icon but contains no filename reference, and no image files exist in the directory. Add the icon image file to this asset catalog, otherwise the widget extension will display a placeholder icon instead of your app icon.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@ios/pointron/PointronWidgetExtension/Assets.xcassets/AppIcon.appiconset/Contents.json`
around lines 3 - 7, The AppIcon.appiconset is missing the actual 1024x1024 image
and a filename entry in Contents.json; add a 1024x1024 PNG (e.g.,
AppIcon-1024.png) into the AppIcon.appiconset folder and update Contents.json to
include a "filename" field for the entry with "size":"1024x1024" (keeping
"idiom":"universal" and "platform":"ios"), and ensure the asset is included in
the widget extension target so the widget uses the real icon instead of a
placeholder.

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.

1 participant