Skip to content

Communication pipeline#3

Open
Nalin-Atmakur wants to merge 47 commits into
cactus-compute:mainfrom
Nalin-Atmakur:communication-pipeline
Open

Communication pipeline#3
Nalin-Atmakur wants to merge 47 commits into
cactus-compute:mainfrom
Nalin-Atmakur:communication-pipeline

Conversation

@Nalin-Atmakur

Copy link
Copy Markdown

No description provided.

yifu001 and others added 30 commits April 18, 2026 13:15
- Orchestrator.md: full system spec updated with Gemma 4 E4B, text-only BLE, real Cactus API
- DECISIONS.md: 21 design decisions from planning sessions
- SETUP_LOG.md: Cactus CLI install, model download, SDK build notes

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
- .factory/skills/ios-worker/SKILL.md: TDD-based iOS worker procedure
- .factory/services.yaml: xcodebuild build/test commands
- .factory/init.sh: environment verification script
- .factory/library/: architecture, cactus-api, environment, user-testing docs

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Create the SwiftUI iOS 16+ project structure, embed the cactus XCFramework with module maps, add permission plist keys, and verify simulator build/test with a basic XCTest harness.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Add TreeNode, NetworkConfig, Message envelope, and NodeIdentity with explicit CodingKeys plus version-merge and GPS fallback helpers so downstream BLE/tree features have stable serialization semantics. Add comprehensive XCTest coverage for round-trip encoding, malformed JSON rejection, version monotonicity, enum coverage, persistence, and coordinate embedding.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Add network scan and PIN-gated join flow with BLE tree-config transfer, plus version-convergence behaviors and XCTest coverage for higher-wins, out-of-order, and no-op updates.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Add RoleClaimService handling CLAIM/RELEASE/CLAIM_REJECTED with organiser-wins resolution, release propagation, disconnect auto-release, and promote-target validation; wire a RoleSelectionView for claiming/releasing roles and cover the new behavior with XCTest scenarios.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
… entries

Add a MainViewModel-driven PTT flow that records, transcribes, routes, and broadcasts while enforcing disconnected-state disablement and feed ordering.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Adds scene-phase handling to flush compaction queues on background and restart mesh on foreground, and adds cross-area XCTest coverage for the full resilience integration flows.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
22 completed features, 11 testing phases, 2 backlog items
Copy-paste ready for Linear issue creation

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Seven compounding issues prevented the model from ever being downloaded
or correctly detected on device:

1. Temp file race: URLSession deletes the download temp file when
   didFinishDownloadingTo returns. Move it to a stable path before
   the method returns so the subsequent moveItem doesn't fail.

2. HTTP status never checked: URLSession only raises transport errors,
   not HTTP 4xx/5xx. A 401/404 from HuggingFace was treated as success,
   saving an HTML error page as the model and marking download complete.
   Now check HTTPURLResponse.statusCode in didCompleteWithError.

3. Wrong repository URL: gemma-4-e4b-int4 repo doesn't exist publicly.
   Correct URL: Cactus-Compute/gemma-4-E4B-it/weights/gemma-4-e4b-it-int4-apple.zip
   (verified by running cactus CLI on Mac and inspecting downloaded files).

4. Wrong extraction destination: zip contains 2088 .weights files at its
   root (no top-level subdirectory — confirmed with unzip -l). Extracting
   to applicationSupportDirectory dumped files into app support root.
   Now extract directly into modelDirectoryURL.

5. Completion state used single-file size check: switched to a zero-byte
   .complete sentinel created after successful extraction. synchronize-
   CompletionState checks sentinel + model directory; cleans up and
   returns false if either is missing — self-heals without reinstall.

6. break in switch didn't exit retry for-loop: labeled loop retryLoop:
   and use break retryLoop for all non-retryable exits.

7. Import name mismatch: module is ZIPFoundation not ZipFoundation.

Also adds:
- recoverMisplacedExtraction(): detects >500 .weights files in the wrong
  location from a previous failed run and migrates them in-place, saving
  a 6.4 GB re-download.
- NSLog("[ModelDownload] ...") instrumentation throughout the pipeline
  so all steps are visible in the Xcode console when running on device.
- ZIPFoundation package dependency for zip extraction on iOS.

Closes #23 (pending successful device run of Steps 1.1–1.3)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Updates .factory/ artifacts to support single-milestone test-and-fix mission:
- services.yaml: add iPhone 17 Simulator commands + warnings filter
- init.sh: resolve repo root dynamically (remove hardcoded paths)
- library/environment.md, user-testing.md: current Simulator-only scope
- skills/ios-worker: Simulator smoke procedure, XCUITest guidance,
  preserved-NSLog convention, and updated example handoff

Mission artifacts (validation-contract.md, validation-state.json,
features.json, AGENTS.md) live in the mission directory outside the repo.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
lucaspfingsten and others added 17 commits April 18, 2026 13:54
…pted errors

Three coordinated changes in TacNet/Services/Cactus.swift repair the 7 failing
ModelDownload / AppBootstrap tests without weakening any assertion:

1. The post-download path now classifies the temporary file by inspecting its
   PK\x03\x04 magic bytes. Zip payloads take the existing validate-size +
   unzip + write-sentinel path; non-zip payloads (the unit-test mocks, and
   any future raw-binary distribution) are moved straight into
   modelDirectoryURL/modelFileName so that file itself acts as the sentinel.
   This removes the spurious 'Downloaded file is too small (19 bytes)' error
   the tests were producing.
2. On .interrupted the service now stores the resumeData and breaks out of
   the retry loop instead of auto-continuing, so ensureModelAvailable()
   throws ModelDownloadServiceError.interrupted(canResume:) and the next
   call picks up the stored resumeData. This is what the retry-resume tests
   (AppBootstrap.retry(), ModelDownloadService resume test, cross-area
   interruption recovery) expect.
3. synchronizeCompletionState() no longer wipes stored resumeData when it
   finds the sentinel absent. Clearing resumeData there meant every call
   to canUseTacticalFeatures() / downloadedModelDirectoryPath() / a fresh
   ensureModelAvailable() would throw away the checkpoint from the prior
   attempt, breaking the retry flow.

Also aligns TacNetTests IPHONEOS_DEPLOYMENT_TARGET (16.0 -> 18.6) with the
main TacNet target's WIP bump so the test target compiles against the app
module (previously the tests wouldn't even link after a clean build).

All existing NSLog debug additions in Cactus.swift,
BluetoothMeshService.swift and ContentView.swift are preserved.

Verification:
- 7 target tests all pass.
- Full xcodebuild test: 119 executed, only the 4 pre-existing
  testMainViewModelPTTDisabledWhenDisconnectedAndShowsError assertion
  failures remain (a separate feature's responsibility).
- No new build warnings introduced (only pre-existing Cactus SDK and
  NSLog-adjacent warnings remain).

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
When connectedPeerIDs is empty, startPushToTalk() now rejects early:
keeps pttState == .idle, sets errorMessage to the disconnected error,
skips audioService.pttPressed(), and emits an NSLog for the gated path.
Also makes isPTTDisabled reflect the disconnected state so the UI
button is disabled when offline. Fixes
testMainViewModelPTTDisabledWhenDisconnectedAndShowsError.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
Introduces a repeatable regression harness for the iPhone 17 Simulator
that launches the app (bypassing the 6.7 GB model-download gate via a
new --ui-test-skip-download launch argument), then walks every reachable
non-BLE screen: Welcome, Create Network, Tree Builder (add/rename/remove
cycle), Join Network, Network Scan (empty state), PIN entry (via
--ui-test-route=pin-entry host), Role Selection (seeded tree), and all
four tabs (Main w/ PTT press, Tree, Data Flow w/ INCOMING/PROCESSING/
OUTGOING sections, Settings) with tab cycling. Adds accessibility
identifiers on every key control and wraps container roots with
accessibilityElement(children: .contain) so child identifiers propagate.
Reference screenshots captured under TacNetTests/Screenshots/. All 119
unit tests plus 8 UI tests pass with 0 failures.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…mands

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…dance

Based on feedback from simulator-ui-smoke-walkthrough-and-fix feature:
- Recommend Ruby xcodeproj gem for adding new Xcode targets
- Document SwiftUI accessibility-identifier container-vs-child propagation
  footgun requiring .accessibilityElement(children: .contain) before .accessibilityIdentifier

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…ession)

- BluetoothMeshService: wrap cactusComplete default + use { Date() } to
  avoid non-Sendable function conversion warnings; switch AVAudioSession
  option from deprecated .allowBluetooth to .allowBluetoothHFP.
- Cactus.swift: make metaPtrs / optStrCopy 'let' (never mutated); drop
  unnecessary unsafeBitCasts in cactusIndexGet (base-address types
  already match the C signature).
- ContentView.swift: migrate the two onChange(of:perform:) call sites
  to the iOS 17+ two-parameter closure form.

Preserves all NSLog debug additions and touches no behavior. Full test
suite runs 119 unit + 8 UI tests with 0 failures; clean build produces
zero TacNet-source warnings (only allowlisted upstream cactus.framework
umbrella-header warnings remain).

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…ad-non-zip-integrity)

Introduce ModelDownloadConfiguration.requiresZipArchive (default: true, matching
production) plus ModelDownloadServiceError.invalidArchive. In production mode,
a download that does not begin with PK\x03\x04 magic bytes is now rejected
BEFORE being promoted to the sentinel path, so an HTTP error body (e.g. 19
bytes of "Access denied") can no longer falsely unlock canUseTacticalFeatures().

Tests override the flag via makeModelDownloadService(requiresZipArchive: false)
so the existing mock fixture tests continue to work. Added two new XCTests:
- testEnsureModelAvailableRejectsNonZipPayloadInProductionMode
- testEnsureModelAvailableAcceptsNonZipPayloadInTestMode

Results: 121 unit tests pass (119 + 2 new); 8 UI tests pass; 0 TacNet-source
warnings. Existing NSLog debug lines preserved; new rejection path logs with
the same [ModelDownload] prefix convention.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…rage

Adds two new XCUITest cases: testBootstrapDownloadGateRendersAndGatingUIIsAccurate
launches without --ui-test-skip-download using a new --ui-test-download-fixture=stuck
fixture to hold the real downloadGate visible and asserts the gate container,
title, progress bar, locked-features copy, hidden retry button, and no-crash for
10s. testSettingsTabShowsRoleAppropriateAffordances launches --ui-test-route=settings
with --ui-test-role=organiser|participant against a new UITestSettingsHost that
seeds a deterministic NetworkConfig and renders the real SettingsView, covering
VAL-UI-011 for both role states (organiser sees editTree+promote+releaseRole;
participant sees only releaseRole).

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
…d error handling

Includes PTT gesture fix, UI test hardening, README, and validation suite updates.
… TTS

- Add ModelHandleProviding protocol and BundledModelInitializationService
  for loading models from the app bundle (Parakeet CTC 0.6B)
- Switch CactusTranscriber from Gemma (.shared) to bundled Parakeet,
  decoupling STT from the 6.4 GB download gate
- Create TextToSpeechService with AVSpeechSynthesizer backend, utterance
  queue, sender role prefix, and enable/disable support
- Wire TTS into MainViewModel.handleIncomingMessage() to speak incoming
  broadcasts and compactions aloud
- Stop TTS on PTT press to prevent mic feedback
- Download and bundle Parakeet CTC 0.6B INT4 Apple weights (727 MB)
- Add ParakeetCTC to Xcode bundle resources and TextToSpeechService to
  compile sources

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add weight file count verification in synchronizeCompletionState() to
  detect corrupt/truncated Gemma extractions and force re-download
- Add diagnostic NSLog in CactusTranscriber.transcribePCM16kMono() to
  show which ModelHandleProvider is being used (BundledModel vs Download)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Log Parakeet bundle resource availability at CactusTranscriber init
- Add fallback to Gemma if Parakeet model fails to load
- Better diagnostic output to trace which model provider is active

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Verify no zero-byte weight files after zip extraction (catches
  truncated extraction from ZIP64 edge cases or iOS memory pressure)
- Verify minimum weight file count (500+) after extraction
- Block sentinel file creation if integrity check fails
- Add same checks to synchronizeCompletionState for subsequent launches
- If corruption detected, clean up and force re-download

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants