Conduck 1.3 (build 9)
Conduck 1.3 (build 9) — macOS App Store release, 19 August 2026.
The file-lane, attention and trust release. ~240 commits since 1.2.
Install it: App Store — free for individuals.
Built from: commit 51fccc2, tagged v1.3-9 — marketing version 1.3, build 9. That tag is the exact source the shipped binary was built from.
Building this release yourself
Requires Xcode 26.5 or later. Open Conduck/Conduck.xcodeproj, build and run — no configuration needed.
What you get is Conduck Community: the same functionality under a neutral identity with placeholder artwork, minus CarPlay, which needs an Apple entitlement granted per developer team and cannot travel with source. No functional code is withheld from this repository — what is not here is the brand artwork, the signing identity, and that entitlement. See TRADEMARKS.md for what you may call a build you distribute.
| Platform | Minimum OS |
|---|---|
| iOS / iPadOS | 26.5 |
| macOS | 26.5 |
| watchOS | 26.5 |
Software bill of materials
sbom-v1.3-9-51fccc2.json is attached: CycloneDX 1.5, naming this release's three direct dependencies with the exact revisions they were pinned to and their licences. Bundled licence texts are in THIRD_PARTY_NOTICES.md.
What is in 1.3
Files — the agent-file return lane rebuilt
- Per-conversation output folders; every dispatch names its own and creates nothing (the AGENT creates it — a client-created folder locks the agent out on the two gateways most people use)
- A reply's files come from the folder it was given, never parsed from its prose; an agent's refusal could previously mint four downloads, one from another conversation
- Nothing downloads before a tap — up to 8 MB used to move on its own into a store that syncs to iCloud and the Watch
- A hand-back is believed only when the server can also say no: probes read the body, and no "exists" verdict survives without a negative control on the same lane
- Returned files keep their real name — the inbound gate was
[A-Za-z0-9._-], so spaces, accents and every CJK name were listed, seen and discarded in silence - File-delivery capability is a property of each gateway; an upload-only server keeps its uploads, and only a structural 405/501 proves a server cannot list
- A file that lands after the reply is no longer lost; a turn that got no folder says so when that is news; a refused name is reported as a census
- Watch receives a returned file over the WatchConnectivity link (iCloud mirror measured seven minutes); no bytes, no credential
- Clone carries its attachments and offers to resend the unanswered turn
Attention — the conversation list
- Rows resolve to working / answered-unseen / failed / idle; a failure is reported only while it is still the last activity
- Unread + failure acknowledgement are account facts, mirrored across devices and reaching the Watch; acknowledgement is keyed by delivery-attempt identity, so a retry re-arms it
- Badges follow what the history spans, not what is configured; a forgotten custom keeps two characters and a colour so its archive does not go blank
- A new chat starts on the gateway the last one used, via an App-Group pointer that never rides iCloud
- macOS gains a reply notification, a quit-mid-turn confirmation and burst coalescing; the Watch writes its own sending status
Trust and security
- A publicly-trusted certificate is now REQUIRED; a pin is an optional additional restriction on a chain the system already trusts. TOFU, the consent UI and every pin-as-authorisation path are deleted, and
certFPis gone fromconduck-setup:v1 - Cross-host and scheme-downgrading redirects are refused rather than replayed with history, images, audio, file bytes and the auth token
- Markdown image/emoji loading in replies is blocked outright; untrusted text is projected before it reaches a notification, headline, CarPlay row or VoiceOver label
- Forgetting the last gateway now reaches the Watch, which kept a live route and token across reboots; a deleted voice endpoint takes its key with it
- macOS sends ran on a session that structurally could not carry a pin, silently dropping a configured one that Test Connection honoured
Sync and multi-device correctness
- The XCTest suite had been writing into the real App Group, iCloud KVS and synchronizable Keychain, syncing fixture gateways to every paired device; a storage seam plus a CI guard closes it
- Gateway URL/model sync is hydrate-only, so a device offline during a peer's Forget no longer resurrects the gateway for everyone
- Inbound mirrors added for
remoteAgent.model.*and built-inauthScheme.*; a built-in default pointer is always honoured, where healing the fallback used to move every message to another server
Performance
- Reply-rendering scans that were quadratic are single passes; a 4 MB reply went from 16.4 MiB to 48 KiB of transient allocation
- Claim ordering on a 16 MiB reply produced ~16.8M Substrings and half a gigabyte live — a crash CloudKit would have synced everywhere; now bounded and off the main actor
Setup, errors and diagnostics
- Setup-code review screen: says where a code points, masked at rest, carries the file server's measured capabilities; readiness stops gating on what the next step supplies
- Transport failures split by whether the request could have reached the server; Test Connection and a failed send finally agree
- Free per-gateway recheck; Copy Diagnostics carries recent failed sends and a chat-proven record; a leftover gateway is named, marked and counted once
- Out of credits and rate-limited keep Try again; a 403 no longer asserts the token is wrong; every error names the AI the user configured
- The iOS row stops claiming the gateway is answering while the background session is still waiting for connectivity
macOS and iPad
MacPointerTargets: every mouse-reachable custom-drawn control is properly clickable; one settings rail, full-bleed rows, persistent back chrome- The sidebar fits its window (it was laid out 1399pt tall inside 949pt and centred); compose is pinned beside the sidebar toggle in every split state
- The window asks whether ANY gateway can send, not whether the default one can; Esc targets the innermost editor instead of tearing down Settings
- Drop a file anywhere on the conversation; iPad compose lives on whichever column's bar is on screen
Voice and Watch
- Typed TTS playback outcomes route undecodable bytes, refused starts and mid-clip errors to the Apple fallback with transparency, where they used to end the turn in silence
- Device-local TTS key readiness banner plus a bounded arrival monitor, since iCloud Keychain has no arrival event
- An unreadable STT key is not an absent one — the recording outlives the refusal; a queued Watch capture the iPhone could not read is kept
- Long errors are fully reachable on the wrist via a scrollable detail sheet
Project
- Apache-2.0, SPDX headers on every tracked source file, in-app Open Source Licenses screen, DCO sign-off hook
- spec.md + project-structure.md rewritten from 648 KB to 62 KB as decisions rather than description
- CI source guards on Linux; the macOS TLS test bundle is compiled unconditionally
Release-gating fixes made in this build
- ConverseIntent referenced DEBUG-only
RemoteAgentDiagnosticson a line meant to ship — Release failed to compile on both platforms; it now uses an always-compiledos.Logger - Two test files referenced iOS-only symbols ungated (
AppleSpeechRelayCoordinator,CarPlaySceneDelegate), breaking the macOS test-bundle compile and with it the live TLS trust suite
Verified: iOS 3925 tests / 0 failures · watchOS 206 / 0 · live TLS 16 / 0 · Release builds green on iOS and macOS · SBOM notices gate clean