From 75185cb19d107c9d6a98e04a285ed962f583f0c2 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 1 May 2026 14:15:05 +0000 Subject: [PATCH 1/3] Add CLAUDE.md operating guide Index doc for Claude Code sessions: cheap-gates-first command list, process-isolation architecture overview (macOS XPC + iPhone ExtensionKit), retained-vs-live runtime split caveat, repo-specific rules (no Python backend, project.yml authoritative, target exclusions), Swift 6 concurrency footguns, and the change-X-update-Y mapping. Defers full detail to AGENTS.md. --- CLAUDE.md | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..f86bcce --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,160 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Companion Docs + +- `AGENTS.md` is the full repo operating guide (Codex-authored, but authoritative for Claude too). Treat this file as the fast index; fall back to `AGENTS.md` for detail. +- `README.md` covers product surface, supported platforms, and the basic build flow. +- `CONTRIBUTING.md` covers contributor flow. +- `docs/reference/current-state.md`, `docs/reference/engineering-status.md`, and `docs/reference/release-readiness.md` are the maintained engineering references. + +## Source Of Truth + +When repo facts disagree, trust in this order: + +1. `Sources/` +2. `project.yml` +3. `scripts/` plus `.github/workflows/` +4. `docs/reference/current-state.md`, `engineering-status.md`, `release-readiness.md` +5. other prose docs + +`Sources/Resources/qwenvoice_contract.json` is the source of truth for shared model, speaker, variant, output, and required-file metadata. + +## Common Commands + +Always start with the cheap gates: + +```sh +./scripts/check_project_inputs.sh +python3 scripts/harness.py validate +``` + +Project regen (after editing `project.yml`): + +```sh +./scripts/regenerate_project.sh +``` + +Builds: + +```sh +xcodebuild -project QwenVoice.xcodeproj -scheme QwenVoice build +xcodebuild -project QwenVoice.xcodeproj -scheme VocelloiOS \ + -destination 'generic/platform=iOS' \ + CODE_SIGNING_ALLOWED=NO ONLY_ACTIVE_ARCH=YES build +./scripts/build_foundation_targets.sh macos +./scripts/build_foundation_targets.sh ios +``` + +Tests (the harness is the single entrypoint; pick the layer you need — there is no "single test" knob below the layer): + +```sh +python3 scripts/harness.py test --layer contract +python3 scripts/harness.py test --layer swift +python3 scripts/harness.py test --layer native +python3 scripts/harness.py test --layer ios # structurally skips when no iOS simulator is installed +python3 scripts/harness.py test --layer e2e +QWENVOICE_E2E_STRICT=1 python3 scripts/harness.py test --layer e2e # release-signoff strict mode +python3 scripts/harness.py diagnose +``` + +Benchmarks are opt-in, never default gates: + +```sh +python3 scripts/harness.py bench --category latency|load|quality|tts_roundtrip --runs 3 +``` + +Local rescue and release: + +```sh +./scripts/rescue_gate.sh --fast # docs / static cleanup +./scripts/rescue_gate.sh # full lane; gates on swap, override via QW_RESCUE_SWAP_LIMIT_MB +./scripts/release.sh --preflight full +./scripts/verify_release_bundle.sh build/Vocello.app +./scripts/verify_packaged_dmg.sh build/Vocello-macos26.dmg build/release-metadata.txt +``` + +Harness artifacts: `build/harness/{derived-data,results,source-packages,artifacts}`. On failure, inspect the `.xcresult` bundle: + +```sh +xcrun xcresulttool get build-results --path build/harness/results//... +xcrun xcresulttool get test-results summary --path build/harness/results//... +``` + +SourceKit diagnostics like `No such module 'MLX'` or `Cannot find type X in scope` after an edit are index staleness. Trust only `xcodebuild`, `scripts/harness.py`, and `./scripts/build_foundation_targets.sh`. + +## Architecture + +The repo identity stays `QwenVoice`; the shipping macOS bundle is `Vocello.app` (its Swift `PRODUCT_MODULE_NAME` is kept as `QwenVoice` so sibling modules still resolve). The iPhone app is `Vocello` for iPhone, currently deferred from public release. + +**macOS process split** + +- App process: `Sources/QwenVoiceApp.swift` composes app-global services and runs `AppEngineSelection`; `Sources/ContentView.swift` owns the `NavigationSplitView` chrome and persisted drafts. +- App-side engine layer: `Sources/QwenVoiceNative/` — `TTSEngineStore`, `XPCNativeEngineClient`, chunk brokering, `MacTTSEngine`. +- Transport boundary: `Sources/QwenVoiceEngineSupport/`. +- Out-of-process helper: `Sources/QwenVoiceEngineService/` (XPC service, hosts the active shared-core runtime through `QwenVoiceCore`). + +**iPhone process split** + +- App shell: `Sources/iOS/` + `Sources/iOSSupport/` (SwiftUI, model delivery UX, library/history, memory-pressure coordination). +- Engine extension: `Sources/iOSEngineExtension/` runs heavy generation/prewarm out of the UI process via ExtensionKit. Discovery: `Sources/iOS/VocelloEngineExtensionPoint.swift`. Active transport replacement / teardown: `Sources/QwenVoiceCore/ExtensionEngineHostManager.swift`. + +**Cross-platform core** + +- `Sources/QwenVoiceCore/` owns shared engine semantics, contract types, model-variant resolution, and ExtensionKit transport. Keep it free of app-process UI assumptions. +- `Sources/SharedSupport/` owns shared playback and generation-persistence surfaces consumed by both shells. +- `Sources/Services/AppPaths.swift` (macOS) and `Sources/iOSSupport/Services/AppPaths.swift` (iPhone) are the runtime-data path boundaries. Default macOS data root: `~/Library/Application Support/QwenVoice/{models,outputs,voices,history.sqlite}` (override via `QWENVOICE_APP_SUPPORT_DIR`). + +**Retained-vs-live runtime split** + +`Sources/QwenVoiceNativeRuntime/` keeps duplicate copies of runtime types (notably `NativeStreamingSynthesisSession`) alongside the live `Sources/QwenVoiceCore/` implementations. Behavior fixes in shared streaming/session semantics often need to land in **both** copies until the split is consolidated. + +**Manifests** + +- `Sources/Resources/qwenvoice_contract.json` is loaded by `Sources/Models/TTSContract.swift`, `Sources/Models/TTSModel.swift`, and the `QwenVoiceCore` semantic types. +- `config/apple-platform-capability-matrix.json` is the release-verification baseline for bundle ids, app groups, opportunistic memory-limit entitlements, and packaged-resource exclusions. + +**Vendored backend** + +`third_party_patches/mlx-audio-swift/` is the repo-owned MLXAudioSwift source. Keep its package manifest pinned with `project.yml` and `Package.resolved`. + +## Repo-Specific Rules + +These override generic agent defaults — read them before editing. + +- **No repo-owned Python backend, Python setup path, or standalone CLI.** Do not reintroduce one. +- **Git default**: work on the user-designated branch (currently `claude/init-project-7ZdXV`). Do not create extra branches or worktrees unless the user asks. +- **Edit `project.yml`, never the generated `*.xcodeproj` files directly.** Run `./scripts/regenerate_project.sh` after structural changes. Watch outputs for accidental `__pycache__`, `.pyc`, `.DS_Store`, or `.profraw` paths. +- The macOS app target intentionally **excludes** `QwenVoiceEngineService/`, `QwenVoiceEngineSupport/`, `QwenVoiceNativeRuntime/`, `iOS*/`, `Resources/ffmpeg/`, and `Resources/vendor/` from its `Sources` glob; the XPC service is embedded as a separate target. Keep that split intact. +- **Process isolation is a product requirement** on both platforms. Do not move heavy generation, prewarm, or model-load work back into the UI process. +- **Platform floors**: macOS 26.0+ and iOS 26.0+; Apple Silicon; Mac mini M1 8 GB / iPhone 15 Pro minimums. iPhone uses 4-bit Speed only; macOS defaults to 4-bit Speed and may expose 8-bit Quality where admission allows. `macOS 15` and `QW_UI_LEGACY_GLASS` are retired — do not restore. +- **`QW_TEST_SUPPORT`** is a Debug/test-only Swift compilation condition (stub engines, fault injection, fixtures, opt-in benchmark hooks). Release builds must not depend on it. +- **Native SwiftUI discipline**: prefer standard SwiftUI primitives plus `Sources/Views/Components/AppTheme.swift` (macOS) or the iPhone shell theme. Do not reintroduce the removed desktop-studio shell, generated-reference redesign, hero chrome, inspector layout, or full-window footer player. +- **Operational safety on this 8 GB dev machine**: never overlap heavy `xcodebuild`, `scripts/harness.py`, release packaging, live app validation, or benchmark processes. Kill old `QwenVoice`/`Vocello` instances before launching a new build. + +## Swift Concurrency Gotchas + +- `Self` cannot be referenced inside a `static let` initializer on a class — use the concrete type name (e.g. `EngineServiceHost.logger`) instead of `Self.logger`. +- `Task.detached { ... }` does not inherit cancellation. If cancellation must propagate, wrap `try await task.value` in `withTaskCancellationHandler { ... } onCancel: { task.cancel() }`. +- `AsyncThrowingStream` consumers do not automatically observe the consuming task's cancellation when the producer runs in its own `Task`. Add `try Task.checkCancellation()` at the top of `for try await` loop bodies. +- When promoting a helper out of a `@MainActor`-isolated class to module scope, mark the closure parameter `@MainActor` (e.g. `condition: @escaping @MainActor () -> Bool`) and invoke via `await MainActor.run(body: condition)`. Without this, Swift 6 flags call sites as "Sending risks data race". + +## When Changing X, Also Update Y + +- Model registry / speakers / output folders / required model files / install variants → `Sources/Resources/qwenvoice_contract.json` first, then contract loaders and contract-facing docs. +- Adding or renaming source files → `project.yml`, then `./scripts/regenerate_project.sh`. +- Shared engine semantics or model-variant resolution → review `Sources/QwenVoiceCore/` and iPhone model delivery together; mirror retained runtime copies in `Sources/QwenVoiceNativeRuntime/` when applicable. +- macOS engine/client behavior → review `QwenVoiceNative/`, `QwenVoiceEngineSupport/`, and `QwenVoiceNativeRuntime/` together. +- iPhone engine extension transport / host → review `QwenVoiceCore/Extension*`, `iOSEngineExtension/`, and `iOS/VocelloEngineExtensionPoint.swift` together. +- Memory pressure / prewarm / low-RAM admission → `QwenVoiceCore/IOSMemorySnapshot.swift`, `iOS/TTSEngineStore.swift`, `iOS/QVoiceiOSApp.swift`, plus iPhone settings/status UI. +- Playback or generation persistence → `Sources/SharedSupport/` and the affected feature views. +- macOS release packaging / notarization → `scripts/release.sh`, `scripts/create_dmg.sh`, `scripts/verify_release_bundle.sh`, `scripts/verify_packaged_dmg.sh`, `.github/workflows/macos-release.yml`, and release-facing docs. +- iPhone TestFlight → `scripts/check_ios_catalog.py`, `scripts/release_ios_testflight.sh`, `scripts/verify_ios_release_archive.sh`, `.github/workflows/ios-testflight.yml`. + +## Before Finishing + +- Prefer manifest-backed data over duplicated constants. +- Keep accessibility identifiers stable when UI control types change. +- Re-run `./scripts/check_project_inputs.sh` and `python3 scripts/harness.py validate` plus the most relevant harness layer before declaring work complete. +- If you changed engine architecture, runtime ownership, or release behavior, verify that `AGENTS.md` and `docs/reference/current-state.md` still describe the same split. From 794c0bfd8d17b5bc522a9a29521c3ebc6a5ca71a Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 1 May 2026 14:25:34 +0000 Subject: [PATCH 2/3] Allow CLAUDE.md and Claude Code references in project-inputs check Drops the CLAUDE.md / "Claude Code" / claude.ai/code substring bans from PROHIBITED_REFERENCE_PATTERNS so the Claude Code operating guide can coexist with AGENTS.md. Keeps the .claude/worktrees ban (separate concern: enforces the no-extra-worktrees git workflow rule). --- scripts/check_project_inputs.sh | 3 --- 1 file changed, 3 deletions(-) diff --git a/scripts/check_project_inputs.sh b/scripts/check_project_inputs.sh index 77a9384..5404708 100755 --- a/scripts/check_project_inputs.sh +++ b/scripts/check_project_inputs.sh @@ -59,9 +59,6 @@ PROHIBITED_REFERENCE_PATTERNS=( "docs/reference/testing.md" "QwenVoice-macos15.dmg" "build/QwenVoice.app" - "CLAU""DE.md" - "Clau""de Code" - "claude.ai/code" ".claude/worktrees" ) From 4059c4c293f62da6faf9cd6bd12dd70298276ed7 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 1 May 2026 14:29:35 +0000 Subject: [PATCH 3/3] Replace AGENTS.md with CLAUDE.md as the primary operating guide The project is now developed primarily in Claude Code, so the agent operating guide moves to CLAUDE.md. This commit: - expands CLAUDE.md to absorb every operationally meaningful section of AGENTS.md (repo overview, maintained docs, source-of-truth order, architecture boundaries, platform/product constraints, SwiftUI discipline, command surface, Swift concurrency gotchas, CI/release workflows, change-X-update-Y mapping, operational safety) - drops the Codex-specific Computer Use / Desktop UI validation section - deletes AGENTS.md - updates docs/README.md, docs/qwen_tone.md, docs/reference/current-state.md, docs/reference/backend-freeze-gate.md, and docs/reference/frontend-backend-contract.md to point at CLAUDE.md - removes the `.claude/worktrees` ban from scripts/check_project_inputs.sh PROHIBITED_REFERENCE_PATTERNS so the Claude Code workflow is no longer policed by a substring match --- AGENTS.md | 282 -------------------- CLAUDE.md | 278 ++++++++++++++----- docs/README.md | 2 +- docs/qwen_tone.md | 2 +- docs/reference/backend-freeze-gate.md | 2 +- docs/reference/current-state.md | 2 +- docs/reference/frontend-backend-contract.md | 2 +- scripts/check_project_inputs.sh | 1 - 8 files changed, 209 insertions(+), 362 deletions(-) delete mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index e2c5a86..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,282 +0,0 @@ -# AGENTS.md - -This file provides guidance to Codex (Codex.ai/code) when working with code in this repository. It is the primary repo operating guide for coding agents working in QwenVoice. - -## Repo Overview - -QwenVoice is the repository identity and continuity brand for the merged Vocello Apple-platform product line. - -Current product reality: - -- the repo stays `QwenVoice` -- the shipped iPhone app is `Vocello` -- the next macOS release ships as `Vocello.app` inside `Vocello-macos26.dmg`; the supporting framework/service/runtime modules (`QwenVoiceCore`, `QwenVoiceEngineService`, `QwenVoiceEngineSupport`, `QwenVoiceNative`, `QwenVoiceNativeRuntime`) keep their `QwenVoice` names internally for continuity -- the current public milestone uses a `macOS-first release track`, with iPhone retained as a compile-safe and deferred release surface - -The main working surfaces are: - -- `Sources/` for the macOS app shell, shared app models/services/views, and the shipping Mac target -- `Sources/QwenVoiceCore/` for shared Apple-platform runtime semantics, contract types, model variants, and iOS extension transport -- `Sources/QwenVoiceNative/` for the macOS app-facing engine proxy/store/client layer -- `Sources/QwenVoiceEngineSupport/` for shared macOS engine IPC and transport types -- `Sources/QwenVoiceNativeRuntime/` for retained macOS compatibility and regression coverage -- `Sources/QwenVoiceEngineService/` for the bundled macOS XPC helper -- `Sources/iOS/` and `Sources/iOSSupport/` for the iPhone app shell and iPhone-only support layers -- `Sources/SharedSupport/` for cross-platform playback, persistence, and other shared app-layer helpers -- `Sources/iOSEngineExtension/` for the isolated iPhone engine extension target -- `Sources/Resources/qwenvoice_contract.json` for shared model, variant, speaker, output, and required-file metadata -- `scripts/` plus `.github/workflows/` for validation, release packaging, and CI behavior -- `config/apple-platform-capability-matrix.json` for the maintained cross-platform capability, bundle-identity, and entitlement baseline used by release verification - -This checkout is a native Apple-platform codebase for macOS and iPhone. Do not reintroduce a repo-owned Python backend, Python setup path, or standalone CLI surface. - -## Maintained Docs - -The maintained repo docs are: - -- `AGENTS.md` -- `README.md` -- `docs/README.md` -- `docs/qwen_tone.md` -- `docs/reference/current-state.md` -- `docs/reference/engineering-status.md` -- `docs/reference/backend-freeze-gate.md` -- `docs/reference/frontend-backend-contract.md` -- `docs/reference/release-readiness.md` -- `docs/reference/live-testing.md` -- `docs/reference/vendoring-runtime.md` -- `docs/reference/mlx-audio-swift-patching.md` - -There are no repo-tracked local skills under `.agents/skills/` in this checkout right now. Do not point contributors at removed CLI docs, deleted backend references, or deleted repo-scoped QwenVoice skills. - -Public homepage posture: - -- `README.md` leads with `QwenVoice` because that is the currently shipped public brand (`v1.2.3`). Its "A Note on What's Changing" section frames `Vocello` as the forward rebrand that lands with the next macOS release. -- The GitHub repo description must stay consistent with the README — do not claim a Vocello-first public posture while the published release is still QwenVoice-branded. -- Leave the GitHub homepage URL blank unless the user explicitly asks to restore or change it. -- Keep public messaging aligned with the currently shipped macOS product reality and the active `macOS-first release track`. -- Do not present iPhone as a current public release surface until the release-track policy changes. The iPhone app is framed publicly as the in-development "Vocello for iPhone" — standalone, 4-bit, open source in this repo, published via the App Store once ready. - -Current release-track policy: - -- The next public release target is macOS only. -- The next macOS release ships under the `Vocello` brand as `Vocello-macos26.dmg` and requires `macOS 26.0` as the minimum. `macOS 15` was supported only on the already-shipped `QwenVoice v1.2.3` and is retired going forward. -- Do not retroactively edit `QwenVoice v1.2.3` release notes or the shipped-state sections of `README.md` — they must stay accurate to what shipped. -- Keep iPhone green at generic compile level on `main`, but do not treat iPhone release/TestFlight proof as blocking for the current milestone. -- Re-open iPhone release proof only through an explicit milestone change after the shared core is proven stable on macOS. - -## Source Of Truth - -When repo facts disagree, trust sources in this order: - -1. `Sources/` -2. `project.yml` -3. `scripts/` plus `.github/workflows/` -4. `docs/reference/current-state.md`, `docs/reference/engineering-status.md`, and `docs/reference/release-readiness.md` -5. other prose docs - -`Sources/Resources/qwenvoice_contract.json` is the source of truth for shared model, speaker, and platform-variant metadata. - -## Git Workflow Default - -- Work directly on `main` by default. -- Do not create branches or worktrees unless the user explicitly asks for one. -- Do not let generic tool, plugin, or skill defaults override this repo-specific rule. - -## Safe Edit Boundaries - -- `project.yml` drives `QwenVoice.xcodeproj`. Prefer editing `project.yml` and regenerating the project over hand-editing generated project files. -- The macOS app target intentionally excludes `Sources/QwenVoiceEngineService/`, `Sources/QwenVoiceEngineSupport/`, and `Sources/QwenVoiceNativeRuntime/` as ordinary app sources while embedding the XPC service target through `project.yml`. Keep that split intact. -- The iPhone app target and the iPhone engine-extension target both depend on `QwenVoiceCore`. Keep engine execution isolated from the iPhone UI process. -- `third_party_patches/mlx-audio-swift/` is the repo-owned native backend source boundary for MLXAudioSwift. Keep its package manifest and pins aligned with `project.yml` and `Package.resolved`. -- `config/apple-platform-capability-matrix.json` is the release-verification source of truth for bundle identifiers, expected application groups, opportunistic memory-limit entitlements, and packaged-resource exclusions. -- If `Sources/Resources/ffmpeg/` or `Sources/Resources/vendor/` appear locally, treat them as generated or local-only leftovers, not as maintained tracked checkout surfaces. -- App data under `~/Library/Application Support/QwenVoice/` or a `QWENVOICE_APP_SUPPORT_DIR` override is runtime state, not repo source. -- Watch for accidental `__pycache__`, `.pyc`, `.DS_Store`, and `.profraw` paths when regenerating or reviewing changes. - -## Architecture Boundaries - -- `Sources/QwenVoiceApp.swift` composes macOS app-global services, owns the separate Settings scene, and initializes the app-facing Mac engine through `AppEngineSelection`. -- `Sources/ContentView.swift` owns the macOS `NavigationSplitView`, toolbar/search chrome, sidebar selection, and persisted generation drafts. -- `Sources/QwenVoiceNative/` is the macOS app-side engine layer: `TTSEngineStore`, `XPCNativeEngineClient`, chunk brokering, and the app-facing `MacTTSEngine` surface live there. -- `Sources/QwenVoiceEngineSupport/` is the shared macOS engine transport boundary used by both the app and the helper. -- `Sources/QwenVoiceEngineService/` now hosts the active macOS shared-core runtime through `QwenVoiceCore`. Treat `Sources/QwenVoiceNativeRuntime/` as a retained compatibility surface rather than the primary live policy owner. -- `Sources/QwenVoiceEngineService/` owns the bundled macOS XPC helper entrypoint and session/host behavior. -- `Sources/QwenVoiceCore/` is the cross-platform engine core and shared semantic boundary. Keep it free of app-process UI assumptions. -- `Sources/iOSEngineExtension/` hosts the isolated iPhone engine process through ExtensionFoundation. Heavy generation and prewarm work belongs there, not in the iPhone UI app process. -- `Sources/iOS/VocelloEngineExtensionPoint.swift` owns monitor-backed iPhone extension discovery and preferred-identity selection, while `Sources/QwenVoiceCore/ExtensionEngineHostManager.swift` owns active transport replacement and teardown. -- `Sources/iOS/` and `Sources/iOSSupport/` own the iPhone SwiftUI shell, model delivery UX, library/history views, and memory-pressure coordination. -- `Sources/SharedSupport/` owns shared playback and generation-persistence surfaces that now serve both the macOS and iPhone apps. -- `Sources/Services/AppPaths.swift` and `Sources/iOSSupport/Services/AppPaths.swift` are the path boundaries for runtime data on each platform. -- The iPhone App Group surface is intentionally file-based and rooted under `Sources/iOSSupport/Services/AppPaths.swift`; keep shared state constrained to the required app-support subtree for models, downloads, outputs, voices, and cache data. -- `Sources/Models/TTSContract.swift`, `Sources/Models/TTSModel.swift`, and the `QwenVoiceCore` semantic types load `Sources/Resources/qwenvoice_contract.json`. -- `Sources/QwenVoiceNativeRuntime/` keeps retained copies of runtime types (notably `NativeStreamingSynthesisSession`) beside the live `Sources/QwenVoiceCore/` implementation. Behavior fixes in shared streaming/session semantics often have to land in both copies until the retained-vs-live split is consolidated. - -## Platform And Product Constraints - -- Minimum supported OS versions are `macOS 26.0+` and `iOS 26.0+`. -- The official minimum hardware floor is `Mac mini M1, 8 GB RAM` and `iPhone 15 Pro`. -- Process isolation is a product requirement on both platforms. Do not move heavy generation, prewarm, or model-load work back into the UI process. -- `QW_UI_LEGACY_GLASS` and macOS 15 compatibility are retired. Do not restore older dual-profile or dual-OS support. -- iPhone uses 4-bit `Speed` variants only. -- macOS defaults to 4-bit `Speed` on minimum hardware and can also expose 8-bit `Quality` when runtime admission allows it. -- Keep shared styling centralized in `Sources/Views/Components/AppTheme.swift` on macOS and in the iPhone shell primitives/theme layer on iOS. - -## Native SwiftUI Discipline - -- Keep UI work conservative, native, and stability-led. Prefer standard SwiftUI navigation, forms, lists, toolbars, sheets, controls, and system materials over theme-first redesigns. -- Do not reintroduce the removed desktop-studio shell, generated-reference redesign workflow, oversized hero chrome, inspector layout, full-window footer player, or decorative glass/card systems. -- Treat UI polish as small refinements to the existing app structure. Any broad layout change needs an explicit product decision and must not happen while runtime responsiveness or memory behavior is unstable. -- Keep generation screens responsive under backend activity: avoid speculative mode-switch work, broad environment-object invalidation, and visual effects that rebuild large view subtrees during prewarm or generation. -- Keep rescue work boring: no generated UI redesigns, no large Liquid Glass shell work, no screen-mount model warmup, and no overlapping heavy validation commands on this 8 GB development machine. - -## Required Workflows - -Start with repo truth first: - -- Search with `rg`, inspect source, manifests, scripts, and workflows before assuming docs are current. -- Prefer repo scripts, `python3 scripts/harness.py`, and `xcodebuild` over improvised one-off workflows. - -Fast gates: - -```bash -./scripts/check_project_inputs.sh -python3 scripts/harness.py validate -``` - -Serialized local rescue loop: - -```bash -./scripts/rescue_gate.sh --fast -./scripts/rescue_gate.sh -``` - -Use the fast lane for documentation and static cleanup. Use the full lane only when the current change justifies Swift tests and foundation builds; it prints swap usage before heavy work and stops early when local memory pressure is too high. Override the default 4 GB swap limit with `QW_RESCUE_SWAP_LIMIT_MB` only when you have deliberately accepted the local memory risk. - -Core local commands: - -```bash -./scripts/regenerate_project.sh -xcodebuild -project QwenVoice.xcodeproj -scheme QwenVoice build -xcodebuild -project QwenVoice.xcodeproj -scheme VocelloiOS -destination 'generic/platform=iOS' CODE_SIGNING_ALLOWED=NO ONLY_ACTIVE_ARCH=YES build -./scripts/build_foundation_targets.sh macos -./scripts/build_foundation_targets.sh ios -python3 scripts/harness.py test --layer swift -python3 scripts/harness.py test --layer contract -python3 scripts/harness.py test --layer native -python3 scripts/harness.py test --layer ios -python3 scripts/harness.py test --layer e2e -QWENVOICE_E2E_STRICT=1 python3 scripts/harness.py test --layer e2e -python3 scripts/harness.py diagnose -python3 scripts/harness.py bench --category latency -python3 scripts/harness.py bench --category load -python3 scripts/harness.py bench --category quality -python3 scripts/harness.py bench --category tts_roundtrip -python3 scripts/check_ios_catalog.py -./scripts/release.sh -./scripts/release.sh --preflight full -./scripts/release_ios_testflight.sh -``` - -Notes: - -- `scripts/harness.py` is the primary local test, diagnostic, and benchmark entrypoint. -- The maintained harness layers are `contract`, `swift`, `native`, `ios`, and `e2e`. -- `e2e` is the macOS XCUITest smoke layer. Hosted CI may soft-skip first-time macOS Accessibility/TCC or foreground-window activation failures; strict local release proof uses `QWENVOICE_E2E_STRICT=1`, where those issues fail instead of becoming skipped passes. -- Benchmarks are opt-in release-investigation lanes, not default PR gates. Use `bench --category latency|load|quality|tts_roundtrip|all` explicitly. -- The harness resolves pinned Swift packages into `build/harness/source-packages/`, uses isolated derived data under `build/harness/derived-data/`, emits `.xcresult` bundles under `build/harness/results/`, and serializes heavy lanes with `build/harness/.lock`. -- `QwenVoice Foundation`, `VocelloiOS Foundation`, and `Vocello UI` are the maintained plan-backed test schemes. The committed plans live under `tests/Plans/`. -- `QW_TEST_SUPPORT` is a Debug/test-only compilation condition for stub engines, UI launch configuration, fault injection, fixture helpers, and opt-in benchmark hooks. Release builds must not depend on that code path. -- During the current `macOS-first release track`, the default required local release-readiness loop is `check_project_inputs`, `harness validate`, `contract`, `swift`, `native`, foundation builds for macOS and iOS, `release.sh`, `verify_release_bundle.sh`, and `verify_packaged_dmg.sh`. Add strict `e2e` on the controlled release machine before public signoff. -- Keep `python3 scripts/harness.py test --layer ios` available, but expect a structured skip on machines without an installed iPhone simulator. Generic iPhone compile proof still comes from `./scripts/build_foundation_targets.sh ios`. -- For deterministic local compile proof, prefer `./scripts/build_foundation_targets.sh` over a shared-DerivedData signed debug build. The script uses isolated build roots and `.xcresult` bundles. -- On this machine, keep validation deliberately low-RAM and serialized: run the cheapest relevant gate first, and never overlap heavy `xcodebuild`, `scripts/harness.py`, release packaging, live app validation, or native smoke processes. -- Do not jump to local packaging or manual Computer Use until `./scripts/check_project_inputs.sh`, `python3 scripts/harness.py validate`, and the smallest relevant source gate are already green. -- If a harness layer fails, inspect the emitted `.xcresult` bundle under `build/harness/results//` before changing code. For builds, use `xcrun xcresulttool get build-results --path `; for tests, use `xcrun xcresulttool get test-results summary --path `. -- SourceKit diagnostics like `No such module 'MLX'` / `Cannot find type X in scope` after an edit are index staleness, not real errors. Trust only errors reported by the harness, `xcodebuild`, and `./scripts/build_foundation_targets.sh`. - -## Codex Desktop UI Validation - -Use Codex Desktop's Computer Use and screen-aware tooling as the first-class visual validation layer after repo-script gates are green. Shell scripts remain authoritative for builds, tests, audio QC, and benchmark orchestration; Computer Use is for proving the visible app experience. - -- For UI validation, first run the relevant cheap gates, then launch one fresh Debug app through `./scripts/build_and_run.sh --verify`. Do not open stale benchmark-built app bundles under `build/audio-qc/`. -- Use Computer Use for real app workflows: mode switching, text-field focus, Generate activation, visible busy feedback, playback/save behavior, screenshots, responsiveness checks, and "try it yourself in the opened app" debugging. -- Prefer the V2 benchmark entrypoint, `scripts/run_generation_benchmark.py`, for clean With UI / Without UI measurements. Use `--surface headless-xpc` for backend-only XPC timing and `--surface ui-app` for visible-app timing plus responsiveness. Legacy scripts such as `scripts/run_custom_voice_ui_perf_audit.py` and `scripts/run_ui_generation_benchmark.py` remain useful for focused investigation. -- For UI benchmark runs, Computer Use is the primary visual validation layer. The scripts own deterministic timing, trace, process, memory, and audio-QC artifacts; AX/AppleScript are structured probes, and coordinate/cliclick fallback is last resort only. -- Store UI benchmark artifacts under `build/audio-qc/` or `build/audits/`. Reports should include screenshots, trace JSON, timing CSV, responsiveness samples, process and memory snapshots, audio-QC reports, and a concise Markdown summary. -- UI responsiveness evidence must cover the app and helper process count, app/helper RSS, swap delta, missed focus/input state, stuck busy state, duplicate playback, and duplicate helper detection. -- Keep accessibility identifiers stable. If an accessibility or `cliclick` path fails, capture the visible UI and process state before changing product code. Coordinate-click fallbacks are acceptable only inside local benchmark scripts and should be documented there. -- Do not treat missing Accessibility metadata alone as a product failure unless screenshots, traces, logs, or process evidence confirm the visible app is wrong. - -## Swift Concurrency Gotchas - -- `Self` cannot be referenced inside a `static let` initializer on a class (covariant-Self rule). Use the concrete type name (e.g. `EngineServiceHost.logger`) instead of `Self.logger` in static member initializers. -- `Task.detached { ... }` does not inherit cancellation from the parent. If you need cancellation to propagate, wrap the `try await task.value` in `withTaskCancellationHandler { try await task.value } onCancel: { task.cancel() }`. -- `AsyncThrowingStream` iterators do not automatically observe the consuming task's cancellation when the producer runs in its own Task. Inside `for try await event in stream { ... }`, add `try Task.checkCancellation()` at the top of the loop body. -- When promoting a helper out of a `@MainActor`-isolated class to module scope, mark the closure parameter `@MainActor` (e.g. `condition: @escaping @MainActor () -> Bool`) and invoke via `await MainActor.run(body: condition)`. Without this, Swift 6 flags call sites that capture actor-isolated state with "Sending risks data race". - -## CI And Release Workflows - -The active GitHub workflows are: - -- `Project Inputs` -- `Apple Platform QA Gate` -- `Vocello macOS Release` -- `Vocello iOS TestFlight` - -Release facts: - -- macOS GitHub Releases carry the signed and notarized `Vocello-macos26.dmg`. -- iPhone distribution is App Store / TestFlight only. Do not add iPhone install artifacts to GitHub Releases. -- `scripts/release.sh` is the maintained local macOS packaging entrypoint. -- `scripts/release_ios_testflight.sh` is the maintained iPhone archive/export entrypoint. -- `scripts/verify_ios_release_archive.sh` is the maintained structural verifier for the iPhone archive/export artifacts. -- Both release scripts now use explicit derived-data and cloned-package roots under `build/foundation/` so resolve, build, archive, and export are separate phases. -- `Apple Platform QA Gate` now acts as the shared-core regression gate for the current `macOS-first release track`, uploads harness/build `.xcresult` bundles, keeps generic iPhone compile proof, runs soft-skippable hosted UI smoke, and runs the unsigned macOS release-verification path in CI. -- `Vocello macOS Release` is the only signed/public release workflow required for the current milestone. -- `Vocello iOS TestFlight` remains maintained but is deferred from current public release signoff. -- Shipped macOS bundles and notarized DMGs must not contain `Contents/Resources/backend`, `Contents/Resources/python`, or bundled `Contents/Resources/ffmpeg`. - -## When Changing X, Also Update Y - -- Model registry, speakers, output folders, required model files, or platform-specific install variants: - update `Sources/Resources/qwenvoice_contract.json` first, then the contract loaders, platform delivery code, and contract-facing docs. -- Adding or renaming source files: - update `project.yml`, run `./scripts/regenerate_project.sh`, and confirm generated project files did not capture `__pycache__` or `.pyc` paths. -- Shared engine semantics or model-variant resolution: - review `Sources/QwenVoiceCore/` and iPhone model delivery code together. -- macOS engine/client behavior: - review `Sources/QwenVoiceNative/`, `Sources/QwenVoiceEngineSupport/`, and `Sources/QwenVoiceNativeRuntime/` together. -- iPhone engine-extension transport or host behavior: - review `Sources/QwenVoiceCore/Extension*`, `Sources/iOSEngineExtension/`, `Sources/iOS/VocelloEngineExtensionPoint.swift`, and iPhone build coverage together. -- Memory-pressure, prewarm, or low-RAM admission behavior: - review `Sources/QwenVoiceCore/IOSMemorySnapshot.swift`, `Sources/iOS/TTSEngineStore.swift`, `Sources/iOS/QVoiceiOSApp.swift`, and iPhone settings/status UI together. -- Playback or generation-persistence behavior: - review `Sources/SharedSupport/` and affected macOS or iPhone feature views together. -- macOS release packaging or notarization behavior: - keep `scripts/release.sh`, `scripts/create_dmg.sh`, `scripts/verify_release_bundle.sh`, `scripts/verify_packaged_dmg.sh`, `.github/workflows/macos-release.yml`, and release-facing docs aligned. -- iPhone archive/export/TestFlight behavior: - keep `scripts/check_ios_catalog.py`, `scripts/release_ios_testflight.sh`, `scripts/verify_ios_release_archive.sh`, `.github/workflows/ios-testflight.yml`, and iPhone distribution docs aligned. -- Broad repo facts that users or contributors rely on: - update `AGENTS.md`, `README.md`, `docs/README.md`, `docs/reference/current-state.md`, `docs/reference/engineering-status.md`, `docs/reference/backend-freeze-gate.md`, `docs/reference/frontend-backend-contract.md`, and `docs/reference/release-readiness.md`. - -## Operational Safety - -- Avoid running multiple `QwenVoice` or `Vocello` app instances at once while debugging model loads, clone prep, playback, XPC behavior, or engine-extension behavior. -- Prefer killing an old instance before launching a new build. -- Never overlap heavy `xcodebuild`, `scripts/harness.py`, release packaging, live app validation, or native smoke processes on this machine. -- Never run more than one heavy model load, generation, or benchmark at a time. -- Use Computer Use intentionally for UI validation and visual benchmarks after heavy automation is finished; never keep desktop interaction active while memory-heavy build or validation work is still running. -- For V2 benchmarks, use `scripts/run_generation_benchmark.py --memory-policy normal|stress` instead of a raw swap cutoff. `normal` warns early; `stress` may push swap further, but abort decisions must be based on real pressure symptoms such as sustained unhealthy memory pressure, UI unresponsiveness, duplicate helpers, or runaway swap delta. -- Before live model generation, GUI acceptance, release packaging, or exhaustive benchmarks, check for duplicate Codex/Claude MCP helper stacks such as `xcodebuildmcp`, `apple-docs-mcp`, `chrome-devtools-mcp`, and `SkyComputerUseClient`. If multiple same-purpose helpers are active, treat them as optional background pressure: restart/trim the agent app or disable unused heavy plugins before continuing unless the user explicitly accepts the memory risk. -- Do not start Computer Use or Xcode/Apple-docs MCP-heavy workflows during QwenVoice live generation unless that tool is the thing being tested. Prefer shell-first repo scripts for builds, tests, and benchmarks. - -## Before Finishing - -- Prefer manifest-backed data over duplicated constants. -- Keep accessibility identifiers stable when UI control types change. -- If you changed engine architecture or runtime ownership, verify `AGENTS.md` and `docs/reference/current-state.md` still describe the same app/service/runtime split. -- If you changed release behavior, verify the scripts, workflows, artifact names, `docs/reference/release-readiness.md`, and README/docs all still agree. -- If you changed any public-facing product copy, make sure the README and GitHub repo description still honor the active public homepage posture and current release-track policy. -- For doc-only refreshes, rerun the stale-reference grep and verify referenced commands, workflows, artifact names, and doc links still exist. -- Run the most relevant harness layer plus `python3 scripts/harness.py validate` before calling work complete. diff --git a/CLAUDE.md b/CLAUDE.md index f86bcce..6832b61 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,25 +1,148 @@ # CLAUDE.md -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Companion Docs - -- `AGENTS.md` is the full repo operating guide (Codex-authored, but authoritative for Claude too). Treat this file as the fast index; fall back to `AGENTS.md` for detail. -- `README.md` covers product surface, supported platforms, and the basic build flow. -- `CONTRIBUTING.md` covers contributor flow. -- `docs/reference/current-state.md`, `docs/reference/engineering-status.md`, and `docs/reference/release-readiness.md` are the maintained engineering references. +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. It is the primary repo operating guide for coding agents working in QwenVoice. + +## Repo Overview + +QwenVoice is the repository identity and continuity brand for the merged Vocello Apple-platform product line. + +Current product reality: + +- the repo stays `QwenVoice` +- the shipped iPhone app is `Vocello` +- the next macOS release ships as `Vocello.app` inside `Vocello-macos26.dmg`; the supporting framework/service/runtime modules (`QwenVoiceCore`, `QwenVoiceEngineService`, `QwenVoiceEngineSupport`, `QwenVoiceNative`, `QwenVoiceNativeRuntime`) keep their `QwenVoice` names internally for continuity +- the current public milestone uses a `macOS-first release track`, with iPhone retained as a compile-safe and deferred release surface + +The main working surfaces are: + +- `Sources/` for the macOS app shell, shared app models/services/views, and the shipping Mac target +- `Sources/QwenVoiceCore/` for shared Apple-platform runtime semantics, contract types, model variants, and iOS extension transport +- `Sources/QwenVoiceNative/` for the macOS app-facing engine proxy/store/client layer +- `Sources/QwenVoiceEngineSupport/` for shared macOS engine IPC and transport types +- `Sources/QwenVoiceNativeRuntime/` for retained macOS compatibility and regression coverage +- `Sources/QwenVoiceEngineService/` for the bundled macOS XPC helper +- `Sources/iOS/` and `Sources/iOSSupport/` for the iPhone app shell and iPhone-only support layers +- `Sources/SharedSupport/` for cross-platform playback, persistence, and other shared app-layer helpers +- `Sources/iOSEngineExtension/` for the isolated iPhone engine extension target +- `Sources/Resources/qwenvoice_contract.json` for shared model, variant, speaker, output, and required-file metadata +- `scripts/` plus `.github/workflows/` for validation, release packaging, and CI behavior +- `config/apple-platform-capability-matrix.json` for the maintained cross-platform capability, bundle-identity, and entitlement baseline used by release verification + +This checkout is a native Apple-platform codebase for macOS and iPhone. Do not reintroduce a repo-owned Python backend, Python setup path, or standalone CLI surface. + +## Maintained Docs + +- `CLAUDE.md` — this file, the primary repo operating guide for Claude Code and other coding agents +- `README.md` — public landing page and end-user overview +- `CONTRIBUTING.md` — contributor workflow, source-of-truth order, validation entrypoints +- `docs/README.md` — documentation index +- `docs/qwen_tone.md` — supplemental tone and prompt-writing guidance +- `docs/reference/current-state.md` — current repo facts +- `docs/reference/engineering-status.md` — current strengths and caveats +- `docs/reference/backend-freeze-gate.md` — rebuilt QA gate for static validation, source/native/UI harness layers, builds, and unsigned release proof +- `docs/reference/frontend-backend-contract.md` — app-facing backend state, delivery state, and QA gate +- `docs/reference/release-readiness.md` — macOS-first release-track policy, proof status, public-homepage freeze rules, tier→workflow mapping +- `docs/reference/live-testing.md` — local harness lanes, strict e2e behavior, result paths, xcresult triage commands +- `docs/reference/privacy-storage.md` — local model, output, history, saved-voice, App Group, and deletion-path reference +- `docs/reference/vendoring-runtime.md` — runtime, vendoring, and packaging boundaries +- `docs/reference/mlx-audio-swift-patching.md` — vendor delta under `third_party_patches/mlx-audio-swift/`, rebase procedure, and post-rebase build checklist + +There are no repo-tracked local skills under `.agents/skills/` or `.claude/skills/` in this checkout. Do not point contributors at removed CLI docs, deleted backend references, or deleted repo-scoped skills. + +Public homepage posture: + +- `README.md` leads with `QwenVoice` because that is the currently shipped public brand (`v1.2.3`). Its "A Note on What's Changing" section frames `Vocello` as the forward rebrand that lands with the next macOS release. +- The GitHub repo description must stay consistent with the README — do not claim a Vocello-first public posture while the published release is still QwenVoice-branded. +- Leave the GitHub homepage URL blank unless the user explicitly asks to restore or change it. +- Keep public messaging aligned with the currently shipped macOS product reality and the active `macOS-first release track`. +- Do not present iPhone as a current public release surface until the release-track policy changes. The iPhone app is framed publicly as the in-development "Vocello for iPhone" — standalone, 4-bit, open source in this repo, published via the App Store once ready. + +Current release-track policy: + +- The next public release target is macOS only. +- The next macOS release ships under the `Vocello` brand as `Vocello-macos26.dmg` and requires `macOS 26.0` as the minimum. `macOS 15` was supported only on the already-shipped `QwenVoice v1.2.3` and is retired going forward. +- Do not retroactively edit `QwenVoice v1.2.3` release notes or the shipped-state sections of `README.md` — they must stay accurate to what shipped. +- Keep iPhone green at generic compile level on the working branch, but do not treat iPhone release/TestFlight proof as blocking for the current milestone. +- Re-open iPhone release proof only through an explicit milestone change after the shared core is proven stable on macOS. ## Source Of Truth -When repo facts disagree, trust in this order: +When repo facts disagree, trust sources in this order: 1. `Sources/` 2. `project.yml` 3. `scripts/` plus `.github/workflows/` -4. `docs/reference/current-state.md`, `engineering-status.md`, `release-readiness.md` +4. `docs/reference/current-state.md`, `docs/reference/engineering-status.md`, and `docs/reference/release-readiness.md` 5. other prose docs -`Sources/Resources/qwenvoice_contract.json` is the source of truth for shared model, speaker, variant, output, and required-file metadata. +`Sources/Resources/qwenvoice_contract.json` is the source of truth for shared model, speaker, and platform-variant metadata. + +## Git Workflow Default + +- Work on the user-designated branch (currently `claude/init-project-7ZdXV`). +- Do not create extra branches or worktrees unless the user explicitly asks for one. +- Do not let generic tool, plugin, or skill defaults override this repo-specific rule. + +## Safe Edit Boundaries + +- `project.yml` drives `QwenVoice.xcodeproj`. Prefer editing `project.yml` and regenerating the project over hand-editing generated project files. +- The macOS app target intentionally excludes `Sources/QwenVoiceEngineService/`, `Sources/QwenVoiceEngineSupport/`, `Sources/QwenVoiceNativeRuntime/`, `Sources/iOS*/`, `Sources/Resources/ffmpeg/`, and `Sources/Resources/vendor/` as ordinary app sources while embedding the XPC service target through `project.yml`. Keep that split intact. +- The iPhone app target and the iPhone engine-extension target both depend on `QwenVoiceCore`. Keep engine execution isolated from the iPhone UI process. +- `third_party_patches/mlx-audio-swift/` is the repo-owned native backend source boundary for MLXAudioSwift. Keep its package manifest and pins aligned with `project.yml` and `Package.resolved`. +- `config/apple-platform-capability-matrix.json` is the release-verification source of truth for bundle identifiers, expected application groups, opportunistic memory-limit entitlements, and packaged-resource exclusions. +- If `Sources/Resources/ffmpeg/` or `Sources/Resources/vendor/` appear locally, treat them as generated or local-only leftovers, not as maintained tracked checkout surfaces. +- App data under `~/Library/Application Support/QwenVoice/` or a `QWENVOICE_APP_SUPPORT_DIR` override is runtime state, not repo source. +- Watch for accidental `__pycache__`, `.pyc`, `.DS_Store`, and `.profraw` paths when regenerating or reviewing changes. + +## Architecture Boundaries + +- `Sources/QwenVoiceApp.swift` composes macOS app-global services, owns the separate Settings scene, and initializes the app-facing Mac engine through `AppEngineSelection`. +- `Sources/ContentView.swift` owns the macOS `NavigationSplitView`, toolbar/search chrome, sidebar selection, and persisted generation drafts. +- `Sources/QwenVoiceNative/` is the macOS app-side engine layer: `TTSEngineStore`, `XPCNativeEngineClient`, chunk brokering, and the app-facing `MacTTSEngine` surface live there. +- `Sources/QwenVoiceEngineSupport/` is the shared macOS engine transport boundary used by both the app and the helper. +- `Sources/QwenVoiceEngineService/` now hosts the active macOS shared-core runtime through `QwenVoiceCore`. Treat `Sources/QwenVoiceNativeRuntime/` as a retained compatibility surface rather than the primary live policy owner. +- `Sources/QwenVoiceEngineService/` owns the bundled macOS XPC helper entrypoint and session/host behavior. +- `Sources/QwenVoiceCore/` is the cross-platform engine core and shared semantic boundary. Keep it free of app-process UI assumptions. +- `Sources/iOSEngineExtension/` hosts the isolated iPhone engine process through ExtensionFoundation. Heavy generation and prewarm work belongs there, not in the iPhone UI app process. +- `Sources/iOS/VocelloEngineExtensionPoint.swift` owns monitor-backed iPhone extension discovery and preferred-identity selection, while `Sources/QwenVoiceCore/ExtensionEngineHostManager.swift` owns active transport replacement and teardown. +- `Sources/iOS/` and `Sources/iOSSupport/` own the iPhone SwiftUI shell, model delivery UX, library/history views, and memory-pressure coordination. +- `Sources/SharedSupport/` owns shared playback and generation-persistence surfaces that now serve both the macOS and iPhone apps. +- `Sources/Services/AppPaths.swift` and `Sources/iOSSupport/Services/AppPaths.swift` are the path boundaries for runtime data on each platform. +- The iPhone App Group surface is intentionally file-based and rooted under `Sources/iOSSupport/Services/AppPaths.swift`; keep shared state constrained to the required app-support subtree for models, downloads, outputs, voices, and cache data. +- `Sources/Models/TTSContract.swift`, `Sources/Models/TTSModel.swift`, and the `QwenVoiceCore` semantic types load `Sources/Resources/qwenvoice_contract.json`. +- `Sources/QwenVoiceNativeRuntime/` keeps retained copies of runtime types (notably `NativeStreamingSynthesisSession`) beside the live `Sources/QwenVoiceCore/` implementation. Behavior fixes in shared streaming/session semantics often have to land in **both** copies until the retained-vs-live split is consolidated. + +Default macOS runtime data layout: + +```text +~/Library/Application Support/QwenVoice/ + models/ + outputs/ + CustomVoice/ + VoiceDesign/ + Clones/ + voices/ + history.sqlite +``` + +## Platform And Product Constraints + +- Minimum supported OS versions are `macOS 26.0+` and `iOS 26.0+`. +- The official minimum hardware floor is `Mac mini M1, 8 GB RAM` and `iPhone 15 Pro`. +- Process isolation is a product requirement on both platforms. Do not move heavy generation, prewarm, or model-load work back into the UI process. +- `QW_UI_LEGACY_GLASS` and macOS 15 compatibility are retired. Do not restore older dual-profile or dual-OS support. +- iPhone uses 4-bit `Speed` variants only. +- macOS defaults to 4-bit `Speed` on minimum hardware and can also expose 8-bit `Quality` when runtime admission allows it. +- Keep shared styling centralized in `Sources/Views/Components/AppTheme.swift` on macOS and in the iPhone shell primitives/theme layer on iOS. +- `QW_TEST_SUPPORT` is a Debug/test-only Swift compilation condition (stub engines, UI launch configuration, fault injection, fixture helpers, opt-in benchmark hooks). Release builds must not depend on it. + +## Native SwiftUI Discipline + +- Keep UI work conservative, native, and stability-led. Prefer standard SwiftUI navigation, forms, lists, toolbars, sheets, controls, and system materials over theme-first redesigns. +- Do not reintroduce the removed desktop-studio shell, generated-reference redesign workflow, oversized hero chrome, inspector layout, full-window footer player, or decorative glass/card systems. +- Treat UI polish as small refinements to the existing app structure. Any broad layout change needs an explicit product decision and must not happen while runtime responsiveness or memory behavior is unstable. +- Keep generation screens responsive under backend activity: avoid speculative mode-switch work, broad environment-object invalidation, and visual effects that rebuild large view subtrees during prewarm or generation. +- Keep rescue work boring: no generated UI redesigns, no large Liquid Glass shell work, no screen-mount model warmup, and no overlapping heavy validation commands on this 8 GB development machine. ## Common Commands @@ -75,86 +198,93 @@ Local rescue and release: ./scripts/verify_packaged_dmg.sh build/Vocello-macos26.dmg build/release-metadata.txt ``` -Harness artifacts: `build/harness/{derived-data,results,source-packages,artifacts}`. On failure, inspect the `.xcresult` bundle: +iPhone TestFlight (deferred): ```sh -xcrun xcresulttool get build-results --path build/harness/results//... -xcrun xcresulttool get test-results summary --path build/harness/results//... +python3 scripts/check_ios_catalog.py +./scripts/release_ios_testflight.sh +./scripts/verify_ios_release_archive.sh ``` -SourceKit diagnostics like `No such module 'MLX'` or `Cannot find type X in scope` after an edit are index staleness. Trust only `xcodebuild`, `scripts/harness.py`, and `./scripts/build_foundation_targets.sh`. - -## Architecture - -The repo identity stays `QwenVoice`; the shipping macOS bundle is `Vocello.app` (its Swift `PRODUCT_MODULE_NAME` is kept as `QwenVoice` so sibling modules still resolve). The iPhone app is `Vocello` for iPhone, currently deferred from public release. - -**macOS process split** - -- App process: `Sources/QwenVoiceApp.swift` composes app-global services and runs `AppEngineSelection`; `Sources/ContentView.swift` owns the `NavigationSplitView` chrome and persisted drafts. -- App-side engine layer: `Sources/QwenVoiceNative/` — `TTSEngineStore`, `XPCNativeEngineClient`, chunk brokering, `MacTTSEngine`. -- Transport boundary: `Sources/QwenVoiceEngineSupport/`. -- Out-of-process helper: `Sources/QwenVoiceEngineService/` (XPC service, hosts the active shared-core runtime through `QwenVoiceCore`). +Notes: + +- `scripts/harness.py` is the primary local test, diagnostic, and benchmark entrypoint. +- Maintained harness layers are `contract`, `swift`, `native`, `ios`, and `e2e`. +- `e2e` is the macOS XCUITest smoke layer. Hosted CI may soft-skip first-time macOS Accessibility/TCC or foreground-window activation failures; strict local release proof uses `QWENVOICE_E2E_STRICT=1`, where those issues fail instead of becoming skipped passes. +- Benchmarks are opt-in release-investigation lanes, not default PR gates. Use `bench --category latency|load|quality|tts_roundtrip|all` explicitly. +- Harness artifacts live under `build/harness/{derived-data,results,source-packages,artifacts}`; heavy lanes serialize on `build/harness/.lock`. +- `QwenVoice Foundation`, `VocelloiOS Foundation`, and `Vocello UI` are the maintained plan-backed test schemes. The committed plans live under `tests/Plans/`. +- During the current `macOS-first release track`, the default required local release-readiness loop is `check_project_inputs`, `harness validate`, `contract`, `swift`, `native`, foundation builds for macOS and iOS, `release.sh`, `verify_release_bundle.sh`, and `verify_packaged_dmg.sh`. Add strict `e2e` on the controlled release machine before public signoff. +- Generic iPhone compile proof comes from `./scripts/build_foundation_targets.sh ios` — the iOS harness layer requires an installed iPhone simulator. +- For deterministic local compile proof, prefer `./scripts/build_foundation_targets.sh` over a shared-DerivedData signed debug build. The script uses isolated build roots and `.xcresult` bundles. +- On this 8 GB development machine, keep validation deliberately low-RAM and serialized: run the cheapest relevant gate first, and never overlap heavy `xcodebuild`, `scripts/harness.py`, release packaging, live app validation, or native smoke processes. +- If a harness layer fails, inspect the emitted `.xcresult` bundle under `build/harness/results//` before changing code: `xcrun xcresulttool get build-results --path ` for builds, `xcrun xcresulttool get test-results summary --path ` for tests. +- SourceKit diagnostics like `No such module 'MLX'` or `Cannot find type X in scope` after an edit are index staleness, not real errors. Trust only `xcodebuild`, `scripts/harness.py`, and `./scripts/build_foundation_targets.sh`. -**iPhone process split** - -- App shell: `Sources/iOS/` + `Sources/iOSSupport/` (SwiftUI, model delivery UX, library/history, memory-pressure coordination). -- Engine extension: `Sources/iOSEngineExtension/` runs heavy generation/prewarm out of the UI process via ExtensionKit. Discovery: `Sources/iOS/VocelloEngineExtensionPoint.swift`. Active transport replacement / teardown: `Sources/QwenVoiceCore/ExtensionEngineHostManager.swift`. - -**Cross-platform core** - -- `Sources/QwenVoiceCore/` owns shared engine semantics, contract types, model-variant resolution, and ExtensionKit transport. Keep it free of app-process UI assumptions. -- `Sources/SharedSupport/` owns shared playback and generation-persistence surfaces consumed by both shells. -- `Sources/Services/AppPaths.swift` (macOS) and `Sources/iOSSupport/Services/AppPaths.swift` (iPhone) are the runtime-data path boundaries. Default macOS data root: `~/Library/Application Support/QwenVoice/{models,outputs,voices,history.sqlite}` (override via `QWENVOICE_APP_SUPPORT_DIR`). - -**Retained-vs-live runtime split** - -`Sources/QwenVoiceNativeRuntime/` keeps duplicate copies of runtime types (notably `NativeStreamingSynthesisSession`) alongside the live `Sources/QwenVoiceCore/` implementations. Behavior fixes in shared streaming/session semantics often need to land in **both** copies until the split is consolidated. - -**Manifests** - -- `Sources/Resources/qwenvoice_contract.json` is loaded by `Sources/Models/TTSContract.swift`, `Sources/Models/TTSModel.swift`, and the `QwenVoiceCore` semantic types. -- `config/apple-platform-capability-matrix.json` is the release-verification baseline for bundle ids, app groups, opportunistic memory-limit entitlements, and packaged-resource exclusions. - -**Vendored backend** +## Swift Concurrency Gotchas -`third_party_patches/mlx-audio-swift/` is the repo-owned MLXAudioSwift source. Keep its package manifest pinned with `project.yml` and `Package.resolved`. +- `Self` cannot be referenced inside a `static let` initializer on a class (covariant-Self rule). Use the concrete type name (e.g. `EngineServiceHost.logger`) instead of `Self.logger` in static member initializers. +- `Task.detached { ... }` does not inherit cancellation from the parent. If cancellation must propagate, wrap `try await task.value` in `withTaskCancellationHandler { try await task.value } onCancel: { task.cancel() }`. +- `AsyncThrowingStream` iterators do not automatically observe the consuming task's cancellation when the producer runs in its own `Task`. Inside `for try await event in stream { ... }`, add `try Task.checkCancellation()` at the top of the loop body. +- When promoting a helper out of a `@MainActor`-isolated class to module scope, mark the closure parameter `@MainActor` (e.g. `condition: @escaping @MainActor () -> Bool`) and invoke via `await MainActor.run(body: condition)`. Without this, Swift 6 flags call sites that capture actor-isolated state with "Sending risks data race". -## Repo-Specific Rules +## CI And Release Workflows -These override generic agent defaults — read them before editing. +The active GitHub workflows are: -- **No repo-owned Python backend, Python setup path, or standalone CLI.** Do not reintroduce one. -- **Git default**: work on the user-designated branch (currently `claude/init-project-7ZdXV`). Do not create extra branches or worktrees unless the user asks. -- **Edit `project.yml`, never the generated `*.xcodeproj` files directly.** Run `./scripts/regenerate_project.sh` after structural changes. Watch outputs for accidental `__pycache__`, `.pyc`, `.DS_Store`, or `.profraw` paths. -- The macOS app target intentionally **excludes** `QwenVoiceEngineService/`, `QwenVoiceEngineSupport/`, `QwenVoiceNativeRuntime/`, `iOS*/`, `Resources/ffmpeg/`, and `Resources/vendor/` from its `Sources` glob; the XPC service is embedded as a separate target. Keep that split intact. -- **Process isolation is a product requirement** on both platforms. Do not move heavy generation, prewarm, or model-load work back into the UI process. -- **Platform floors**: macOS 26.0+ and iOS 26.0+; Apple Silicon; Mac mini M1 8 GB / iPhone 15 Pro minimums. iPhone uses 4-bit Speed only; macOS defaults to 4-bit Speed and may expose 8-bit Quality where admission allows. `macOS 15` and `QW_UI_LEGACY_GLASS` are retired — do not restore. -- **`QW_TEST_SUPPORT`** is a Debug/test-only Swift compilation condition (stub engines, fault injection, fixtures, opt-in benchmark hooks). Release builds must not depend on it. -- **Native SwiftUI discipline**: prefer standard SwiftUI primitives plus `Sources/Views/Components/AppTheme.swift` (macOS) or the iPhone shell theme. Do not reintroduce the removed desktop-studio shell, generated-reference redesign, hero chrome, inspector layout, or full-window footer player. -- **Operational safety on this 8 GB dev machine**: never overlap heavy `xcodebuild`, `scripts/harness.py`, release packaging, live app validation, or benchmark processes. Kill old `QwenVoice`/`Vocello` instances before launching a new build. +- `Project Inputs` +- `Apple Platform QA Gate` +- `Vocello macOS Release` +- `Vocello iOS TestFlight` -## Swift Concurrency Gotchas +Release facts: -- `Self` cannot be referenced inside a `static let` initializer on a class — use the concrete type name (e.g. `EngineServiceHost.logger`) instead of `Self.logger`. -- `Task.detached { ... }` does not inherit cancellation. If cancellation must propagate, wrap `try await task.value` in `withTaskCancellationHandler { ... } onCancel: { task.cancel() }`. -- `AsyncThrowingStream` consumers do not automatically observe the consuming task's cancellation when the producer runs in its own `Task`. Add `try Task.checkCancellation()` at the top of `for try await` loop bodies. -- When promoting a helper out of a `@MainActor`-isolated class to module scope, mark the closure parameter `@MainActor` (e.g. `condition: @escaping @MainActor () -> Bool`) and invoke via `await MainActor.run(body: condition)`. Without this, Swift 6 flags call sites as "Sending risks data race". +- macOS GitHub Releases carry the signed and notarized `Vocello-macos26.dmg`. +- iPhone distribution is App Store / TestFlight only. Do not add iPhone install artifacts to GitHub Releases. +- `scripts/release.sh` is the maintained local macOS packaging entrypoint. +- `scripts/release_ios_testflight.sh` is the maintained iPhone archive/export entrypoint; `scripts/verify_ios_release_archive.sh` is the structural verifier. +- Both release scripts use explicit derived-data and cloned-package roots under `build/foundation/` so resolve, build, archive, and export are separate phases. +- `Apple Platform QA Gate` is the shared-core regression gate for the current `macOS-first release track`: harness/build `.xcresult` upload, generic iPhone compile proof, soft-skippable hosted UI smoke, and unsigned macOS release verification. +- `Vocello macOS Release` is the only signed/public release workflow required for the current milestone. +- `Vocello iOS TestFlight` remains maintained but is deferred from current public release signoff. +- Shipped macOS bundles and notarized DMGs must not contain `Contents/Resources/backend`, `Contents/Resources/python`, or bundled `Contents/Resources/ffmpeg`. ## When Changing X, Also Update Y -- Model registry / speakers / output folders / required model files / install variants → `Sources/Resources/qwenvoice_contract.json` first, then contract loaders and contract-facing docs. -- Adding or renaming source files → `project.yml`, then `./scripts/regenerate_project.sh`. -- Shared engine semantics or model-variant resolution → review `Sources/QwenVoiceCore/` and iPhone model delivery together; mirror retained runtime copies in `Sources/QwenVoiceNativeRuntime/` when applicable. -- macOS engine/client behavior → review `QwenVoiceNative/`, `QwenVoiceEngineSupport/`, and `QwenVoiceNativeRuntime/` together. -- iPhone engine extension transport / host → review `QwenVoiceCore/Extension*`, `iOSEngineExtension/`, and `iOS/VocelloEngineExtensionPoint.swift` together. -- Memory pressure / prewarm / low-RAM admission → `QwenVoiceCore/IOSMemorySnapshot.swift`, `iOS/TTSEngineStore.swift`, `iOS/QVoiceiOSApp.swift`, plus iPhone settings/status UI. -- Playback or generation persistence → `Sources/SharedSupport/` and the affected feature views. -- macOS release packaging / notarization → `scripts/release.sh`, `scripts/create_dmg.sh`, `scripts/verify_release_bundle.sh`, `scripts/verify_packaged_dmg.sh`, `.github/workflows/macos-release.yml`, and release-facing docs. -- iPhone TestFlight → `scripts/check_ios_catalog.py`, `scripts/release_ios_testflight.sh`, `scripts/verify_ios_release_archive.sh`, `.github/workflows/ios-testflight.yml`. +- Model registry, speakers, output folders, required model files, or platform-specific install variants: + update `Sources/Resources/qwenvoice_contract.json` first, then the contract loaders, platform delivery code, and contract-facing docs. +- Adding or renaming source files: + update `project.yml`, run `./scripts/regenerate_project.sh`, and confirm generated project files did not capture `__pycache__` or `.pyc` paths. +- Shared engine semantics or model-variant resolution: + review `Sources/QwenVoiceCore/` and iPhone model delivery code together; mirror retained runtime copies in `Sources/QwenVoiceNativeRuntime/` when applicable. +- macOS engine/client behavior: + review `Sources/QwenVoiceNative/`, `Sources/QwenVoiceEngineSupport/`, and `Sources/QwenVoiceNativeRuntime/` together. +- iPhone engine-extension transport or host behavior: + review `Sources/QwenVoiceCore/Extension*`, `Sources/iOSEngineExtension/`, `Sources/iOS/VocelloEngineExtensionPoint.swift`, and iPhone build coverage together. +- Memory-pressure, prewarm, or low-RAM admission behavior: + review `Sources/QwenVoiceCore/IOSMemorySnapshot.swift`, `Sources/iOS/TTSEngineStore.swift`, `Sources/iOS/QVoiceiOSApp.swift`, and iPhone settings/status UI together. +- Playback or generation-persistence behavior: + review `Sources/SharedSupport/` and affected macOS or iPhone feature views together. +- macOS release packaging or notarization behavior: + keep `scripts/release.sh`, `scripts/create_dmg.sh`, `scripts/verify_release_bundle.sh`, `scripts/verify_packaged_dmg.sh`, `.github/workflows/macos-release.yml`, and release-facing docs aligned. +- iPhone archive/export/TestFlight behavior: + keep `scripts/check_ios_catalog.py`, `scripts/release_ios_testflight.sh`, `scripts/verify_ios_release_archive.sh`, `.github/workflows/ios-testflight.yml`, and iPhone distribution docs aligned. +- Broad repo facts that users or contributors rely on: + update `CLAUDE.md`, `README.md`, `docs/README.md`, `docs/reference/current-state.md`, `docs/reference/engineering-status.md`, `docs/reference/backend-freeze-gate.md`, `docs/reference/frontend-backend-contract.md`, and `docs/reference/release-readiness.md`. + +## Operational Safety + +- Avoid running multiple `QwenVoice` or `Vocello` app instances at once while debugging model loads, clone prep, playback, XPC behavior, or engine-extension behavior. Kill an old instance before launching a new build. +- Never overlap heavy `xcodebuild`, `scripts/harness.py`, release packaging, live app validation, or native smoke processes on this machine. +- Never run more than one heavy model load, generation, or benchmark at a time. +- For V2 benchmarks, use `scripts/run_generation_benchmark.py --memory-policy normal|stress` instead of a raw swap cutoff. `normal` warns early; `stress` may push swap further, but abort decisions must be based on real pressure symptoms such as sustained unhealthy memory pressure, UI unresponsiveness, duplicate helpers, or runaway swap delta. ## Before Finishing - Prefer manifest-backed data over duplicated constants. - Keep accessibility identifiers stable when UI control types change. - Re-run `./scripts/check_project_inputs.sh` and `python3 scripts/harness.py validate` plus the most relevant harness layer before declaring work complete. -- If you changed engine architecture, runtime ownership, or release behavior, verify that `AGENTS.md` and `docs/reference/current-state.md` still describe the same split. +- If you changed engine architecture or runtime ownership, verify `CLAUDE.md` and `docs/reference/current-state.md` still describe the same app/service/runtime split. +- If you changed release behavior, verify the scripts, workflows, artifact names, `docs/reference/release-readiness.md`, and README/docs all still agree. +- If you changed any public-facing product copy, make sure the README and GitHub repo description still honor the active public homepage posture and current release-track policy. +- For doc-only refreshes, rerun the stale-reference grep and verify referenced commands, workflows, artifact names, and doc links still exist. diff --git a/docs/README.md b/docs/README.md index 6595ae9..305385e 100644 --- a/docs/README.md +++ b/docs/README.md @@ -4,7 +4,7 @@ This folder contains the current repo-authored documentation for QwenVoice. ## Maintained Reference Docs -- [`../AGENTS.md`](../AGENTS.md) — primary repository operating guide for coding agents and maintainers +- [`../CLAUDE.md`](../CLAUDE.md) — primary repository operating guide for coding agents and maintainers - [`reference/current-state.md`](reference/current-state.md) — shared current repo facts - [`reference/engineering-status.md`](reference/engineering-status.md) — current strengths, caveats, and validation posture - [`reference/backend-freeze-gate.md`](reference/backend-freeze-gate.md) — rebuilt QA gate for static validation, source/native/UI harness layers, builds, and unsigned release proof diff --git a/docs/qwen_tone.md b/docs/qwen_tone.md index 0f8fb16..278ec2a 100644 --- a/docs/qwen_tone.md +++ b/docs/qwen_tone.md @@ -8,7 +8,7 @@ For current repo truth about app structure, workflows, or supported behavior, tr 1. `README.md` 2. `docs/reference/current-state.md` -3. `AGENTS.md` +3. `CLAUDE.md` ## What the App Exposes diff --git a/docs/reference/backend-freeze-gate.md b/docs/reference/backend-freeze-gate.md index 81ef920..6e79d93 100644 --- a/docs/reference/backend-freeze-gate.md +++ b/docs/reference/backend-freeze-gate.md @@ -143,7 +143,7 @@ When one of these changes, treat it as a backend contract review: Update these docs together when the gate changes: -- `AGENTS.md` +- `CLAUDE.md` - `docs/README.md` - `docs/reference/current-state.md` - `docs/reference/engineering-status.md` diff --git a/docs/reference/current-state.md b/docs/reference/current-state.md index 3b122ff..2807c4c 100644 --- a/docs/reference/current-state.md +++ b/docs/reference/current-state.md @@ -159,7 +159,7 @@ The maintained harness and foundation paths now use: ## Current Documentation Boundaries -- `AGENTS.md` is the primary repo-operating guide for agents and maintainers. +- `CLAUDE.md` is the primary repo-operating guide for agents and maintainers. - `docs/README.md` is the index of the maintained documentation set. - `docs/reference/current-state.md`, `docs/reference/engineering-status.md`, `docs/reference/backend-freeze-gate.md`, `docs/reference/frontend-backend-contract.md`, `docs/reference/live-testing.md`, and `docs/reference/vendoring-runtime.md` are the maintained reference docs. - `docs/reference/privacy-storage.md` records local storage, deletion paths, and voice-cloning consent posture. diff --git a/docs/reference/frontend-backend-contract.md b/docs/reference/frontend-backend-contract.md index 8bccbf5..521e8bd 100644 --- a/docs/reference/frontend-backend-contract.md +++ b/docs/reference/frontend-backend-contract.md @@ -169,7 +169,7 @@ Any backend change that alters one of the stable state surfaces above is a contr When that happens, update: -- `AGENTS.md` +- `CLAUDE.md` - `docs/README.md` - `docs/reference/current-state.md` - `docs/reference/engineering-status.md` diff --git a/scripts/check_project_inputs.sh b/scripts/check_project_inputs.sh index 5404708..3d230ad 100755 --- a/scripts/check_project_inputs.sh +++ b/scripts/check_project_inputs.sh @@ -59,7 +59,6 @@ PROHIBITED_REFERENCE_PATTERNS=( "docs/reference/testing.md" "QwenVoice-macos15.dmg" "build/QwenVoice.app" - ".claude/worktrees" ) for removed_pattern in "${PROHIBITED_REFERENCE_PATTERNS[@]}"; do