diff --git a/.gitignore b/.gitignore
index 773a29b..987dd7d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -112,3 +112,18 @@ CHANGES.md
.ai-skills-layout-shots/
.workbuddy-ai/
reports/
+
+# Internal planning / audit docs kept locally only (zero in-repo or
+# GitHub-Pages references; never re-add these — they were removed
+# in 2026-08 to keep the public docs/ limited to GitHub Pages assets).
+/docs/ACCOUNT_MANAGED_GATEWAY_TESTING.md
+/docs/APPSTORE_METADATA.md
+/docs/STOREKIT_TESTING.md
+/docs/TYPING_KEYBOARD_BASELINE.md
+/docs/ios-pip-voice-session-plan.md
+/docs/keyboard-accuracy-plan.md
+/docs/keyboard-memory-budget.md
+/docs/local-asr-architecture.md
+/docs/mac-parity-audit-2026-08-23.md
+/docs/personal-dictionary-icloud-kvs-checklist.md
+/docs/screenshots/README.md
diff --git a/OSGKeyboard/Assets.xcassets/anthropic.imageset/Contents.json b/OSGKeyboard/Assets.xcassets/anthropic.imageset/Contents.json
deleted file mode 100644
index 9f10dd1..0000000
--- a/OSGKeyboard/Assets.xcassets/anthropic.imageset/Contents.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "images" : [
- {
- "filename" : "anthropic.svg",
- "idiom" : "universal"
- }
- ],
- "info" : {
- "author" : "xcode",
- "version" : 1
- },
- "properties" : {
- "preserves-vector-representation" : true,
- "template-rendering-intent" : "template"
- }
-}
diff --git a/OSGKeyboard/Assets.xcassets/anthropic.imageset/anthropic.svg b/OSGKeyboard/Assets.xcassets/anthropic.imageset/anthropic.svg
deleted file mode 100644
index 5b81844..0000000
--- a/OSGKeyboard/Assets.xcassets/anthropic.imageset/anthropic.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/OSGKeyboard/Assets.xcassets/apple.imageset/Contents.json b/OSGKeyboard/Assets.xcassets/apple.imageset/Contents.json
deleted file mode 100644
index bc991b6..0000000
--- a/OSGKeyboard/Assets.xcassets/apple.imageset/Contents.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "images" : [
- {
- "filename" : "apple.svg",
- "idiom" : "universal"
- }
- ],
- "info" : {
- "author" : "xcode",
- "version" : 1
- },
- "properties" : {
- "preserves-vector-representation" : true,
- "template-rendering-intent" : "template"
- }
-}
diff --git a/OSGKeyboard/Assets.xcassets/apple.imageset/apple.svg b/OSGKeyboard/Assets.xcassets/apple.imageset/apple.svg
deleted file mode 100644
index e41c537..0000000
--- a/OSGKeyboard/Assets.xcassets/apple.imageset/apple.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/OSGKeyboard/Assets.xcassets/ark.imageset/Contents.json b/OSGKeyboard/Assets.xcassets/ark.imageset/Contents.json
deleted file mode 100644
index 6f122ce..0000000
--- a/OSGKeyboard/Assets.xcassets/ark.imageset/Contents.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "images" : [
- {
- "filename" : "ark.svg",
- "idiom" : "universal"
- }
- ],
- "info" : {
- "author" : "xcode",
- "version" : 1
- },
- "properties" : {
- "preserves-vector-representation" : true,
- "template-rendering-intent" : "template"
- }
-}
diff --git a/OSGKeyboard/Assets.xcassets/ark.imageset/ark.svg b/OSGKeyboard/Assets.xcassets/ark.imageset/ark.svg
deleted file mode 100644
index 28556c3..0000000
--- a/OSGKeyboard/Assets.xcassets/ark.imageset/ark.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/OSGKeyboard/Assets.xcassets/codingplanx.imageset/Contents.json b/OSGKeyboard/Assets.xcassets/codingplanx.imageset/Contents.json
deleted file mode 100644
index 5b51e01..0000000
--- a/OSGKeyboard/Assets.xcassets/codingplanx.imageset/Contents.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "images" : [
- {
- "filename" : "codingplanx.svg",
- "idiom" : "universal"
- }
- ],
- "info" : {
- "author" : "xcode",
- "version" : 1
- },
- "properties" : {
- "preserves-vector-representation" : true,
- "template-rendering-intent" : "template"
- }
-}
diff --git a/OSGKeyboard/Assets.xcassets/codingplanx.imageset/codingplanx.svg b/OSGKeyboard/Assets.xcassets/codingplanx.imageset/codingplanx.svg
deleted file mode 100644
index d80b55c..0000000
--- a/OSGKeyboard/Assets.xcassets/codingplanx.imageset/codingplanx.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/OSGKeyboard/Assets.xcassets/cometapi.imageset/Contents.json b/OSGKeyboard/Assets.xcassets/cometapi.imageset/Contents.json
deleted file mode 100644
index 39305f7..0000000
--- a/OSGKeyboard/Assets.xcassets/cometapi.imageset/Contents.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "images" : [
- {
- "filename" : "cometapi.svg",
- "idiom" : "universal"
- }
- ],
- "info" : {
- "author" : "xcode",
- "version" : 1
- },
- "properties" : {
- "preserves-vector-representation" : true,
- "template-rendering-intent" : "template"
- }
-}
diff --git a/OSGKeyboard/Assets.xcassets/cometapi.imageset/cometapi.svg b/OSGKeyboard/Assets.xcassets/cometapi.imageset/cometapi.svg
deleted file mode 100644
index efe9317..0000000
--- a/OSGKeyboard/Assets.xcassets/cometapi.imageset/cometapi.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/OSGKeyboard/Assets.xcassets/custom.imageset/Contents.json b/OSGKeyboard/Assets.xcassets/custom.imageset/Contents.json
deleted file mode 100644
index 998fdfc..0000000
--- a/OSGKeyboard/Assets.xcassets/custom.imageset/Contents.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "images" : [
- {
- "filename" : "custom.svg",
- "idiom" : "universal"
- }
- ],
- "info" : {
- "author" : "xcode",
- "version" : 1
- },
- "properties" : {
- "preserves-vector-representation" : true,
- "template-rendering-intent" : "template"
- }
-}
diff --git a/OSGKeyboard/Assets.xcassets/custom.imageset/custom.svg b/OSGKeyboard/Assets.xcassets/custom.imageset/custom.svg
deleted file mode 100644
index b9ba5cb..0000000
--- a/OSGKeyboard/Assets.xcassets/custom.imageset/custom.svg
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/OSGKeyboard/Assets.xcassets/deepseek.imageset/Contents.json b/OSGKeyboard/Assets.xcassets/deepseek.imageset/Contents.json
deleted file mode 100644
index 2076164..0000000
--- a/OSGKeyboard/Assets.xcassets/deepseek.imageset/Contents.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "images" : [
- {
- "filename" : "deepseek.svg",
- "idiom" : "universal"
- }
- ],
- "info" : {
- "author" : "xcode",
- "version" : 1
- },
- "properties" : {
- "preserves-vector-representation" : true,
- "template-rendering-intent" : "template"
- }
-}
diff --git a/OSGKeyboard/Assets.xcassets/deepseek.imageset/deepseek.svg b/OSGKeyboard/Assets.xcassets/deepseek.imageset/deepseek.svg
deleted file mode 100644
index dc224e4..0000000
--- a/OSGKeyboard/Assets.xcassets/deepseek.imageset/deepseek.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/OSGKeyboard/Assets.xcassets/gemini.imageset/Contents.json b/OSGKeyboard/Assets.xcassets/gemini.imageset/Contents.json
deleted file mode 100644
index ca57c6b..0000000
--- a/OSGKeyboard/Assets.xcassets/gemini.imageset/Contents.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "images" : [
- {
- "filename" : "gemini.svg",
- "idiom" : "universal"
- }
- ],
- "info" : {
- "author" : "xcode",
- "version" : 1
- },
- "properties" : {
- "preserves-vector-representation" : true,
- "template-rendering-intent" : "template"
- }
-}
diff --git a/OSGKeyboard/Assets.xcassets/gemini.imageset/gemini.svg b/OSGKeyboard/Assets.xcassets/gemini.imageset/gemini.svg
deleted file mode 100644
index 87736bb..0000000
--- a/OSGKeyboard/Assets.xcassets/gemini.imageset/gemini.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/OSGKeyboard/Assets.xcassets/groq.imageset/Contents.json b/OSGKeyboard/Assets.xcassets/groq.imageset/Contents.json
deleted file mode 100644
index 3874e19..0000000
--- a/OSGKeyboard/Assets.xcassets/groq.imageset/Contents.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "images" : [
- {
- "filename" : "groq.svg",
- "idiom" : "universal"
- }
- ],
- "info" : {
- "author" : "xcode",
- "version" : 1
- },
- "properties" : {
- "preserves-vector-representation" : true,
- "template-rendering-intent" : "template"
- }
-}
diff --git a/OSGKeyboard/Assets.xcassets/groq.imageset/groq.svg b/OSGKeyboard/Assets.xcassets/groq.imageset/groq.svg
deleted file mode 100644
index 7294646..0000000
--- a/OSGKeyboard/Assets.xcassets/groq.imageset/groq.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/OSGKeyboard/Assets.xcassets/mimo.imageset/Contents.json b/OSGKeyboard/Assets.xcassets/mimo.imageset/Contents.json
deleted file mode 100644
index 1f42ccf..0000000
--- a/OSGKeyboard/Assets.xcassets/mimo.imageset/Contents.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "images" : [
- {
- "filename" : "mimo.png",
- "idiom" : "universal"
- }
- ],
- "info" : {
- "author" : "xcode",
- "version" : 1
- },
- "properties" : {
- "template-rendering-intent" : "template"
- }
-}
diff --git a/OSGKeyboard/Assets.xcassets/mimo.imageset/mimo.png b/OSGKeyboard/Assets.xcassets/mimo.imageset/mimo.png
deleted file mode 100644
index 499bb95..0000000
Binary files a/OSGKeyboard/Assets.xcassets/mimo.imageset/mimo.png and /dev/null differ
diff --git a/OSGKeyboard/Assets.xcassets/minimax.imageset/Contents.json b/OSGKeyboard/Assets.xcassets/minimax.imageset/Contents.json
deleted file mode 100644
index ac7e6a2..0000000
--- a/OSGKeyboard/Assets.xcassets/minimax.imageset/Contents.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "images" : [
- {
- "filename" : "minimax.svg",
- "idiom" : "universal"
- }
- ],
- "info" : {
- "author" : "xcode",
- "version" : 1
- },
- "properties" : {
- "preserves-vector-representation" : true,
- "template-rendering-intent" : "template"
- }
-}
diff --git a/OSGKeyboard/Assets.xcassets/minimax.imageset/minimax.svg b/OSGKeyboard/Assets.xcassets/minimax.imageset/minimax.svg
deleted file mode 100644
index 1d32449..0000000
--- a/OSGKeyboard/Assets.xcassets/minimax.imageset/minimax.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/OSGKeyboard/Assets.xcassets/mistral.imageset/Contents.json b/OSGKeyboard/Assets.xcassets/mistral.imageset/Contents.json
deleted file mode 100644
index f28b753..0000000
--- a/OSGKeyboard/Assets.xcassets/mistral.imageset/Contents.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "images" : [
- {
- "filename" : "mistral.svg",
- "idiom" : "universal"
- }
- ],
- "info" : {
- "author" : "xcode",
- "version" : 1
- },
- "properties" : {
- "preserves-vector-representation" : true,
- "template-rendering-intent" : "template"
- }
-}
diff --git a/OSGKeyboard/Assets.xcassets/mistral.imageset/mistral.svg b/OSGKeyboard/Assets.xcassets/mistral.imageset/mistral.svg
deleted file mode 100644
index 32c6cbd..0000000
--- a/OSGKeyboard/Assets.xcassets/mistral.imageset/mistral.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/OSGKeyboard/Assets.xcassets/moonshot.imageset/Contents.json b/OSGKeyboard/Assets.xcassets/moonshot.imageset/Contents.json
deleted file mode 100644
index 3dc959a..0000000
--- a/OSGKeyboard/Assets.xcassets/moonshot.imageset/Contents.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "images" : [
- {
- "filename" : "moonshot.svg",
- "idiom" : "universal"
- }
- ],
- "info" : {
- "author" : "xcode",
- "version" : 1
- },
- "properties" : {
- "preserves-vector-representation" : true,
- "template-rendering-intent" : "template"
- }
-}
diff --git a/OSGKeyboard/Assets.xcassets/moonshot.imageset/moonshot.svg b/OSGKeyboard/Assets.xcassets/moonshot.imageset/moonshot.svg
deleted file mode 100644
index fb56ac1..0000000
--- a/OSGKeyboard/Assets.xcassets/moonshot.imageset/moonshot.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/OSGKeyboard/Assets.xcassets/openai.imageset/Contents.json b/OSGKeyboard/Assets.xcassets/openai.imageset/Contents.json
deleted file mode 100644
index 7f508a4..0000000
--- a/OSGKeyboard/Assets.xcassets/openai.imageset/Contents.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "images" : [
- {
- "filename" : "openai.svg",
- "idiom" : "universal"
- }
- ],
- "info" : {
- "author" : "xcode",
- "version" : 1
- },
- "properties" : {
- "preserves-vector-representation" : true,
- "template-rendering-intent" : "template"
- }
-}
diff --git a/OSGKeyboard/Assets.xcassets/openai.imageset/openai.svg b/OSGKeyboard/Assets.xcassets/openai.imageset/openai.svg
deleted file mode 100644
index 78caf4f..0000000
--- a/OSGKeyboard/Assets.xcassets/openai.imageset/openai.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/OSGKeyboard/Assets.xcassets/openrouter.imageset/Contents.json b/OSGKeyboard/Assets.xcassets/openrouter.imageset/Contents.json
deleted file mode 100644
index 1ba973b..0000000
--- a/OSGKeyboard/Assets.xcassets/openrouter.imageset/Contents.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "images" : [
- {
- "filename" : "openrouter.svg",
- "idiom" : "universal"
- }
- ],
- "info" : {
- "author" : "xcode",
- "version" : 1
- },
- "properties" : {
- "preserves-vector-representation" : true,
- "template-rendering-intent" : "template"
- }
-}
diff --git a/OSGKeyboard/Assets.xcassets/openrouter.imageset/openrouter.svg b/OSGKeyboard/Assets.xcassets/openrouter.imageset/openrouter.svg
deleted file mode 100644
index 31fe130..0000000
--- a/OSGKeyboard/Assets.xcassets/openrouter.imageset/openrouter.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/OSGKeyboard/Assets.xcassets/qwen.imageset/Contents.json b/OSGKeyboard/Assets.xcassets/qwen.imageset/Contents.json
deleted file mode 100644
index 4ea4e9b..0000000
--- a/OSGKeyboard/Assets.xcassets/qwen.imageset/Contents.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "images" : [
- {
- "filename" : "qwen.svg",
- "idiom" : "universal"
- }
- ],
- "info" : {
- "author" : "xcode",
- "version" : 1
- },
- "properties" : {
- "preserves-vector-representation" : true,
- "template-rendering-intent" : "template"
- }
-}
diff --git a/OSGKeyboard/Assets.xcassets/qwen.imageset/qwen.svg b/OSGKeyboard/Assets.xcassets/qwen.imageset/qwen.svg
deleted file mode 100644
index a4bb382..0000000
--- a/OSGKeyboard/Assets.xcassets/qwen.imageset/qwen.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/OSGKeyboard/Assets.xcassets/siliconflow.imageset/Contents.json b/OSGKeyboard/Assets.xcassets/siliconflow.imageset/Contents.json
deleted file mode 100644
index 2386a84..0000000
--- a/OSGKeyboard/Assets.xcassets/siliconflow.imageset/Contents.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "images" : [
- {
- "filename" : "siliconflow.svg",
- "idiom" : "universal"
- }
- ],
- "info" : {
- "author" : "xcode",
- "version" : 1
- },
- "properties" : {
- "preserves-vector-representation" : true,
- "template-rendering-intent" : "template"
- }
-}
diff --git a/OSGKeyboard/Assets.xcassets/siliconflow.imageset/siliconflow.svg b/OSGKeyboard/Assets.xcassets/siliconflow.imageset/siliconflow.svg
deleted file mode 100644
index f06093b..0000000
--- a/OSGKeyboard/Assets.xcassets/siliconflow.imageset/siliconflow.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/OSGKeyboard/Assets.xcassets/xai.imageset/Contents.json b/OSGKeyboard/Assets.xcassets/xai.imageset/Contents.json
deleted file mode 100644
index a0cd740..0000000
--- a/OSGKeyboard/Assets.xcassets/xai.imageset/Contents.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "images" : [
- {
- "filename" : "xai.svg",
- "idiom" : "universal"
- }
- ],
- "info" : {
- "author" : "xcode",
- "version" : 1
- },
- "properties" : {
- "preserves-vector-representation" : true,
- "template-rendering-intent" : "template"
- }
-}
diff --git a/OSGKeyboard/Assets.xcassets/xai.imageset/xai.svg b/OSGKeyboard/Assets.xcassets/xai.imageset/xai.svg
deleted file mode 100644
index 536e713..0000000
--- a/OSGKeyboard/Assets.xcassets/xai.imageset/xai.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/OSGKeyboard/Assets.xcassets/zhipu.imageset/Contents.json b/OSGKeyboard/Assets.xcassets/zhipu.imageset/Contents.json
deleted file mode 100644
index bcf670c..0000000
--- a/OSGKeyboard/Assets.xcassets/zhipu.imageset/Contents.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "images" : [
- {
- "filename" : "zhipu.svg",
- "idiom" : "universal"
- }
- ],
- "info" : {
- "author" : "xcode",
- "version" : 1
- },
- "properties" : {
- "preserves-vector-representation" : true,
- "template-rendering-intent" : "template"
- }
-}
diff --git a/OSGKeyboard/Assets.xcassets/zhipu.imageset/zhipu.svg b/OSGKeyboard/Assets.xcassets/zhipu.imageset/zhipu.svg
deleted file mode 100644
index f7b1252..0000000
--- a/OSGKeyboard/Assets.xcassets/zhipu.imageset/zhipu.svg
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/OSGKeyboard/Views/MainTabView.swift b/OSGKeyboard/Views/MainTabView.swift
index 824114a..8d040c5 100644
--- a/OSGKeyboard/Views/MainTabView.swift
+++ b/OSGKeyboard/Views/MainTabView.swift
@@ -8,7 +8,19 @@ struct MainTabView: View {
@Environment(\.themePalette) private var palette: ThemePalette
@Environment(\.horizontalSizeClass) private var horizontalSizeClass
- @State private var tab: AppTab = .keyboard
+ @State private var tab: AppTab = {
+ #if DEBUG
+ if let raw = ProcessInfo.processInfo.arguments
+ .first(where: { $0.hasPrefix("-osgStartTab=") })?
+ .split(separator: "=").last,
+ let value = Int(raw),
+ let initial = AppTab(rawValue: value)
+ {
+ return initial
+ }
+ #endif
+ return .keyboard
+ }()
private var usesSplitLayout: Bool {
horizontalSizeClass == .regular
diff --git a/OSGKeyboardShared/Models/ProviderLogo.swift b/OSGKeyboardShared/Models/ProviderLogo.swift
deleted file mode 100644
index b763c09..0000000
--- a/OSGKeyboardShared/Models/ProviderLogo.swift
+++ /dev/null
@@ -1,36 +0,0 @@
-// ProviderLogo.swift
-// OSGKeyboard · Shared
-//
-// Maps a provider id to its asset-catalog logo name. Shared by the iOS
-// app and the macOS menu-bar app so both show identical brand marks.
-
-import Foundation
-
-public enum ProviderLogo {
- /// Asset name for the provider's logo, or `nil` when there is no bundled logo.
- public static func assetName(for providerId: String) -> String? {
- switch providerId {
- case "openai", "whisper": return "openai"
- case "deepseek": return "deepseek"
- case "qwen", "bailian", "alibabaCoding": return "qwen"
- case "moonshot": return "moonshot"
- case "zhipu": return "zhipu"
- case "mimo": return "mimo"
- case "ark", "volcengine": return "ark"
- case "siliconflow": return "siliconflow"
- case "groq": return "groq"
- case "minimax": return "minimax"
- case "openrouter": return "openrouter"
- case "gemini": return "gemini"
- case "anthropic": return "anthropic"
- case "xai": return "xai"
- case "mistral": return "mistral"
- case "cometapi": return "cometapi"
- case "codingPlanX": return "codingplanx"
- case "codex_oauth": return "openai"
- case "apple": return "apple"
- case "custom": return "custom"
- default: return nil
- }
- }
-}
diff --git a/docs/ACCOUNT_MANAGED_GATEWAY_TESTING.md b/docs/ACCOUNT_MANAGED_GATEWAY_TESTING.md
deleted file mode 100644
index ebb60df..0000000
--- a/docs/ACCOUNT_MANAGED_GATEWAY_TESTING.md
+++ /dev/null
@@ -1,127 +0,0 @@
-# Account and managed gateway verification
-
-This checklist validates the optional OSG account path without changing the
-existing local or BYOK defaults. Never record tokens, Apple identifiers, audio,
-prompts, transcripts, or model output while running these checks.
-
-## Automated gate
-
-```bash
-./Scripts/run-tests.sh validate
-./Scripts/run-tests.sh pr
-swiftlint lint --quiet --strict
-xcodebuild \
- -project OSGKeyboard.xcodeproj \
- -scheme OSGKeyboardUITests \
- -destination 'platform=iOS Simulator,name=iPhone 17' \
- -only-testing:OSGKeyboardUITests/AccountCenterUITests \
- test
-xcodebuild \
- -project OSGKeyboard.xcodeproj \
- -scheme OSGKeyboard \
- -destination 'generic/platform=iOS Simulator' \
- -configuration Release \
- -onlyUsePackageVersionsFromResolvedFile \
- CODE_SIGNING_ALLOWED=NO \
- build
-```
-
-The account and managed gateway tests must remain hermetic. They use injected
-Apple integrity adapters, URL loading stubs, and WebSocket fakes rather than
-production credentials.
-
-## Physical-device prerequisites
-
-- A development build signed for `com.osgkeyboard.ios`.
-- Sign in with Apple, App Attest, DeviceCheck, Associated Domains, and both
- Keychain groups enabled in the provisioning profile.
-- `https://osglab.com/.well-known/apple-app-site-association` returns HTTP 200
- without a redirect and includes `X329MZU23S.com.osgkeyboard.ios` for `/i/*`.
-- The production account service is ready at `https://account.osglab.com`.
-- The production service temporarily enables `ALLOW_DEVELOPMENT_APP_ATTEST=true`
- for the test window; disable it again after physical-device testing.
-- OSGKeyboard is installed and enabled with Full Access for managed requests.
-- The test account has enough non-production credits for the requested checks.
-
-## Identity and account
-
-1. Sign in with Apple and verify that nickname, balance, and referral state load
- after a cold launch.
-2. Confirm the raw nonce is never persisted and an App Attest assertion is
- accepted. Repeat after an access-token expiry to exercise one refresh.
-3. Open `https://osglab.com/i/{test-code}` while signed out. Sign in, then
- verify the pending code is redeemed exactly once.
-4. Force-quit and reopen the app. Verify session recovery without another Apple
- prompt and confirm the keyboard extension cannot read the account session.
-5. Sign out and verify account tokens plus shared gateway grants are removed.
-6. Sign in again, choose Delete Account, complete Apple reauthentication, and
- verify local and BYOK features still work afterward.
-
-For destructive verification, use a disposable Apple sandbox identity:
-
-1. Set a nickname, generate an invitation code, and select **Use Credits**.
-2. Delete the account after both confirmations and fresh Apple authorization.
-3. Confirm the app returns to signed-out/BYOK state and no account, grant,
- profile, or purchase state remains visible.
-4. Confirm old access and refresh tokens receive `401`; an Apple revoke outage
- must not restore the locally deleted account.
-5. Sign in again and confirm a new local App Attest key state is registered.
-
-Only pseudonymous immutable ledger, StoreKit audit, and time-limited anti-abuse
-records remain where required for replay and abuse prevention.
-
-## Managed DeepSeek
-
-1. Select **Use Credits** for the first time. Verify the managed-cloud data
- disclosure appears, Cancel leaves BYOK selected, and Agree enables credits.
- Switch away and back again to confirm the disclosure is not repeated.
-2. Verify the runtime uses managed Volcengine ASR and managed polishing together.
-3. Run one polish request and one AI request. Verify actionable behavior for
- insufficient balance, expired grant, timeout, and cancellation.
-4. In the server ledger, verify one reservation and one settlement per request.
- Retrying the same transport request must not create a second charge.
-
-## Managed Volcengine ASR
-
-1. Record approximately ten seconds of Mandarin PCM16LE at 16 kHz.
-2. Verify partial and final results, then run translate-and-polish.
-3. Cancel one recording mid-stream and verify the WebSocket closes without a
- stuck reservation.
-4. Verify session-open fallback, idle timeout, empty result, insufficient
- balance, and concurrency-limit behavior.
-5. Confirm the ledger settles successful sessions and releases failed or
- cancelled reservations.
-
-Managed ASR currently does not send hotwords. Treat this as an explicit product
-difference until the server request schema supports them.
-
-## Regression gate
-
-- Signed-out use remains valid.
-- Local ASR never requires an account.
-- Existing BYOK LLM and ASR credentials still use their direct providers.
-- iCloud settings sync never contains account or gateway tokens.
-- Flow, keyboard typing, and keyboard-extension memory-budget tests pass.
-
-## StoreKit credits
-
-The existing `ByRockyACoffee` product remains a voluntary consumable and never
-grants credits. Configure `500tks` for 500 credits at USD 0.99, `1500tks` for
-1,500 credits at USD 1.99 / CNY 18, and `3000tks` for 3,000 credits at
-USD 2.99 / CNY 28.
-
-1. Use a Sandbox Apple account and sign in to the same OSG account before
- purchasing.
-2. Confirm the purchase supplies the OSG account UUID as `appAccountToken`.
-3. Buy each product and verify the server grants exactly 500, 1,500, or 3,000
- credits and appends one `STOREKIT_PURCHASE` ledger entry before the app
- finishes the transaction.
-4. Submit the same signed transaction again and verify the response is marked
- as replayed without changing the balance.
-5. Interrupt the network after App Store success but before server
- acknowledgement. Relaunch and verify the unfinished transaction reconciles
- once.
-6. Sign in to another OSG account and verify the first account's transaction is
- rejected.
-7. Confirm there is no Restore Purchases action for credit packs and that the
- voluntary tip still changes only the local support count.
diff --git a/docs/APPSTORE_METADATA.md b/docs/APPSTORE_METADATA.md
deleted file mode 100644
index c0e7797..0000000
--- a/docs/APPSTORE_METADATA.md
+++ /dev/null
@@ -1,296 +0,0 @@
-# App Store Connect — OSGKeyboard 2.0.3 (build 94)
-
-> Current metadata baseline for the iOS/iPadOS App Store build. Version and build
-> numbers come from `project.yml`. The repository also contains a separate
-> macOS 15+ Developer ID target; it is not this App Store listing.
-
-## App information
-
-| Field | Value | Notes |
-|---|---|---|
-| App name | `OSGKeyboard` | ≤ 30 characters |
-| Subtitle | `Voice input, everywhere` | ≤ 30 characters |
-| Bundle ID | `com.osgkeyboard.ios` | iOS host target |
-| Version / build | `2.0.3` / `94` | `MARKETING_VERSION` / `CURRENT_PROJECT_VERSION` |
-| Minimum system | iOS/iPadOS 26 | iPhone and iPad |
-| Primary locale | `en-US` | Simplified Chinese is also bundled |
-| Primary category | Utilities | |
-| Secondary category | Productivity | Optional |
-| Age rating | 4+ | No objectionable content |
-
-## URLs
-
-| Field | Value |
-|---|---|
-| Support URL | `https://github.com/hkgood/OSGKeyboard/issues` |
-| Marketing URL | `https://hkgood.github.io/OSGKeyboard/` |
-| Privacy Policy URL | `https://hkgood.github.io/OSGKeyboard/privacy/` |
-| EULA | Leave blank; use Apple's standard EULA |
-
-## Pricing and availability
-
-| Field | Value |
-|---|---|
-| Price | Free |
-| In-App Purchases | Optional consumables: tip `ByRockyACoffee`; managed-credit packs `500tks`, `1500tks`, `3000tks` |
-| Availability | All configured App Store territories |
-| Pre-order | No |
-
-## Description (≤ 4000 characters)
-
-```text
-OSGKeyboard is a voice and typing keyboard for iPhone and iPad. Speak in
-any app and insert the transcript at the cursor, or switch to Chinese and
-English typing without leaving the keyboard.
-
-VOICE INPUT
-
-• On-device by default. iOS 26 SpeechAnalyzer and DictationTranscriber
- transcribe locally.
-• Optional cloud recognition. Use your own provider credentials, or
- sign in with Apple and choose managed credits.
-• Optional AI polish and translation. Use your own provider API key or
- managed credits; without either, recognized text can still be inserted.
-• AI keyboard mode. Ask a spoken question, review the generated answer,
- then explicitly insert or send it.
-• Edit the last verified OSGKeyboard insertion by voice before replacing
- or appending the result.
-
-TYPING
-
-• Chinese full pinyin, Microsoft double pinyin, and Sogou double pinyin,
- with optional fuzzy-pinyin pairs.
-• English autocomplete, autocorrect, and next-word prediction from
- offline resources.
-• Personal dictionary terms can participate in Chinese candidates,
- English suggestions, ASR correction, and polish protection.
-• iPhone and iPad layouts, including iPad globe and editing controls.
-• Optional clipboard history is off by default and keeps up to 15 text
- items from this device or Universal Clipboard in this device's App Group.
- Turning it off keeps existing history; clearing is a separate confirmed action.
-
-PRIVACY
-
-• Limited first-party product analytics; no third-party analytics,
- advertising, tracking SDKs, ATT, or IDFA.
-• Product analytics never includes keyboard input, audio, transcripts,
- prompts, model output, credentials, or personal identifiers. It can be
- disabled in Settings, which deletes queued events.
-• Local recognition does not upload audio.
-• User-configured cloud requests go directly to that provider. Managed-credit
- requests go through OSGKeyboard's managed gateway to the managed provider.
-• Provider keys are stored in Keychain.
-• Clipboard history stays device-local, does not iCloud-sync, and is not
- sent to AI automatically. Text you insert may later be included when you
- actively invoke polish with your configured provider.
-• Core use requires no OSGKeyboard account.
-
-OSGKeyboard's own code is source available for audit and personal,
-non-commercial local use. It is not MIT-licensed or open source; see the
-repository LICENSE for redistribution and commercial-use restrictions.
-
-Requires iOS or iPadOS 26 or later.
-
-https://github.com/hkgood/OSGKeyboard
-```
-
-## Promotional text (≤ 170 characters)
-
-```text
-Voice input anywhere, with on-device recognition by default. Use your own AI key or optional managed credits for cloud speech, polish, translation, and AI answers.
-```
-
-## Keywords (≤ 100 characters)
-
-```text
-keyboard,voice,dictation,speech,transcribe,AI,pinyin,Chinese,English,polish,typing,productivity
-```
-
-## What's new in 2.0.3
-
-```text
-NEW
-• Home can now suggest repeated rare English terms alongside Chinese terms
- for explicit review before adding them to the Personal Dictionary.
-
-CHANGED
-• Cards, settings typography, native selection menus, account rows, and
- primary-to-detail navigation transitions now follow one visual system.
-
-FIXED
-• Apple account restoration now retries transient secure-session failures and
- keeps sign-in loading feedback consistent.
-• English and Chinese candidates now recover reliably after switching apps
- without carrying state from the previous input field.
-```
-
-## App Review information
-
-| Field | Value |
-|---|---|
-| Sign-in required | No |
-| Demo account | Not applicable |
-| Contact info | Maintainer's Apple Developer account details |
-
-### Notes to App Review
-
-```text
-OSGKeyboard is a custom keyboard for iOS/iPadOS 26.
-
-1. Add the keyboard:
- Settings → General → Keyboard → Keyboards → Add New Keyboard →
- OSGKeyboard.
-2. Enable Full Access. It is required for App Group communication between
- the keyboard and host app and for optional provider network requests.
-3. Complete onboarding in the OSGKeyboard host app.
-4. In any editable field, switch to OSGKeyboard and tap the microphone.
- The default local engine uses on-device Apple speech recognition.
-5. Local dictation and user-configured providers require no OSGKeyboard
- account. The Account tab offers optional Sign in with Apple.
-6. After signing in, Settings → AI Service → Use Credits enables the managed
- cloud path. The consumable products are `500tks`, `1500tks`, and `3000tks`.
- Purchased credits are verified by the account service before StoreKit
- transactions are finished.
-7. AI polish and AI mode can use either managed credits or a user-owned
- provider key. Without either, local dictation still inserts recognized text.
-8. Optional tip `ByRockyACoffee` remains a consumable support purchase and
- does not grant managed credits or unlock features.
-9. Clipboard history is off by default. To test it, open Settings →
- Clipboard, enable History, copy text on this device or through Universal
- Clipboard, then return to the keyboard. Secure fields hide the clipboard
- entry point. Turning History off preserves saved items; use the separate
- confirmed clear action to delete them.
-10. First-party Product Analytics is enabled by default under Settings →
- About → Privacy. Turning it off deletes queued events. It does not collect
- keyboard input, audio, transcripts, prompts, model output, or credentials.
-
-Privacy policy:
-https://hkgood.github.io/OSGKeyboard/privacy/
-
-Source and license:
-https://github.com/hkgood/OSGKeyboard
-```
-
-## App Privacy answers
-
-Use conservative disclosures that cover optional cloud recognition, cloud
-polish/translation, and AI mode even though local recognition is the default.
-
-### User Content → Audio Data
-
-- Collected: Yes
-- Purpose: App Functionality
-- Linked to the user: Yes
-- Used for tracking: No
-
-Audio is sent off-device only when the user enables cloud recognition. The
-configured provider may associate requests with the user's credential. In
-managed-credit mode, audio is linked to the OSGKeyboard account for service
-authorization and credit accounting.
-
-### User Content → Other User Content
-
-- Collected: Yes
-- Purpose: App Functionality
-- Linked to the user: Yes
-- Used for tracking: No
-
-This covers transcripts and nearby cursor context used for polish/translation,
-AI questions and skill prompts, optional provider search requests, dictionary
-terms included in provider prompts, and clipboard text only after the user
-actively invokes a clipboard skill, names the clipboard in AI mode, or inserts
-it and requests polish. Skill results may also be handed on-device to an Apple
-Shortcut, while navigation addresses may be opened in the selected map app.
-Device-local clipboard history and typing-learning data by themselves are not
-collected by the developer.
-
-### Contact Info → Name
-
-- Collected: Yes
-- Purpose: App Functionality
-- Linked to the user: Yes
-- Used for tracking: No
-
-The display name supplied by Sign in with Apple is optional and is used only
-for the optional OSGKeyboard account profile.
-
-### Purchases → Purchase History
-
-- Collected: Yes
-- Purpose: App Functionality
-- Linked to the user: Yes
-- Used for tracking: No
-
-StoreKit transaction identifiers, product identifiers, and granted-credit
-results are processed to verify consumable managed-credit purchases, prevent
-replay, and maintain the account credit ledger.
-
-### Identifiers → User ID
-
-- Collected: Yes
-- Purpose: App Functionality
-- Linked to the user: Yes
-- Used for tracking: No
-
-This covers the pseudonymous OSGKeyboard account identifier and scoped
-managed-service grant identifiers. Core use remains available without an
-OSGKeyboard account.
-
-### Identifiers → Device ID
-
-- Collected: Yes
-- Purpose: Analytics
-- Linked to the user: Yes
-- Used for tracking: No
-
-This is an app-scoped random installation identifier. It rotates when analytics
-is re-enabled, after account deletion, or when a different account signs in. It
-is not IDFA and is not used across apps.
-
-### Usage Data → Product Interaction
-
-- Collected: Yes
-- Purpose: Analytics
-- Linked to the user: Yes
-- Used for tracking: No
-
-This covers fixed event names for app and keyboard sessions, purchase-page
-interactions, and invitation actions. It contains no free-form properties.
-
-### Usage Data → Other Usage Data
-
-- Collected: Yes
-- Purpose: Analytics
-- Linked to the user: Yes
-- Used for tracking: No
-
-This covers fixed AI feature categories, execution modes, outcome categories,
-and coarse duration buckets. It does not include prompts, transcripts, model
-output, audio, or keyboard content.
-
-### Do not select
-
-- Advertising, marketing, product personalization, or tracking
-- Email address, phone number, physical address, location, contacts, photos,
- browsing history, or search history
-- Usage data or diagnostics stored only locally or in the user's private iCloud
-
-## Encryption
-
-`Info.plist` declares `ITSAppUsesNonExemptEncryption = false`. Network calls use
-standard HTTPS. Re-evaluate this answer if non-exempt cryptography is added.
-
-## Submission checklist
-
-- [ ] Confirm `project.yml` still reads version 2.0.3 / build 94
-- [ ] Open the existing Xcode project (do not regenerate unless needed)
-- [ ] Run the release build and test suites on macOS with Xcode 26
-- [ ] Replace screenshots with captures from the submitted build
-- [ ] Verify the privacy answers against the submitted provider features
-- [ ] In App Store Connect, add Device ID, Product Interaction, and Other
- Usage Data for Analytics; linked to the user, not used for tracking
-- [ ] Confirm `500tks`, `1500tks`, and `3000tks` are approved, consumable,
- and mapped to the server credit catalog
-- [ ] Confirm `ByRockyACoffee` remains an optional consumable tip and unlocks
- no feature
-- [ ] Upload, select build 94, add review notes, and submit
diff --git a/docs/STOREKIT_TESTING.md b/docs/STOREKIT_TESTING.md
deleted file mode 100644
index b239f37..0000000
--- a/docs/STOREKIT_TESTING.md
+++ /dev/null
@@ -1,122 +0,0 @@
-# StoreKit — Voluntary Support Tip
-
-OSGKeyboard offers a single **Consumable** in-app purchase:
-
-| Field | Value |
-|---|---|
-| **Type** | Consumable |
-| **Product ID** | `ByRockyACoffee` |
-| **Reference name (Connect)** | 给老刘买杯咖啡 |
-| **Display name (en)** | Buy me a cup of coffee |
-| **Display name (zh-Hans)** | 给老刘买杯冰美式 |
-| **Price (China)** | ¥28 (nearest available App Store tier) |
-| **Price (US, suggested)** | $3.99 equivalent tier |
-
-**Important:** this tip does **not** unlock translation, dictionary limits,
-long Flow sessions, or any other capability. The app remains fully free.
-
-Consumable purchases **cannot be restored** (Apple policy). Settings copy
-explains this to users.
-
----
-
-## App Store Connect setup
-
-1. Open **App Store Connect → OSGKeyboard → In-App Purchases**.
-2. Create **Consumable** with Product ID `ByRockyACoffee`.
-3. Add localizations (en + zh-Hans) using the strings in
- `OSGKeyboardShared/*/Shared.strings` (`tip.*` keys) and
- `docs/APPSTORE_METADATA.md`.
-4. Set pricing: **China ¥28** (App Store only offers fixed tiers — ¥28 is
- the nearest to ¥30); pick equivalent tiers for other territories.
-5. Submit the IAP for review **with** the app version that includes the
- Settings → Support the Developer entry.
-
-### Review screenshot (required — fixes「元数据丢失」)
-
-App Store Connect → IAP **ByRockyACoffee** → **审核信息** → **截屏**:
-
-1. Run the app (Simulator or device) with Settings open at the top
- **支持开发者** card showing the green **打赏 ¥28.00** button.
-2. Capture that screen (⌘S in Simulator, or device screenshot).
-3. Upload to **截屏 → 选取文件**.
-4. Optional **审核备注**:
-
-```
-Optional voluntary tip only (Consumable IAP ByRockyACoffee).
-All features free before and after purchase. Settings tab → top of page.
-Consumable — cannot restore (stated in UI).
-```
-
-Save — status should become **准备提交**.
-
----
-
-## Local testing (StoreKit Test — no Connect / sandbox account)
-
-Uses [`OSGKeyboard.storekit`](../OSGKeyboard.storekit). The **OSGKeyboard**
-scheme already references it in `project.yml` (`storeKitConfiguration`).
-
-1. `xcodegen generate && open OSGKeyboard.xcodeproj`
-2. **Product → Scheme → Edit Scheme → Run → Options**
- - Confirm **StoreKit Configuration** = `OSGKeyboard.storekit`
-3. Run **OSGKeyboard** on Simulator (e.g. iPhone 17) or a plugged-in device
-4. Open **Settings** tab → top card **支持开发者**
-5. Tap **打赏 ¥28.00** → StoreKit Test purchase sheet appears
-6. **Buy** → thank-you alert; **Cancel** → no error
-7. Repeat buy once (Consumable allows multiple)
-
-**Debug menu (optional):** Xcode → **Debug → StoreKit → Manage Transactions**
-to view / delete test purchases.
-
----
-
-## Sandbox testing (real App Store sandbox — after Connect IAP is 准备提交)
-
-1. App Store Connect → **用户和访问** → **沙盒** → create a **Sandbox Tester**
-2. On device: **设置 → App Store → 沙盒账户** → sign in (not your real Apple ID)
-3. Install via **TestFlight** or **Debug run without** `.storekit`:
- - To hit Connect products: Edit Scheme → Run → Options → set StoreKit
- Configuration to **None**, then run on device
-4. Settings → **支持开发者** → purchase with sandbox account
-5. Sandbox charges are free; receipt is real sandbox flow
-
----
-
-## Submit to App Review (after local + sandbox pass)
-
-Yes — **wait until testing looks good**, then:
-
-1. IAP **ByRockyACoffee** status = **准备提交** (pricing + screenshot + localizations)
-2. Bump app version in `project.yml` / `CHANGELOG.md` if needed
-3. **Archive** → upload build to App Store Connect
-4. Open the new **App Store version** page → **App 内购买项目** → **+** → select **ByRockyACoffee**
-5. Fill metadata, attach build, submit **version + IAP together** (first IAP rule)
-
-### Sandbox checklist
-
-- [ ] Product loads and shows localized price
-- [ ] Successful purchase shows thank-you alert
-- [ ] User cancel returns to idle (no error spam)
-- [ ] Repeat purchase works (Consumable allows multiple)
-- [ ] No Restore button for this product (Consumable)
-- [ ] Translation, Flow, dictionary, BYOK unchanged after tipping
-
-### macOS note
-
-The menu-bar Mac build (`com.osgkeyboard.mac`) ships via Developer ID outside
-the Mac App Store today. StoreKit products load only for App Store builds.
-The Mac Settings UI is present for parity; tip IAP requires an App Store
-distribution if Mac tipping is enabled later.
-
----
-
-## Files
-
-| File | Role |
-|---|---|
-| `OSGKeyboardShared/Services/Tip/TipProduct.swift` | Product ID constants |
-| `OSGKeyboardShared/Services/Tip/TipPurchaseManager.swift` | StoreKit 2 purchase flow |
-| `OSGKeyboardShared/DesignSystem/SupportDeveloperSection.swift` | iOS Settings UI |
-| `OSGKeyboardMac/MacSupportDeveloperTipRows.swift` | macOS Settings UI |
-| `OSGKeyboard.storekit` | Local StoreKit Test catalog |
diff --git a/docs/TYPING_KEYBOARD_BASELINE.md b/docs/TYPING_KEYBOARD_BASELINE.md
deleted file mode 100644
index dc232df..0000000
--- a/docs/TYPING_KEYBOARD_BASELINE.md
+++ /dev/null
@@ -1,55 +0,0 @@
-# Typing Keyboard Baseline (Phase 0)
-
-Frozen reference for NanoMouse × OSGKeyboard fusion.
-
-## NanoMouse reference
-
-| Field | Value |
-|-------|--------|
-| Repo | https://github.com/xjwhnxjwhn/nanomouse |
-| Frozen commit | `a6177d898a01662ce551b43a01cf82a9f84ca54c` (2026-07-29 tip at clone) |
-| Local clone (gitignored) | `.refs/nanomouse/` |
-
-## KeyboardKit / key shell
-
-NanoMouse does **not** SPM-pin upstream KeyboardKit Pro. It vendors an MIT KeyboardKit tree inside:
-
-`ios/Packages/HamsterKeyboardKit/Sources/KeyboardKit/` (see that folder’s `LICENSE`).
-
-OSG ships a **lean SwiftUI key shell** in-repo (`OSGKeyboardExt/Typing/`)
-behind `TypingLayoutProviding`; KeyboardKit is not linked.
-
-**KeyboardKit Pro is not used.**
-
-## Chinese schema / lexicon
-
-| Choice | Detail |
-|--------|--------|
-| Product intent | Full pinyin + Microsoft/Sogou double pinyin + opt-in fuzzy pairs |
-| Engine | librime 1.17.0 via static XCFramework (BSD-3-Clause) |
-| Binary package | `ghostflyby/librime-xcframework` `1.17.0-pack.1`, checksum `0f0fc13b…1164` |
-| Baseline | rime-pinyin-simp (Apache-2.0) |
-| Modern words | Jieba frequencies + phrase-pinyin-data + pinyin-data (MIT) |
-| Generated lexicon | `Resources/Typing/Rime/osg_pinyin.dict.yaml` (~365K entries) |
-| User learning | librime userdb in App Group |
-| Explicitly excluded | rime-ice / rime-double-pinyin (GPL), Luna / Essay (LGPL) |
-
-The dictionary is rebuilt deterministically by
-`Scripts/typing/build_rime_dictionary.py`; `manifest.json` pins every source
-commit and SHA-256.
-
-## Memory / height budget
-
-| Mode | Target height | Memory notes |
-|------|---------------|--------------|
-| Voice | 281 pt (`KeyboardRootView.totalHeight`) | Matches typing height; no typing engine loaded |
-| Typing | 281 pt (`TypingRootView.totalHeight`) | Host-prebuilt Rime data; extension opens one session |
-| RSS goal | Typing peak < 50 MB | Session closes on voice switch / memory warning |
-
-## Success criteria (Phase 1)
-
-- Top-right tab switches voice ↔ typing; recording/processing locks voice.
-- English QWERTY + 123 / basic symbols.
-- Full pinyin and Microsoft/Sogou double pinyin produce phrase candidates.
-- Fuzzy pairs default off and are enabled individually in Settings.
-- No GPL/LGPL input data in the app; NOTICE lists exact source licenses.
diff --git a/docs/assets/badges/appstore-en.svg b/docs/assets/badges/appstore-en.svg
deleted file mode 100644
index 072b425..0000000
--- a/docs/assets/badges/appstore-en.svg
+++ /dev/null
@@ -1,46 +0,0 @@
-
diff --git a/docs/assets/badges/appstore-zh.svg b/docs/assets/badges/appstore-zh.svg
deleted file mode 100644
index e3f29cd..0000000
--- a/docs/assets/badges/appstore-zh.svg
+++ /dev/null
@@ -1,29 +0,0 @@
-
diff --git a/docs/ios-pip-voice-session-plan.md b/docs/ios-pip-voice-session-plan.md
deleted file mode 100644
index 228612a..0000000
--- a/docs/ios-pip-voice-session-plan.md
+++ /dev/null
@@ -1,385 +0,0 @@
-# iOS PiP 语音会话保活规划
-
-> **文档状态**:产品与架构规划(待验证,未进入实现)
-> **适用范围**:iOS 主 App + 键盘扩展 + Live Activity
-> **目标版本**:待产品验证后确定
-> **创建日期**:2026-07-26
-
----
-
-## 1. Executive Summary
-
-### 1.1 目标
-
-在不要求 OSGKeyboard 长时间占用麦克风的前提下,尽量保持主 App 可响应键盘扩展的听写指令,降低用户在宿主 App 与 OSGKeyboard 之间反复切换的频率。
-
-核心方向是将两个当前耦合的能力拆开:
-
-1. **会话可用性**:主 App 仍可接收键盘命令。
-2. **麦克风采集**:仅在用户明确开始听写时启用,完成后立即释放。
-
-PiP(画中画)只承担系统可见的多任务会话载体,不绕过麦克风授权,也不应使用静音音频循环伪造后台活动。
-
-### 1.2 核心结论
-
-| 决策 | 规划选择 |
-|------|----------|
-| 产品定位 | 将 PiP 作为可选的「免切换模式」,不替代普通 Flow |
-| 麦克风策略 | PiP 空闲时关闭;键盘点按听写后按需激活 |
-| 默认策略 | 保留当前隐私友好的 5 分钟 Flow;PiP 由用户主动开启 |
-| 降级路径 | PiP 不可用或失效时回落到现有 `startflow` 冷启动流程 |
-| 状态展示 | PiP 显示有意义的语音会话状态;Live Activity 继续负责锁屏与灵动岛 |
-| 禁止方案 | 不播放静音文件保活,不使用定位或 VoIP 等无关后台模式 |
-| 上线方式 | 先做真机技术验证和 TestFlight 审核验证,再决定正式产品化 |
-
-### 1.3 非目标
-
-- 不让键盘扩展直接访问麦克风;这是 iOS 平台限制。
-- 不承诺 App 被用户强制退出后仍可免切换听写。
-- 不承诺电话、Siri、相机或其他录音 App 抢占音频设备时继续录音。
-- 不用 PiP 绕过麦克风权限、隐私提示或系统音频策略。
-- 第一阶段不重写 ASR、润色、App Group 或 Darwin 通知管线。
-
----
-
-## 2. 问题定义
-
-### 2.1 平台约束
-
-iOS 键盘扩展无法直接申请或使用麦克风。系统级语音键盘因此必须采用:
-
-```text
-键盘扩展
- → 发送开始/停止命令
- → 主 App 采集并转写
- → App Group 返回结果
- → 键盘插入文本
-```
-
-当主 App 被系统挂起或终止时,键盘无法即时启动录音,只能打开主 App 重新建立会话。当前 Flow 通过持续运行 `AVAudioEngine` 输入链路换取后台可用性,但会带来麦克风长期占用、橙色隐私指示、电量消耗和音频冲突。
-
-### 2.2 用户问题
-
-| 用户感知 | 当前根因 | 目标变化 |
-|----------|----------|----------|
-| 频繁跳转主 App | 后台主进程不可响应 | PiP 有效时直接响应键盘命令 |
-| 麦克风指示长时间亮起 | Flow 会话级连续采集 | 空闲时释放麦克风 |
-| 耗电或发热 | 音频引擎持续采样和处理 | 仅听写期间采样 |
-| 其他 App 无法使用麦克风 | OSGKeyboard 持有输入设备 | 听写结束后主动释放 |
-| 不知道会话是否可用 | Flow、麦克风和进程状态混为一体 | 分开展示「免切换已就绪」和「正在录音」 |
-
-### 2.3 成功定义
-
-PiP 模式下,用户应能:
-
-1. 在 OSGKeyboard 主 App 中主动开启免切换模式。
-2. 将 PiP 小窗收纳到屏幕边缘。
-3. 回到微信、邮件等宿主 App。
-4. 点击键盘麦克风后直接开始听写。
-5. 停止听写后收到文本,同时麦克风在短时间内释放。
-6. PiP 失效时收到明确提示,并能通过现有冷启动路径恢复。
-
----
-
-## 3. 竞品与行业模式
-
-### 3.1 Typeless
-
-Typeless iOS 1.9.0 将该能力命名为 Picture in picture / Skip app switching:
-
-- 用户先在主 App 中主动开启。
-- PiP 可拖到屏幕边缘收纳。
-- 用户在其他 App 的 Typeless 键盘中开始说话。
-- 官方产品说明强调麦克风空闲时关闭,以降低电量消耗。
-
-其公开资料无法证明具体内部实现,因此本规划只借鉴产品模型,不假定其私有代码结构。
-
-### 3.2 Wispr Flow、TypeWhisper 与同类开源项目
-
-常见架构是主 App 持有 `AVAudioEngine`,键盘通过 App Group 与 Darwin 通知控制句子开始和停止。优点是首字延迟低,缺点是会话期间通常持续占用音频输入。
-
-OSGKeyboard 当前 Flow 已属于此模式,并已具备:
-
-- 主 App 会话所有权;
-- 键盘与主 App IPC;
-- 连续采集与 utterance gate;
-- App Group 结果回传;
-- Live Activity;
-- 冷启动与恢复流程。
-
-因此 PiP 应作为会话生命周期的新载体,而不是重建整条语音管线。
-
-### 3.3 SuperWhisper / App Intents 路线
-
-更保守的方案是不做长期后台保活,使用 App Intents、Action Button、快捷指令或显式 App 切换启动录音。该方案最符合系统预期,但无法完全满足键盘内即时听写。
-
-OSGKeyboard 应保留这类入口作为稳定降级,而不是依赖 PiP 达到 100% 可用。
-
-### 3.4 合规边界
-
-以下方式不应采用:
-
-- 循环播放静音音频以防止挂起;
-- 声明与产品无关的定位、VoIP 后台能力;
-- 使用不可见或无实际产品意义的伪视频,仅为延长进程生命;
-- 在用户未明确开启会话时自动恢复麦克风。
-
-PiP 内容需要能被解释为真实的语音会话控制面,例如展示:
-
-- 「免切换已就绪」;
-- 「正在聆听」及音量反馈;
-- 「正在转写」;
-- 暂停、结束或返回 App 操作。
-
----
-
-## 4. 目标产品模型
-
-### 4.1 三层可用性
-
-```text
-层级 0:冷启动
- 主 App 不可用
- → 键盘打开 startflow
- → 主 App 建立语音会话
-
-层级 1:短时 Flow
- AVAudioEngine 会话保持
- → 最低首字延迟
- → 默认 5 分钟无活动后结束
-
-层级 2:PiP 免切换模式
- PiP 保持用户可见的多任务会话
- → 空闲时麦克风关闭
- → 键盘命令触发按需开麦
-```
-
-三个层级必须共用同一份 `FlowSessionBridge` 状态合约,键盘不应根据实现细节分别写三套逻辑。
-
-### 4.2 用户入口
-
-建议在首页提供独立状态卡,而不是继续扩张设置开关:
-
-- 未开启:`开启免切换模式`
-- 启动中:`正在准备画中画`
-- 已就绪:`免切换已就绪 · 麦克风未使用`
-- 录音中:`正在聆听`
-- 失效:`会话已断开,点击恢复`
-
-首次开启时应明确说明:
-
-1. 屏幕上会出现可收纳的 PiP 小窗。
-2. 空闲时不会使用麦克风。
-3. 用户关闭 PiP、强制退出 App 或系统回收进程后,需要重新开启。
-
-### 4.3 键盘状态
-
-键盘麦克风状态应从「主 App 是否活着」升级为明确能力状态:
-
-| 状态 | 表现 | 点击结果 |
-|------|------|----------|
-| 不可用 | 灰色 | 引导权限或 Full Access |
-| 需恢复 | 橙色 | 打开主 App 恢复会话 |
-| PiP 就绪、麦克风关闭 | 绿色 | 请求主 App 按需开麦 |
-| 正在激活麦克风 | 绿色加载态 | 等待真实音频 proof |
-| 正在录音 | 红色/波形 | 发送停止命令 |
-| 正在转写 | 处理中 | 等待结果 |
-
----
-
-## 5. 目标架构
-
-### 5.1 组件边界
-
-```text
-Keyboard Extension
- └─ FlowSessionBridge / Darwin command
- ↓
-Host App
- ├─ VoiceSessionCoordinator
- │ ├─ FlowSessionManager
- │ ├─ PiPVoiceSessionController
- │ └─ AudioCaptureLifecycle
- ├─ FlowContinuousCapture
- ├─ ASR + Polish pipeline
- └─ Live Activity
-```
-
-规划职责:
-
-- `PiPVoiceSessionController`:只管理 PiP 生命周期和展示状态。
-- `AudioCaptureLifecycle`:管理按需激活、音频 proof、停止及释放。
-- `FlowSessionManager`:继续负责命令、ASR、润色和结果回传。
-- `FlowSessionBridge`:发布跨进程能力快照,不让键盘猜测主 App 状态。
-
-### 5.2 状态机
-
-```text
-inactive
- → preparingPiP
- → pipReadyMicOff
- → activatingMic
- → recording
- → processing
- → releasingMic
- → pipReadyMicOff
-
-任意状态
- → interrupted
- → recovering 或 inactive
-```
-
-重要不变量:
-
-1. `pipReadyMicOff` 必须确认音频输入已停止并释放。
-2. 键盘只有在收到 `recording` 和真实 audio proof 后才显示正在录音。
-3. PiP 存活不能等价于麦克风可用。
-4. 电话/Siri 中断后不得静默恢复录音。
-5. 任何超时都要回收麦克风并写入明确错误。
-
-### 5.3 PiP 内容方案
-
-技术验证阶段应比较两类 Apple 官方能力:
-
-1. 基于 `AVPlayerLayer` 的媒体 PiP;
-2. 基于 `AVSampleBufferDisplayLayer` / 视频通话内容源的实时 PiP。
-
-选择标准不是「哪种最容易保活」,而是:
-
-- 是否符合 OSGKeyboard 的真实产品用途;
-- 能否展示动态语音会话状态;
-- 麦克风激活/释放是否稳定;
-- 收纳、锁屏、音频中断行为是否可预测;
-- App Review 是否能清楚理解其用途。
-
-在完成真机和审核验证前,不冻结具体 AVKit 实现。
-
----
-
-## 6. 实施阶段
-
-### Phase 0:技术与审核可行性验证
-
-目标:证明「PiP 存活 + 闲时关麦 + 键盘触发按需开麦」在目标 iOS 版本可行。
-
-验证项:
-
-- PiP 启动、收纳、恢复与关闭;
-- 空闲 30 分钟后主 App 是否仍能响应;
-- 空闲期间系统麦克风指示是否消失;
-- 键盘命令到首个有效音频帧的延迟;
-- 连续 20 次开始/停止是否稳定;
-- 电话、Siri、蓝牙切换、锁屏、低电量模式;
-- 用户关闭 PiP 后的降级行为;
-- TestFlight / App Review 说明是否被接受。
-
-退出标准:
-
-- 空闲时没有麦克风占用;
-- P95 命令到有效音频帧小于 1 秒;
-- 20 次连续听写无僵尸录音或失联状态;
-- 失败后都能回到冷启动路径;
-- 没有使用静音循环或无关后台能力。
-
-### Phase 1:内部可用版本
-
-- 新增 PiP 会话控制器;
-- 将持续采集改造成可重复激活/释放;
-- 扩展跨进程状态快照;
-- 键盘增加激活中、PiP 就绪和失效状态;
-- 复用现有 ASR、润色、结果回传和 Live Activity;
-- 添加状态机与 IPC 单元测试。
-
-### Phase 2:产品化
-
-- 首页免切换状态卡;
-- 首次开启说明与 PiP 收纳引导;
-- 中英文文案与隐私说明;
-- 诊断页增加 PiP、音频会话和最近中断原因;
-- 增加遥测指标,但不采集音频内容。
-
-### Phase 3:灰度与决策
-
-- TestFlight 小流量开启;
-- 比较 PiP 与普通 Flow 的成功率、首字延迟和耗电;
-- 根据审核反馈决定默认入口和长期支持范围;
-- 若 PiP 不稳定或审核风险不可接受,保留为实验功能或停止上线。
-
----
-
-## 7. 测试矩阵
-
-### 7.1 功能场景
-
-| 场景 | 预期 |
-|------|------|
-| PiP 空闲 | 主 App 可响应,麦克风未占用 |
-| 键盘开始听写 | 按需激活并获得真实音频帧 |
-| 停止听写 | 完成转写并及时释放麦克风 |
-| 连续多句 | 每句均重新激活成功,无第二句无音频 |
-| PiP 被关闭 | 键盘切为需恢复,不显示假就绪 |
-| App 被强退 | 清除旧 generation 和僵尸状态 |
-| 电话/Siri 中断 | 当前句失败并提示,不自动偷录 |
-| 蓝牙设备变化 | 音频格式重建,不崩溃 |
-| 网络失败 | 本地 ASR 保留;润色按现有策略降级 |
-
-### 7.2 设备与系统
-
-- 最低支持 iOS 版本、当前稳定版和最新 beta;
-- 刘海机、灵动岛机型、iPad;
-- AirPods、普通蓝牙耳机、车载音频、有线设备;
-- 微信、信息、邮件、Slack、Notes 及自定义文本输入控件;
-- 锁屏、横竖屏、多窗口、低电量和后台刷新关闭状态。
-
----
-
-## 8. 指标与验收
-
-### 8.1 核心指标
-
-| 指标 | 定义 | 目标 |
-|------|------|------|
-| 免切换成功率 | PiP 就绪时无需打开主 App完成听写 | ≥ 98% |
-| 麦克风空闲占用 | 非录音期间仍占麦的时长比例 | 接近 0 |
-| 首帧延迟 P95 | 键盘点击到真实音频 proof | < 1 秒 |
-| 结果回传成功率 | 停止后键盘收到最终结果 | ≥ 99% |
-| 僵尸状态率 | 键盘显示可用但主 App无法响应 | < 0.5% |
-| 恢复成功率 | 失效后通过冷启动恢复 | ≥ 99% |
-
-### 8.2 观察指标
-
-- 每日 PiP 开启人数与启用留存;
-- PiP 被用户主动关闭的比例;
-- 每小时耗电和温升相对普通 Flow 的变化;
-- 音频中断类型分布;
-- 用户因橙色麦克风指示或隐私产生的反馈;
-- App Review 反馈和政策变化。
-
----
-
-## 9. 风险与应对
-
-| 风险 | 等级 | 应对 |
-|------|------|------|
-| PiP 被认定与媒体用途不匹配 | 高 | 提供真实会话 UI、明确审核说明;先 TestFlight/审核验证 |
-| 系统版本改变 PiP 行为 | 高 | 保持冷启动降级;按系统版本做兼容验证 |
-| 按需开麦首字丢失 | 中 | 激活态 + audio proof + 预录缓冲,不提前向键盘宣告录音 |
-| 频繁激活导致音频路由异常 | 中 | 串行状态机、格式重建、媒体服务重置恢复 |
-| PiP 与 Live Activity 状态冲突 | 中 | 单一 coordinator 发布状态,两个 UI 只消费 |
-| 用户误解 PiP 仍在监听 | 中 | 空闲状态明确写「麦克风未使用」,隐私说明可验证 |
-| 其他 App 抢占麦克风 | 中 | 显式中断提示,不承诺并发录音 |
-
----
-
-## 10. 产品决策门
-
-进入代码实现前,需要确认:
-
-1. 是否接受 PiP 作为用户主动开启、系统可见且可收纳的产品形态;
-2. 是否优先「空闲关麦」而接受约数百毫秒的重新激活延迟;
-3. PiP 中展示哪些真实功能,确保它不是纯保活黑窗;
-4. 是否将现有「跳过 App 切换」重命名并拆为普通 Flow / PiP 两种模式;
-5. 最低支持系统和目标测试设备;
-6. 技术验证失败或审核风险过高时,是否接受回退到短 Flow + App Intents。
-
-在上述决策和 Phase 0 证据完成前,不建议直接进入正式实现。
diff --git a/docs/keyboard-accuracy-plan.md b/docs/keyboard-accuracy-plan.md
deleted file mode 100644
index 38247d6..0000000
--- a/docs/keyboard-accuracy-plan.md
+++ /dev/null
@@ -1,350 +0,0 @@
-# 打字键盘输入准确率提升计划
-
-> **文档状态**:产品与工程规划(**实现中**:Phase 1~4 已落地,待真机试打)
-> **适用范围**:iOS 键盘扩展打字面(`OSGKeyboardExt` 打字键网格)+ 共享布局度量(`OSGKeyboardShared`)
-> **关联基线**:`docs/TYPING_KEYBOARD_BASELINE.md`
-> **实现分支**:`feat/keyboard-touch-accuracy`
-> **创建日期**:2026-08-03
-> **目标版本**:待 Phase 0 基线试打后确定发布节奏
-
----
-
-## 1. Executive Summary
-
-### 1.1 目标
-
-在拼音打字主路径上,将点按准确率与按键契约提升到**接近系统键盘的日常体感**:
-
-- 漏触(按了没反应)接近 0
-- 邻键误触明显下降
-- 「不准」不再成为用户槽点
-
-**成功定义(产品语言)**:同设备、同姿势对比系统键盘时,内部试打主观分接近;键缝无空响应;字母键支持预览与滑动改选。
-
-### 1.2 核心结论(冻结)
-
-| 决策 | 选择 |
-|------|------|
-| 准确率策略 | 对齐系统**触控契约 + 分层纠错**,不复刻私有算法 |
-| 字母 / 数字 / 符号 | 按下高亮 → 可滑动改选 → **松手确认** |
-| 删除 | **按下即删** + 长按连删(与现状及系统一致) |
-| 空格 / 回车 | 与字母一致:**松手确认** |
-| 键缝 | 视觉保留间距;触控**不允许无响应**(最近键中心) |
-| 滑出键平面再松手 | **取消本次输入**(不出字) |
-| 视觉 | 键位外观不因热区/智能变形(热区隐形) |
-| 智能边界 | 只微调;用户明确落点或滑动可覆盖 |
-| 语言策略 | 中文拼音过程可偏心;英文先求稳 |
-| 反馈最低标准 | Phase 1~2:**高亮 + 按键音**;气泡为加分项 |
-| 实施顺序 | 无死区 → 按键契约 → 触点微调 → 拼音偏心 → 气泡等 |
-
-### 1.3 非目标(本期不做)
-
-- 绝对准确率 100% 追平系统键盘
-- QuickPath / 滑行输入
-- 依赖 `UIKeyboardLayoutStar` 等系统私有 hit API
-- 完整双 strike zone 个性化、键盘整体漂移重定位(re-spotting)
-- 英文词级 predictive hit 放大(后置评估)
-- 重写 Rime / 抛光引擎(触控层与词级兜底分工,不互相替代)
-
-### 1.4 现实预期
-
-| 目标 | 是否现实 |
-|------|----------|
-| 漏触接近系统 | ✅ Phase 1~2 可达 |
-| 邻键误触主观接近系统 | ✅ Phase 2~4 可达 |
-| 中文拼音连打体感不输系统 | ✅ 相对最有机会(拼音偏心) |
-| 全场景绝对准确率 = 系统 | ❌ 不作为本期 KPI |
-
----
-
-## 2. 背景与现状差距
-
-### 2.1 问题定义(第一性原理)
-
-用户打字时只关心两件事:
-
-1. **我以为我点了谁**(视觉反馈)
-2. **机器最后认了谁**(实际输入)
-
-系统键盘准,是因为整条链路在缩小二者差距,并在不一致时提供改选与词级兜底。OSG 当前布局已对齐系统键位,但触控模型仍接近「普通按钮网格」。
-
-### 2.2 系统键盘能力分层(参考模型)
-
-系统做法是多层流水线(专利 + 私有头文件命名 + 可观察行为),不是单一「热区 +N pt」:
-
-| 层 | 含义 | 本期态度 |
-|----|------|----------|
-| ① 触点估计 | 触点中心 ≠ 意图点,可做偏移/加权 | Phase 3 轻量做 |
-| ② 几何热区 | 视觉 ≠ hit;缝可命中 | Phase 1 必做 |
-| ③ 动态偏心 | 下一键更可能则更好点 | Phase 4 拼音合法优先 |
-| ④ 触摸生命周期 | Down 反馈 / Move 改选 / Up 提交 | Phase 2 必做 |
-| ⑤ 个性化双区 | 按节奏/习惯自适应 | 后置 |
-| ⑥ 词级兜底 | 自动更正 / 候选 | 已有 Rime + 抛光,需保住 |
-
-### 2.3 现状(代码事实)
-
-| 区域 | 现状 | 影响 |
-|------|------|------|
-| 字符键 | SwiftUI `Button`,视觉 frame ≈ hit | 键间 6~8pt 为死区;松手且需仍在键内 |
-| 删除 / 部分工具栏 | `DragGesture(minimumDistance: 0)` 按下即响 | 已接近系统删除契约 |
-| 主动扩大 hit | 基本仅候选栏展开按钮(34 视觉 / 44 hit) | 字母区无 fat-finger 扩展 |
-| 滑动改选 | 无行级/网格级跟踪 | 邻键误触难在松手前纠正 |
-| 点击音 | 字符键未统一走 `KeyboardSoundFeedback` | 反馈弱于系统 |
-| 词级 | Rime 候选 + 抛光 | 后段较强;前段触控不稳会浪费兜底 |
-
-主要相关位置:
-
-- `OSGKeyboardExt/Typing/TypingRootView.swift`(键网格、间距、`keyButton`)
-- `OSGKeyboardExt/Views/ToolbarActionButtons.swift`(按下即响模板)
-- `OSGKeyboardExt/Utilities/KeyboardSoundFeedback.swift`
-- `OSGKeyboardShared/Typing/TypingLayoutProviding.swift`(键位布局)
-
-### 2.4 用户失败模式
-
-| 体感 | 主因 | 主要靠哪一层治 |
-|------|------|----------------|
-| 点到缝没反应 | 间距死区 | Phase 1 |
-| 经常打成邻键 | 无改选窗口 + 无触点/上下文加权 | Phase 2~4 |
-| 滑出键外丢输入 | per-key Button 生命周期 | Phase 2 |
-
----
-
-## 3. 产品宪法(全程遵守)
-
-1. **字母 / 数字 / 符号**:按下高亮 → 可滑动改选 → **松手确认**
-2. **删除**:**按下即删**,长按连删
-3. **键平面内不允许无响应**(缝必命中最近键中心)
-4. **视觉键位不因智能变形**(热区与偏心均隐形)
-5. **智能只微调**;用户滑动或明确落点可覆盖
-6. **中文拼音过程可以更聪明;英文先求稳**
-
-对内沟通口径(Phase 2):
-
-> 不是变慢,是把确认点从按下挪到松开,换来改选能力——和系统键盘一样。
-
----
-
-## 4. 分阶段计划
-
-### Phase 0 — 基线与验收设计
-
-**目的**:能量化「准不准」,避免凭感觉争论。
-
-| 事项 | 说明 |
-|------|------|
-| 记录现状问题 | 漏触 / 邻键错 / 滑出丢键的典型场景 |
-| 定试打脚本 | 至少 3 类:缝点击、邻键偏触、常见拼音连打(如 `zhongguo`、`pingguo`) |
-| 定对比方式 | 同设备、同姿势;系统键盘 vs OSG;主观 1~5 分 + 错字/漏触次数 |
-| 定反馈最低标准 | Phase 1~2:高亮 + 按键音;气泡单列加分 |
-
-**工期**:0.5~1 天
-**产出**:试打清单 + 打分表(可放飞书/Notion;关键结论可回写本文「附录」)
-
-**出口**:脚本固定、打分表可用,再进入开发。
-
----
-
-### Phase 1 — 无死区热区(P0)
-
-**产品一句话**:缝也算键,别点空。
-
-**范围**
-
-- 字母 / 数字 / 符号区:视觉间距保留,触控无空洞
-- 缝内命中:**最近键中心**
-- 边缘键(如 Q/P、A/L)外侧略扩,降低边缘难点
-- 不改变键的视觉大小与间距观感
-
-**工期**:可与 Phase 2 合并开发,但**验收分开**
-**建议**:约短迭代内完成几何命中;若与 Phase 2 同 PR,须分 commit 或分验收项
-
-**验收**
-
-- [ ] 故意点键缝 20 次,0 次无响应
-- [ ] 视觉观感与改前一致
-- [ ] 无明显「看在 A 上却出 S」的跳键
-
----
-
-### Phase 2 — 系统同款按键契约(P0)
-
-**产品一句话**:先告诉你按了谁,松手再算数。
-
-**范围**
-
-| 键类 | 行为 |
-|------|------|
-| 字母 / 数字 / 符号 | Down 高亮 → Move 改选 → Up 提交 |
-| 删除 | Down 即删 + 长按连删(保持) |
-| 空格 / 回车 | 松手确认 |
-| 滑出键平面再松手 | 取消本次输入 |
-| 反馈 | 高亮 + `KeyboardSoundFeedback.keyClick()` |
-
-**本阶段不做**:按键气泡、长按附加符号、滑行输入。
-
-**工期**:约 1 个迭代(含内部试打调参)
-
-**验收**
-
-- [ ] 按下 F 滑到 G 松开 → 只出 G,高亮跟随
-- [ ] 按下后滑出键盘再松 → 不出字
-- [ ] 删除仍为按下即删、可连删
-- [ ] 内部试打:「手感接近系统」主观分显著高于改前
-
-**工程注意(规划级)**
-
-- 宜收敛为网格/行级触摸跟踪,而非继续堆 per-key `Button`
-- 可复用 `RepeatingPressButton` 与现有 UIKit 触控跟踪经验,但字母键生命周期以 Up 确认为准
-- 需单列与候选栏、Shift、123 切换的手势边界用例
-
----
-
-### Phase 3 — 触点微调(P1)
-
-**产品一句话**:按手指意图认键,不只认触点中心。
-
-**范围**
-
-- 对命中点做轻微、稳定的向上偏移(或按触点大小微调)
-- 规则保持可预期;偏移量建议可配置便于试打
-- 中英文均可生效(与语言无关的几何修正)
-
-**工期**:0.5~1 天(可挂在 Phase 2 尾声)
-
-**验收**
-
-- [ ] 拇指自然打字时,偏下误触主观减少
-- [ ] 不引入新的「明显认错键」投诉
-- [ ] 偏移可调,便于内测收敛
-
----
-
-### Phase 4 — 拼音下一键偏心(P1)
-
-**产品一句话**:拼到一半时,更可能的下一家更好点。
-
-**范围(档位 1:合法字母优先)**
-
-- 仅拼音组词过程中启用
-- 依据当前拼音/Rime 状态:合法后续字母热区略增,非法略减
-- **偏心有上限**(微调,不霸道)
-- 用户滑动改选 / 明确落点可覆盖
-- 英文模式:关闭或明显减弱
-- 上屏或开始新音节:偏心状态重置
-
-**后置**:英文词预测式放大、重度概率模型、个性化学习。
-
-**工期**:约 1 个迭代
-**依赖**:必须在 Phase 2 稳定之后(无改选窗口时,偏心误判会被感知为「自作主张」)
-
-**验收**
-
-- [ ] 常见拼音连打,邻键误触下降
-- [ ] 故意打冷门 / 非法后续字母,仍能打上
-- [ ] 视觉上看不出键变大;体感更顺
-- [ ] 可用试打脚本对比「偏心开 / 关」
-
----
-
-### Phase 5 — 体验加分(P2,不阻塞主目标)
-
-| 项 | 说明 | 优先级 |
-|----|------|--------|
-| 按键气泡预览 | 强化所见即所得 | 高(体验) |
-| 候选 / 抛光联动检查 | 触控改善后词级兜底不回退 | 高(质量) |
-| 长按附加符号 | 系统能力对齐,非准确率核心 | 中 |
-| 个性化热区 | 长期数据,后置 | 低 |
-
----
-
-## 5. 推荐排期
-
-```text
-Week 0 Phase 0 基线试打脚本 + 打分
-Week 1 Phase 1+2 无死区 + 松手确认/滑动改选(可合并开发,分开验收)
-Week 1 末 Phase 3 触点微调
-Week 2 内部试打 / 调参 / 边界修复(滑出取消、宽键、Shift、候选冲突)
-Week 3 Phase 4 拼音合法下一键偏心
-Week 3~4 Phase 5 气泡等加分项(可选)
-```
-
-**底线**:Week 1 必须交出 Phase 1 + 2;没有这两项,不宣称「接近系统准确率」。
-
----
-
-## 6. 范围边界
-
-| 做 | 不做(本项目) |
-|----|----------------|
-| 点按准确率与系统同款契约 | QuickPath |
-| 拼音过程热区偏心(合法优先) | 系统私有 hit API |
-| 高亮 + 按键音 | 完整自动更正引擎重写 |
-| 与现有 Rime / 候选协同 | 键盘整体自动平移 / 缩放 |
-| 可配置调参(偏移、偏心强度) | 为准确率牺牲视觉键位一致性 |
-
----
-
-## 7. 风险与对策
-
-| 风险 | 对策 |
-|------|------|
-| 「松手确认」被觉得变钝 | 对内对齐系统口径;用试打错字率对比说话 |
-| 热区重叠导致跳键 | 最近中心 + 偏心上限;内测可关偏心 |
-| 拼音偏心误伤冷门输入 | 合法优先 + 可覆盖;可选调试开关 |
-| 与删除 / 空格 / 候选手势冲突 | Phase 2 单列边界用例;滑出取消 |
-| 扩展内难自动化 UI 测 | Phase 0 人工脚本为主;命中几何/偏心纯逻辑补单测 |
-| 改动面集中在打字面 | 语音面工具栏契约保持;避免一次改两套手感却无验收 |
-
----
-
-## 8. 角色与出口标准
-
-| 角色 | 职责 |
-|------|------|
-| 产品 | 守宪法、主观验收、决定气泡是否进本期 |
-| 设计 | 高亮态;(可选)气泡视觉 |
-| 工程 | Phase 1~4 实现;关键常量可配置 |
-| 内测 | 每 Phase 用同一试打脚本回归 |
-
-**每阶段出口**:试打清单通过 + 无 P0「无响应 / 乱出字」再进入下一阶段。
-
----
-
-## 9. 与版本发布的关系
-
-- 本能力属用户可感知的打字体验提升,合并 `main` 并准备发布时:
- - 按 `AGENTS.md` 用 Conventional Commits 评估版本 bump(预期含 `feat` → **MINOR**)
- - `CHANGELOG.md` 在 `[Unreleased]` 下补双语条目(实现时再写,不在本文提前编造版本号)
-- 功能分支开发期间**不**提前 bump `project.yml` 版本号
-
----
-
-## 10. 一句话执行顺序
-
-> **先量基线 → 无死区 + 系统按键契约 → 触点微调 → 拼音偏心 → 气泡等加分。**
-
----
-
-## 附录 A. 试打脚本草案(Phase 0 细化)
-
-| 编号 | 场景 | 操作 | 记录 |
-|------|------|------|------|
-| T1 | 键缝 | 在同行相邻键缝点击 20 次 | 无响应次数 |
-| T2 | 邻键偏触 | 瞄准某键但故意偏向邻键 20 次 | 错键次数;有/无滑动改选 |
-| T3 | 滑动改选 | 按下 A 滑到 S 松开(Phase 2+) | 是否只出 S |
-| T4 | 滑出取消 | 按下后滑出键盘松开(Phase 2+) | 是否不出字 |
-| T5 | 删除契约 | 点按 / 长按删除 | 是否按下即删、可连删 |
-| T6 | 拼音连打 | `zhongguo` / `pingguo` 等各 5 遍 | 错字次数、主观 1~5 |
-| T7 | 偏心开关 | Phase 4 后 T6 对比开/关 | 错字差、误伤冷门键与否 |
-
-对比时尽量:同一 iPhone、竖屏、单手拇指、系统键盘与 OSG 各跑一轮。
-
----
-
-## 附录 B. 修订记录
-
-| 日期 | 说明 |
-|------|------|
-| 2026-08-03 | 初稿:基于系统键盘分层分析与产品讨论冻结 Phase 0~5 |
-| 2026-08-05 | 开分支 `feat/keyboard-touch-accuracy`;落地 Phase 1(无死区)+ Phase 2(网格 Down/Move/Up)+ Phase 3(触点上偏);纯逻辑见 `KeyHitTesting` / `TypingKeyLayout`,触控见 `TypingKeyTouchPad` |
-| 2026-08-05 | Phase 4:全拼合法下一键偏心(`PinyinNextKeyResolver` + `rawInput`);双拼/英文保持中性;歧义最近键加权,单键明确命中不受偏置 |
diff --git a/docs/keyboard-memory-budget.md b/docs/keyboard-memory-budget.md
deleted file mode 100644
index 3207e73..0000000
--- a/docs/keyboard-memory-budget.md
+++ /dev/null
@@ -1,81 +0,0 @@
-# Keyboard memory budget — acceptance (OSGDiag)
-
-Phase 0 / 2 memory work is validated on a **physical iPhone** with Console
-filtering for `OSGDiag`. Unit tests and `xcodebuild` cover compile-time
-wiring; jetsam behavior is device-only.
-
-## Hybrid Flow (product default)
-
-- Foreground host is **light by default**: orphan Live Activity cleanup only —
- **no** auto `startSession` / continuous capture on appear.
-- Capture starts only on explicit Start / `osgkeyboard://startflow` / mic press.
-- Idle background capture is stopped so a parked host does not jetsam the keyboard.
-- **Do not** stack CLM + Rime + ASR in the same second after onboarding.
-- ASR warmup runs on **first mic press** (`beginUtterance`), gated by
- `HostMemoryBudget` (~260 MB RSS). `hostHeavy` is set only while heavy work
- actually runs, then cleared. A sticky `hostHeavy` (host died mid-work)
- expires after `hostHeavyMaxAge` (~120 s) and is cleared on
- `clearFlowState` / host-launch reconciliation so typing 中文/EN is not
- permanently blocked.
-
-## Console checklist
-
-Filter Console by process separately: host `OSGKeyboard` vs extension
-`com.osgkeyboard.ios.keyboard`. Host-only filter will never show `KVC.*`.
-
-| Scenario | Expect |
-|----------|--------|
-| **Force-quit host**, open Notes, switch to OSG | First `dyld.constructor`, then `KVC.init` → `viewDidLoad` → `viewDidAppear` |
-| Host foreground right after launch | `skip capture` + `postOnboardingWarmup scheduled … delay=45s` (no immediate Rime/CLM) |
-| ~45 s later, host still active | Serial `rime.installIfNeeded` then `clm.prepare` |
-| First mic press | `scheduleASRWarmup`; `hostHeavy` only while work runs |
-| Switch to typing | Single `rime.prepare` / `englishPrepare` |
-
-If neither `dyld.constructor` nor `KVC.init` appears after force-quitting the host,
-the extension is dying in dyld (Shared≈9.4 MB + librime) — next lever is splitting
-Rime out of Shared.
-
-## RSS comparison (optional)
-
-Record `OSGDiag` `rss=` tags for:
-
-1. Cold voice surface only
-2. Cold typing after prepare
-3. Host foreground + extension
-
-Target: typing peak below the old “voice + eager Librime construct” baseline.
-
-## Extension physical-footprint budget
-
-`phys_footprint` is the primary extension metric because device jetsam follows
-it more closely than RSS. `KeyboardExtensionMemoryTelemetry` records structured
-`[OSGDiag/memory] extMemory` lines at lifecycle and heavy-resource milestones,
-plus 50 ms samples during the first four seconds:
-
-- **Normal:** below 36 MiB
-- **Warning:** 36–40 MiB
-- **High:** 40–48 MiB; 40 MiB is the internal safe peak
-- **Critical:** 48 MiB or above
-
-The approximate 60 MiB device boundary is not a public Apple contract. The
-40 MiB target deliberately reserves room for transient SwiftUI, Rime, and
-system-framework pages. Telemetry is observation-only: crossing a band logs
-`crossed=1` but does not change the selected surface or unload resources.
-
-Each record includes the current and peak footprint, delta from process start,
-elapsed startup time, surface/language, Full Access, clipboard state, and
-operation-specific context. Filter Console by `OSGDiag/memory` and compare:
-
-1. `KVC.viewDidLoad.afterInstallServices`
-2. `KVC.viewDidLoad.afterInstallSwiftUI`
-3. `typing.englishPrepare.done`
-4. `typing.rimePrepare.done`
-5. `clipboard.reload.done`
-6. `KVC.viewDidAppear.done`
-
-## Structural split
-
-- Extension links **OSGKeyboardShared** only (no Charts / StoreKit / Speech / HostSupport).
-- Host embeds **OSGKeyboardHostSupport** (ASR, CLM, CloudASR, tip/charts UI).
-- Heavy assets (`osg_pinyin.dict.yaml`, CLM bin, licenses, local-asr catalog)
- ship in the **host app** bundle; extension reads Rime from App Group after deploy.
diff --git a/docs/local-asr-architecture.md b/docs/local-asr-architecture.md
deleted file mode 100644
index 1175ce9..0000000
--- a/docs/local-asr-architecture.md
+++ /dev/null
@@ -1,259 +0,0 @@
-# OSGKeyboard 本地 ASR 技术架构
-
-> **文档状态**:1.7.0 代码事实 + 后续评测方向
-> **适用范围**:macOS 15+ 本地听写;iOS 26+ 使用 Apple SpeechAnalyzer。
-> **当前结论**:Mac 默认安装目录模型为
-> `qwen3-mlx-0.6b-4bit`,通过 `mlx-audio-swift` 做 Qwen3 MLX 真流式识别;
-> 模型不可用时走 Apple Speech fallback。Sherpa 已不在当前 catalog 或运行路径中。
-
-## 1. 架构摘要
-
-本地 ASR 的专有名词质量由三层共同完成:
-
-1. **ASR bias**:`PersonalDictionary` 与内置技术词经
- `LocalASRBiasAdapter` 生成 Qwen3 `promptBias`。
-2. **确定性纠错**:识别后按个人词库 `aliases → term` 做边界受控替换。
-3. **Polish 保真**:把内置词参考作为补充上下文交给润色层;无 API Key 或润色失败时
- 仍返回本地识别与纠错结果。
-
-这三层已经接入 Mac 的 live 与 batch 路径。“Mac 本地路径不消费词库”不再是当前事实。
-
-## 2. 当前端到端数据流
-
-```mermaid
-flowchart LR
- Audio["MacAudioRecorder · 16 kHz samples"] --> Pipeline["MacDictationPipeline"]
- Dict["PersonalDictionary"] --> Bias["LocalASRBiasAdapter"]
- Lexicon["phrases.tsv · BuiltinLexiconIndex"] --> Bias
- App["Front app + locale"] --> Bias
- Bias --> Prompt["promptBias"]
- Bias --> Pairs["correctionPairs"]
- Bias --> PolishTerms["polishFragment"]
-
- Pipeline -->|local + installed MLX| Live["MacMLXLiveCapture"]
- Pipeline -->|batch/recovery| Local["MacLocalASRService"]
- Live --> Qwen["MacMLXStreamingASRProvider"]
- Local --> Qwen
- Local -->|model unavailable / Apple selected| Apple["MacSpeechLocalASR"]
- Prompt --> Qwen
- Qwen --> Raw["Raw transcript"]
- Apple --> Raw
- Raw --> Correct["LocalASRTranscriptCorrector"]
- Pairs --> Correct
- Correct --> Polish["PolishingService"]
- PolishTerms --> Polish
- Polish --> Insert["MacTextInsertionService"]
-```
-
-### 2.1 引擎选择与回退
-
-| 项目 | 当前事实 |
-|---|---|
-| 默认模型 ID | `qwen3-mlx-0.6b-4bit` |
-| 可选 MLX 模型 | 0.6B 4-bit、1.7B 4-bit |
-| Catalog | `OSGKeyboard/Resources/LocalASR/local-asr-catalog.json` |
-| 下载源 | `hf-mirror.com` 与 Hugging Face repository files |
-| 推理 | `MLXAudioSTT.Qwen3ASRModel` |
-| Live partial | 100 ms 音频 feed;流式 session 定期 decode |
-| Batch | 同一 MLX 模型的 `generate(audio:context:language:)` |
-| Fallback | Apple Speech 的本地 `SFSpeechURLRecognitionRequest` |
-
-旧 Sherpa model ID 只在偏好迁移逻辑中映射到当前 MLX 默认值,不代表 Sherpa
-backend 仍可运行。当前 catalog 的 `runtimes` 为空。
-
-### 2.2 词库接线
-
-`MacDictationPipeline.resolveLocalBias` 与 `MacMLXLiveCapture.resolveBias` 都读取
-`store.personalDictionary`,再调用:
-
-```text
-LocalASRBiasAdapter.adapt(
- dictionary + locale + frontAppBundleId + backend capabilities
-)
-```
-
-适配器当前输出:
-
-| 输出 | 当前消费者 |
-|---|---|
-| `promptBias` | Qwen3 MLX streaming `StreamingConfig.context` 与 batch `generate(context:)` |
-| `correctionPairs` | `LocalASRTranscriptCorrector`,在 polish 前修正 aliases |
-| `polishFragment` | `PolishingService` 的 `dictionarySupplement` |
-| `diagnostics` | `LocalASRBiasDiagnosticsStore` |
-| `hardHotwords` | 为具备 hard-hotword capability 的 backend 保留;当前 Qwen3 MLX 不使用 |
-
-个人词优先;`BuiltinLexiconIndex` 从 `phrases.tsv` 选择 `weight >= 4` 的 Top-N,
-代码编辑器/终端前台场景优先 `computer_terms`。默认最多考虑 300 个内置 ASR 词,
-Qwen3 soft prompt 最长 800 字符,润色补充最多 40 个内置词。
-
-### 2.3 Apple Speech fallback
-
-Apple fallback 强制 `requiresOnDeviceRecognition = true`,系统缺少对应语言模型时会
-明确失败,不会静默切云。中文路径会准备打包的 Apple Custom Language Model;
-个人词库仍参与识别后的 alias 纠错与 polish 保真。
-
-当前 `LocalASRCapabilities.appleSpeech` 声明 `hotwordMode = none`,因此不要把
-`contextualStrings` 描述为已经由个人词库动态注入。相关 API 虽有适配入口,但当前
-capability 不生成 hard hotwords。
-
-## 3. 代码索引
-
-| 主题 | 当前路径 |
-|---|---|
-| Mac 听写编排 | `OSGKeyboardMac/MacDictationPipeline.swift` |
-| 本地引擎选择 / fallback | `OSGKeyboardMac/MacLocalASRService.swift` |
-| MLX live capture | `OSGKeyboardMac/MacMLXLiveCapture.swift` |
-| MLX provider | `OSGKeyboardMac/MacMLXStreamingASRProvider.swift` |
-| MLX streaming session | `OSGKeyboardMac/MacMLXStreamingSession.swift` |
-| Apple Speech fallback | `OSGKeyboardMac/MacSpeechLocalASR.swift` |
-| Bias payload / capability | `OSGKeyboardShared/Models/LocalASRBiasPayload.swift`, `LocalASRCapabilities.swift` |
-| Bias 构建 | `OSGKeyboardShared/Services/LocalASRBiasAdapter.swift` |
-| 内置词索引 | `OSGKeyboardShared/Services/BuiltinLexiconIndex.swift` |
-| 用户词库 | `OSGKeyboardShared/Models/PersonalDictionary.swift` |
-| 云 ASR bias | `OSGKeyboardShared/Models/PersonalDictionary+ASRBias.swift` |
-| Apple CLM | `OSGKeyboardHostSupport/Services/CustomLanguageModelManager.swift` |
-| 内置 TSV | `OSGKeyboard/Resources/CustomLanguageModel/v1/phrases.tsv` |
-
-## 4. 设计目标与边界
-
-### 4.1 目标
-
-- Mac 本地听写离线可用,音频默认不离开设备。
-- iOS 与 Mac 复用 `PersonalDictionary` 和 `phrases.tsv` 的源数据。
-- 每个 backend 如实声明 soft prompt、hard hotword、streaming 与 reload 成本。
-- 模型下载、校验、安装和选择显式可管理。
-- 质量、延迟、内存与误触发均可量化评测。
-- 本地失败不静默回退云端。
-
-### 4.2 非目标
-
-- 不把一万词全量塞进 ASR prompt。
-- 不把 polish 当作唯一专名纠错层。
-- 不把尚未进入 catalog/runtime 的 Sherpa 或 SenseVoice 描述成当前能力。
-- 不承诺未经同一测试集验证的模型质量。
-- 不把 iOS 生成的 CLM `.bin` 直接喂给 MLX 或其他 backend。
-
-## 5. 能力模型
-
-`LocalASRCapabilities` 区分:
-
-| 字段 | 含义 |
-|---|---|
-| `hotwordMode` | `none` / `promptOnly` / `perRequest` / `recognizerScoped` / `cloudVocabulary` |
-| `maxPromptCharacters` | soft prompt 上限 |
-| `maxHotwordCount` | hard hotword 上限 |
-| `supportsStreaming` | 是否提供真流式 partial |
-| `hotwordReloadCost` | `none` / `recognizerReload` / `modelReload` |
-
-当前实际运行矩阵:
-
-| Backend | 当前角色 | Bias | Streaming |
-|---|---|---|---|
-| Qwen3 MLX | Mac 默认 | `promptOnly`,800 字符 | 是 |
-| Apple Speech | Mac fallback / 显式选择 | ASR 层 `none`;后处理与 polish 仍接词库 | 否 |
-| iOS SpeechAnalyzer | iOS 主路径 | Apple CLM + 本地纠错 | 渐进结果 |
-| Cloud ASR | 用户显式选择 | 按 provider 使用个人词库 | 按 provider |
-
-## 6. 竞品研究中仍有效的结论
-
-基于 2026-03 的公开源码快照,保留以下架构结论,不把它们当作 OSG 当前实现:
-
-| 项目 | 观察 | 对 OSG 的启示 |
-|---|---|---|
-| OpenLess | 多 provider;本地 Qwen3 词典接线有限 | README 不能把统一词库接口等同于 backend 已消费 |
-| Typeflux | 词库限额、动态排序、项目词学习 | Top-N 与上下文排序有价值;自动学习必须可确认 |
-| SayIt | Sherpa Qwen3 recognizer-scoped hotwords | 若未来重做 Sherpa POC,词库变化需计入 recognizer 重建成本 |
-| VoiceSnap | SenseVoice 离线与静音处理 | 可作为速度/资源基线,不代表具备个性词库 |
-| OpenBroca | 显式 model selection 与 manifest | 禁止扫描目录后任取第一个模型 |
-
-这些研究只支撑未来实验。当前发布架构仍是 Qwen3 MLX + Apple Speech fallback。
-
-## 7. 模型管理
-
-当前安装由 catalog 描述 repository files、模型布局与体积,安装状态由 manifest
-和必要文件校验决定。设计原则保持:
-
-- Catalog 与 runtime 分离。
-- `selectedModelId` 显式保存;旧 ID 有确定迁移规则。
-- 下载进入 staging,经校验后原子发布。
-- 无效或未安装模型不能假装可用。
-- 中国大陆镜像与官方 Hugging Face 可按偏好选择。
-
-ModelScope、自定义企业镜像与额外 backend 可以作为后续 catalog 扩展,但不是
-1.7.0 当前下载路径。
-
-## 8. 评测方法
-
-### 8.1 测试集
-
-| 类别 | 内容 | 目的 |
-|---|---|---|
-| A 普通中文 | 日常口语 50 句 | CER / 误触发基线 |
-| B 技术术语 | SwiftUI、Cursor、Qwen3-ASR 等 50 句 | 专名召回 |
-| C 用户词典 | 20 个个人词,每词多句 | bias 与 alias 纠错 |
-| D 长句 | 30 秒以上口语 | streaming 稳定性与 finalize |
-| E 噪声 / 短句 | 低 SNR、少于 2 秒 | 幻觉与热词污染 |
-| F 中英混合 | 技术会议和代码口述 | 语言提示与专名保真 |
-
-### 8.2 当前对照矩阵
-
-| 配置 | 说明 |
-|---|---|
-| Baseline | Qwen3 MLX 0.6B,不传 bias |
-| B1 | 0.6B + `promptBias` |
-| B2 | B1 + alias correction + polish supplement |
-| Quality | Qwen3 MLX 1.7B + B2 |
-| Fallback | Apple Speech + correction / polish |
-| Reference | 用户所选 Cloud ASR + `PersonalDictionary` |
-
-未来 Sherpa/SenseVoice 只能作为新增 POC 行,不能替换当前 baseline 名称。
-
-### 8.3 指标
-
-- Raw CER/WER。
-- 用户词 raw/final hotword recall。
-- False hotword rate。
-- 录音结束到最终插入的延迟,以及 live partial 首次可见延迟。
-- 8 GB Apple-silicon Mac 上的峰值内存、CPU 与模型加载时间。
-- 无网络完成率、模型安装成功率、fallback 成功率。
-
-若未来评估 hard-hotword backend,建议门槛:
-
-- 用户词召回相对当前 Qwen3 MLX + B2 提升至少 20%。
-- False hotword rate 不高于 2%。
-- 30 秒音频端到端延迟不超过当前基线 1.5 倍。
-- 分发体积、签名、公证与 8 GB 设备内存均可接受。
-
-## 9. 回退与风险
-
-| 条件 | 当前/要求行为 |
-|---|---|
-| MLX 模型未安装 | 使用 Apple Speech fallback |
-| MLX live 失败 | 返回 batch recovery 信号 |
-| Apple 本地语言模型缺失 | 明确报错并提示系统下载,不切云 |
-| Polish 缺 Key、超时或失败 | 返回本地识别/纠错文本 |
-| 用户禁用云 | 不静默切换云 ASR |
-| 热词 dump / 近静音幻觉 | 丢弃可疑 live 结果并尝试 batch |
-
-主要后续风险:
-
-1. 800 字符 soft prompt 对低频专名的提升有限。
-2. 内置 Top-N 过多会污染短句或近静音输入。
-3. alias 替换必须保持整词/高置信边界。
-4. 1.7B 模型在低配 Mac 上的内存与首载延迟需持续量化。
-5. 新 backend 必须先证明收益,再承担下载、签名和维护成本。
-
-## 10. 后续方向
-
-1. 用固定测试集持续比较 0.6B / 1.7B、无 bias / 分层 bias。
-2. 将 diagnostics 与实际命中/截断数据用于调节 Top-N 和 prompt 上限。
-3. 验证 Apple Speech fallback 的个人词动态提示能力后,再决定是否修改 capability。
-4. 只有 hard-hotword 收益达到阈值时,才恢复 Sherpa Qwen3 POC。
-5. 自动词库学习若实验,必须默认关闭、本地处理、用户确认后写入,并可审计/清空。
-
-## 11. 修订记录
-
-| 日期 | 说明 |
-|---|---|
-| 2026-08-11 | 按 1.7.0 代码重写:Qwen3 MLX streaming 默认、Apple Speech fallback、LocalASRBiasAdapter/PersonalDictionary 已接线;移除 Sherpa 当前路径叙述 |
-| 2026-03-31 | 初版竞品研究与评测框架 |
diff --git a/docs/mac-parity-audit-2026-08-23.md b/docs/mac-parity-audit-2026-08-23.md
deleted file mode 100644
index babc1bc..0000000
--- a/docs/mac-parity-audit-2026-08-23.md
+++ /dev/null
@@ -1,818 +0,0 @@
-# OSGKeyboard · iOS 完整检查 + macOS 差距与移植建议报告
-
-> **报告日期**:2026-08-23
-> **当前版本**:2.0.1(build 90)— `project.yml:51-52`
-> **审查范围**:iOS Host (`OSGKeyboard/`, 88 .swift) + iOS HostSupport (`OSGKeyboardHostSupport/`, 29 .swift) + iOS Extension (`OSGKeyboardExt/`, 27 .swift) + Shared (`OSGKeyboardShared/`, 230 .swift) + macOS (`OSGKeyboardMac/`, 39 .swift) + macOS Tests (3 .swift)
-> **方法**:只读代码考古,证据来自 `project.yml` 共享 / 排除清单 + 文件结构 + 关键源码摘录 + `CHANGELOG.md`
-
----
-
-## TL;DR
-
-| 维度 | iOS | macOS | 结论 |
-|---|---|---|---|
-| 产品形态 | 宿主 App + 自定义键盘扩展 | 单窗口菜单栏 App + 浮动 HUD | **不同形态**——Mac 不是键盘,是"按住 Option 说话→⌘V 注入" |
-| 核心流水线 | Flow 跨进程会话 + SpeechAnalyzer | 单一进程 + MLX Audio Qwen3 流式 | **架构不可同构** |
-| LLM 润色 | ✅ 完整(`PolishingService`) | ✅ 完整(**直接复用** iOS `PolishingService`) | **完全对齐** |
-| 本地 ASR | iOS 26 `SpeechAnalyzer + DictationTranscriber` | **MLX Qwen3-ASR 0.6B/1.7B 4-bit** + Apple Speech 兜底 | **完全不同的技术栈** |
-| 云 ASR | 7 家供应商 + 流式 | ✅ **完全对齐**(同 `CloudASRClientFactory`) | **完全对齐** |
-| 个人词典 | ✅ | ✅ | **完全对齐** |
-| 历史 / 使用统计 | ✅ | ✅(iCloud KVS 共用) | **完全对齐** |
-| 润色样式 + 学习 | ✅ | ✅ | **完全对齐** |
-| iCloud 同步 | ✅(5 路:设置/词典/历史/使用/样式) | ✅(**5 路全部对齐**) | **完全对齐** |
-| 提示页 (Tip) | ✅ | ✅ | **完全对齐** |
-| 自定义语言模型 CLM | ✅ UI + 后台 | ⚠️ 后台调用了,**无 UI** | **部分缺失** |
-| 剪贴板历史 + AI 技能 | ✅ | ❌ 共享代码已编译但**无 Mac UI** | **完全缺失** |
-| AI 提示轮播 / 上下文技能 | ✅ | ❌ 共享代码已编译但**无 Mac UI** | **完全缺失** |
-| Flow 跨进程会话 / PiP | ✅ | ❌ | **Mac 形态不需要** |
-| 自定义键盘 (Rime/拼音/英文) | ✅ | ❌(`Typing/**` 全排除) | **Mac 形态不需要** |
-| OSG 账户 (Sign in with Apple + 积分 + 推荐) | ✅ | ❌ | **完全缺失** |
-| App Attest | ✅ | ❌(无对应 entitlement) | **完全缺失** |
-| 一方分析 (Analytics) | ✅(108 测试覆盖) | ❌(共享代码已编译但**无任何调用**) | **完全缺失** |
-| 助手指令 (Shortcuts) | ✅ | ❌ | **完全缺失** |
-| 屏幕唤醒 / 锁 / Live Activity | ✅ | ❌ | **Mac 形态不需要** |
-
-**最关键的发现**:
-
-1. **macOS 复用策略 = 源码级 include**。Mac 不 `import OSGKeyboardShared`——`project.yml:570-602` 把 Shared + HostSupport 的 `.swift` 文件直接编进 Mac target,再用 `excludes:` 列表过滤 iOS-only 文件。**整个框架里没有 `#if os(macOS)` 条件编译**(Mac target 内部有几处 `import AppKit` 守卫,是防御性的)。
-2. **LLM 润色、云 ASR、iCloud 同步、本地 ASR 模型管理、模型目录、个人词典、润色样式、提示页、Onboarding——这 9 大块是 iOS / Mac 完全对齐的**。差异主要在"宿主 App 才有"的系统集成(Account、App Attest、Analytics、Shortcuts、Flow 跨进程会话)。
-3. **"共享代码已编进 Mac 但零调用"的浪费**:`OSGKeyboardShared/Features/Account/`、`Features/ManagedGateway/`、`Features/Analytics/`、`Services/AIClipboard*.swift`、`Services/AIHint*.swift`、`Services/AIUserSkill*.swift` —— 这些文件编译进了 Mac 二进制但没有任何 Mac 文件引用。**编译器开销 + 二进制体积 + 维护面全白付**。要么真正接进 Mac,要么从 Mac target 排除。
-4. **Mac 真正独占的体验是 MLX 流式 ASR**。这是 iOS 受限于 ARM NEON + iOS 26 平台绑定做不到的能力,恰好把 Mac 的 Apple Silicon 算力用足。
-5. **移植优先级不应是"把 iOS 全搬到 Mac"**。Mac 是"按住说话→润色→注入"工具,自然形态完全不同。**真正应该补的是:剪贴板 AI、CLM 管理、Account 体系、Analytics。**其他(Flow、自定义键盘)不应该硬塞进 Mac。
-
----
-
-## 1. iOS 端完整状态
-
-### 1.1 Target 拓扑
-
-| Target | 平台 | 类型 | .swift 数 | 备注 |
-|---|---|---|---|---|
-| `OSGKeyboard` | iOS 26 | App | 88 | 宿主 App,沙盒、StoreKit、iCloud、App Attest |
-| `OSGKeyboardExt` | iOS 26 | App Extension (键盘服务) | 27 | `RequestsOpenAccess: true` |
-| `OSGKeyboardShared` | iOS 26 | Framework | 230 | 跨进程共用 + 复用到 Mac |
-| `OSGKeyboardHostSupport` | iOS 26 | Framework | 29 | 宿主专用(Speech/AVFoundation/Charts/StoreKit) |
-| `OSGKeyboardMac` | macOS 15+ | App | 39 | 菜单栏单窗口,**沙盒关闭**(Accessibility 需要) |
-| `OSGKeyboardMacTests` | macOS 15+ | Unit Test | 3 | 挂在 Mac App bundle 内 |
-| `OSGKeyboardTests` | iOS 26 | Unit Test | 108 | 覆盖 host + shared + hostsupport |
-| `OSGKeyboardExtTests` | iOS 26 | Unit Test | 16 | 覆盖 ext + shared |
-| `OSGKeyboardUITests` | iOS 26 | UI Test | — | TestFlight 截图自动化 |
-
-### 1.2 iOS Host App (`OSGKeyboard/`) — 用户可见的所有功能
-
-**导航**:4 Tab (Keyboard / Skills / Styles / Settings) on iPhone;iPad 自动切换到 `NavigationSplitView` + 240pt 侧栏 + `WideStatusFooter`。
-
-**7 大用户场景**:
-
-1. **Onboarding**(`Views/OnboardingExperienceView.swift`)—— 7 步:欢迎 → 权限 → 键盘安装 → 切换引导 → 4-feature 实践(语音/翻译/回复/AI)→ 登录奖励 → 完成。匿名实践用 `OOBEClientInfrastructure` 拿服务器发放的体验金。
-2. **Home 仪表盘**(`Views/HomeView.swift:122`)—— 7 天柱状图 + 指标瓦片 + Flow 连接卡 + 词典/历史卡 + 中英文高频词条 chip。
-3. **Settings**(`Views/SettingsView.swift:30`)—— 7 个子面板:账户、AI 代理、剪贴板、Locale、润色强度、翻译目标、语音识别、文本润色、通用、关于。NavigationStack + `SettingsRoute` 枚举。
-4. **Account Center**(`Views/Account/AccountCenterView.swift:11`)—— 已登录摘要、积分 + 等级、StoreKit 商品列表、购买历史链接、登出 / 删号 (reauth)、推荐链接。下拉刷新。
-5. **Polish Styles**(`Views/PolishStylesView.swift:11`)—— 内置/社区/自定义样式网格 + 编辑器 sheet + "从历史学习"按钮。
-6. **AI Agent Skills**(`Views/AIAgentSkillsView.swift:12`)—— 已装/未装技能卡,安装调起 `shortcuts://` 或 bundled `.shortcut` 文件。
-7. **Legal / 隐私**—— `PrivacyPolicyView` / `OpenSourceLicensesView` / 远程 web view。
-
-**宿主端 Services(`OSGKeyboard/Services/`)** 关键件:
-
-| 文件 | 职责 | LOC 估算 |
-|---|---|---|
-| `FlowSessionManager.swift` | **唯一拥有者**——AVAudioSession 激活、连续 `.playAndRecord` 捕获、ASR 选择、后处理、`PolishingService` 调用、AI 模式、App Group 写、utterance token、launch reconciliation、start/stop/end session | ~3500 |
-| `FlowPictureInPictureController.swift` | PiP keep-alive(`AVPictureInPictureVideoCallViewController` 0.1pt content,no audio session) | — |
-| `FlowASRPostProcessor.swift` | 本地引擎转写后处理(`LocalASRTranscriptCorrector.apply`) | — |
-| `FlowTerminationCoordinator.swift` | 进程退出时同步释放 mic、结束 LiveActivity | — |
-| `FlowAnalyticsOperationRegistry.swift` | 会话级分析去重,订阅 `FlowSessionManager` 事件 | — |
-| `RimeDeploymentController.swift` | Rime 资源部署(host-owned) | — |
-| `AIAgentShortcutInstaller.swift` | 调起 `shortcuts://` 安装 iCloud 分享的技能 / bundled `.shortcut` | — |
-| `OOBEClientInfrastructure.swift` | 匿名 OOBE 体验金客户端 | — |
-| `OfficialSkillCatalogRefreshService.swift` | 15 分钟 ETag-aware 刷新 `https://account.osglab.com/v1/content/skills` | — |
-| `AIHintRefreshService.swift` | 12 小时静默刷新远程提示包 | — |
-| `AnalyticsHostService.swift` | `BGTaskScheduler` 注册 (`com.osgkeyboard.ios.analytics-sync`) + `NWPathMonitor` + 后台 drain | — |
-| `HostReturnService.swift` | 解析 `FlowSessionBridge.pendingHostBundleId()` 通过 `HostAppURLRegistry` 重开原 App | — |
-| `AppURLHandler.swift` | `UIApplicationDelegate` + `UISceneDelegate`:`osgkeyboard://startflow` / `https://osglab.com/i/...` 通用链接 / `didBecomeActiveNotification` 反弹保留 `sourceApplication` | — |
-| `AppPermissions.swift` | `AVAudioApplication.requestRecordPermission` (iOS 17+) + `SFSpeechRecognizer` + `PasteAccessResult` 一次性 `pasteAccessVerified` | — |
-
-**宿主端 Models 关键点**:
-- `AppGroupConfiguration` 完整 Codable blob —— 镜像到 App Group + iCloud KVS
-- `ProviderConfig` 8 家供应商预设(openai/ark/deepseek/qwen/zhipu/moonshot/siliconflow/groq)
-
-**关键 iOS-only 调用**:
-- `UIOpenURLContext.options.sourceApplication` (iOS 26 only) —— 保留源 bundle id(host-return 白名单唯一路径)
-- `AVAudioApplication.requestRecordPermission` (iOS 17+)
-- `SpeechAnalyzer + DictationTranscriber` (iOS 26 only)
-- `OSAllocatedUnfairLock` (iOS 16+)
-- iOS 26 Icon Composer (`OSGKeyboard/AppIcon.icon`)
-- `UIBackgroundModes: audio` + `BGTaskScheduler`
-- `applinks:osglab.com` Universal Links
-- `LSApplicationQueriesSchemes` 白名单 50+ 国外 App scheme
-- `SKIncludeConsumableInAppPurchaseHistory=true` —— StoreKit 2 一次性商品进历史
-
-**iOS-only entitlements**(在 `OSGKeyboard.entitlements`):
-- App Group `group.com.osgkeyboard.shared`(与扩展共享)
-- 两个 keychain-access-groups(shared + iOS-only)
-- `aps-environment: $(APP_ATTEST_ENVIRONMENT)` ← xcconfig 注入(debug=development / release=production)
-- Sign in with Apple `Default`
-- iCloud KVS `$(TeamIdentifierPrefix)com.osgkeyboard.ios`(**单字符串**,非数组 —— 数组会破坏自动签名)
-- 音频输入
-- 关联域名 `applinks:osglab.com`
-
-**StoreKit 配置**(`OSGKeyboard.storekit`):4 个消耗型 —— 自愿打赏 `ByRockyACoffee` (¥28) + 3 档账户积分 `500tks/1500tks/3000tks` (¥8/¥18/¥28)。**无订阅**。
-
-### 1.3 iOS HostSupport 框架 (`OSGKeyboardHostSupport/`)
-
-设计原则:**唯一**链接 `Speech / AVFoundation / Charts / StoreKit` 的 iOS 框架——键盘扩展**不**链接它,把 jetsam 预算留给 Shared。
-
-**子目录**:
-
-| 路径 | 内容 |
-|---|---|
-| `Services/` | ASR + 云 ASR + Flow 捕获 + Dictation + Tip + `CloudASR/` + `Tip/` |
-| `Models/` | `AudioBufferSnapshot+AVFoundation.swift` —— `AVAudioPCMBuffer` ↔ 共享 `AudioBufferSnapshot` 适配 |
-| `DesignSystem/` | `SevenDayUsageChart` + `UsageStatsCluster` + `SupportDeveloperSection` |
-| `Features/Account/` | `AccountAPIClient` / `SignInWithApple` / `DeviceIntegrity` (App Attest) / `HostPrivateAccountKeychain` / `AccountModels` / `OOBEGrantProvisioningCoordinator` |
-
-**关键 Services**:
-
-- **`ASRService.swift:1`** —— 公共协议 `transcribe(stream:locale:)` 返回 `AsyncStream` (`.capability/.partial/.final/.error`);`transcribeChunk(samples:locale:)` 用于流水线 Flow 路径。`ASRServiceFactory.make(store:)` 返回 `SpeechAnalyzerASR`(本地)或 `CloudASRService`(云)。`SpeechAnalyzerASR` 用 **iOS 26 `SpeechAnalyzer + DictationTranscriber` + bundled `SFCustomLanguageModelData`**,锁用 `OSAllocatedUnfairLock`,支持流水线 chunk 复用。
-- **`CloudASRService.swift:1`** —— 云 ASR 前门。`CloudASRClientFactory.make(...)` 返回 8 个客户端:`ZhipuCloudASRClient` / `AlibabaFunASRClient` / `BailianRealtimeASRClient` / `VolcengineCloudASRClient` / `OpenAIRealtimeASRClient` / `PromptCloudASRClient` / `ManagedVolcengineASRClient` / `UnsupportedCloudASRClient`。
-- **`LiveDictationController.swift:1`** —— `@MainActor ObservableObject`,阶段机 `idle/recording/processing/denied/error`,自管 `AVAudioEngine + AVAudioSession`。生产键盘**不**用它,**生产用 `FlowSessionManager + FlowContinuousCapture`**。
-- **`FlowAudioSessionCoordinator.swift:1`** —— 进程级 `AVAudioSession` + Flow 的 `AVAudioEngine` 拥有者。`.voiceChat` 模式。
-- **`CustomLanguageModelManager.swift`** —— 单例,准备 bundled `OSGKeyboardCLM.bin` 写进 App Group。指数退避 30s/120s/600s。**重到 `MainAppRoot.scheduleCLMWarmup` 延后 45s 启动,Flow 忙时拒绝运行**。
-
-**HostSupport 持久化**:
-- CLM 编译产物 → App Group container
-- Account 令牌 → host-only keychain(**不**走 App Group)
-- Managed AI 凭证 → 单独 `GatewayGrantKeychainStore`;**只**镜像"会话可用"布尔进 App Group
-- Tip 计数 → 标准 `UserDefaults`
-
-### 1.4 iOS Keyboard Extension (`OSGKeyboardExt/`)
-
-**入口**:`KeyboardViewController.swift:33-38` —— `@objc(KeyboardViewController) @MainActor` `UIInputViewController`。**单一** `KeyboardState` ObservableObject。
-
-**生命周期(`KVC.swift`)**:
-- `init` → 启动 `KeyboardExtensionMemoryTelemetry`(warning 36MB / safe 40MB / critical 48MB)
-- `viewDidLoad` (`:148-207`) → 设 `showsSystemGlobeKey = isPad`、`primaryLanguage = "mis"`(隐藏"English"副标题误导),读 `TypingInputConfiguration.preferredSurfaceOnOpen()`,调 `refreshLayoutMode() + installKeyboardHeight() + configureDictationBehavior() + installServices() + installSwiftUI()`
-- `viewWillAppear` (`:256-293`) → 标记 `KeyboardSetupBridge.markExtensionAppearance(hasFullAccess:)`、刷新 Flow/config、同步 onboarding、准备 `KeyboardHapticFeedback` generators
-- `viewIsAppearing/viewDidAppear` (`:295-356`) → 锁高度 `lockPresentedKeyboardHeight()`,禁系统手势延迟,记录 analytics
-- `viewWillDisappear` (`:209-254`) → 通知 `AnalyticsExtensionService.keyboardWillDisappear()`、取消 `assistantFieldActionRefreshTask`、使 `editHintScheduler` 失效、持久化最后 surface
-- `textDidChange/selectionDidChange` → `refreshReturnKeyRole`、同步 English 文档上下文
-- `didReceiveMemoryWarning` (`:390-404`) → **取消流水线、退出 typing 模式、强制回 voice surface** —— typing 引擎是最大内存消费者
-
-**`installServices` (`:419-547`)** 安装的 8 个 coordinator:
-- `EditHintScheduler`(mic 上方一句话提示)
-- `KeyboardTextInserter`(插入 Flow 转写 / undo / redo / 粘贴剪贴板)
-- `KeyboardConfigSync`(App Group 配置 + Darwin observers + onboarding 镜像)
-- `KeyboardFlowCoordinator`(Flow start/stop + session monitor + watchdogs)
-- `LastInputEditCoordinator`(长按 → 编辑上次输入)
-- `AIKeyboardCoordinator`(长按 → AI 模式)
-- `ClipboardCaptureCoordinator`(`changeCount` 轮询,secure-field 抑制,建议条)
-- `AnalyticsExtensionService`
-
-**`installStateActions` (`:551-655`)** 装的 30+ action:`beginRecording/endRecording/tapMic/cancelVoiceInput/beginEditLastInput/.../setMode/setLocale/setEngineMode/setTranslationTargetLocaleId/insertNewline/insertSpace/deleteBackward/undoLastInsertion/redoLastInsertion/copySelection/cutSelection/setSurface`
-
-**手势模型**:
-- 长按 push-to-talk → `RecordButtonGesturePolicy` 阈值 `longPressDuration = 0.45s`
-- 点击 mic → 切换(`RecordButton.tapAction`)
-- 重复删除 → `RepeatingPressButton` + `RepeatingDeleteTiming`(80→50→30→15 ms 加速)
-- Shift 长按 → `TypingSessionController.shiftHeld`,双击 capsLock
-- 语言切换 → 3-tab `KeyboardInputTab`(assistant / 中文 / 英文)
-- 触觉 → `KeyboardHapticFeedback`(5 个 Taptic generator + 4 个按键角色 + `KeyboardHapticIntensity` off/light/strong)
-- 声音 → `UIDevice.playInputClick()` + 系统音 1155 (delete),通过 `@retroactive UIInputView: UIInputViewAudioFeedback` 启用点击声
-- 主题 → 始终深色(`Palette.dark` 是 canonical)
-
-**iOS-only 扩展调用**:`UIInputViewController` / `UIInputView` / `UITextDocumentProxy` / `NSExtensionContext` / `UIPasteboard.general` / `UIImpactFeedbackGenerator` / `UIDevice.playInputClick()` / `AudioServicesPlaySystemSound(1155)` / `UIButton` / `UICollectionView` + `UICollectionViewCompositionalLayout` / `UIHostingController`
-
-**Host/Extension 边界数据**(`AppGroupPersistor.load(into:)` 读取):
-- `providerId` / `baseURL` / `apiKey`(`Keychain.apiKeyOutcome` 解析 shared keychain `$(AppIdentifierPrefix)com.osgkeyboard.shared`)
-- `model` / `modeId`(恒为 `polish`)/ `localeId` / `engineMode`(local/cloud)
-- `translationTargetLocaleId` / `handednessPreference`(左/右手 delete↔space 互换)/ `clipboardHistoryEnabled` / `clipboardCandidateBarEnabled`
-- `keyboardHapticIntensity` / `clipboardSkillSnapshot`(启用技能 ID)/ `apiKeyAvailability`
-
-**`KeyboardConfigSync` (`KeyboardConfigSync.swift:24-62`)** 安装 4 个 `FlowSessionDarwinObserver`:`session.changed` / `command.changed` / `transcription.changed` / `host.ready.changed`
-
-### 1.5 iOS Shared Framework (`OSGKeyboardShared/`)
-
-**目录**:
-
-| 路径 | 内容 |
-|---|---|
-| `Constants/AppGroup.swift` | App Group ID 唯一源 |
-| `Core/Configuration/` | `ConfigurationStore` 协议 + iOS 实现 `AppGroupStore+ConfigurationStore.swift` + `LiveConfigurationStore` |
-| `DesignSystem/` (9 文件) | `Theme` / `RecordButton` / `RecordButtonGesturePolicy` / `WaveformView` / `EditTextPager` / `CardPageLayout` / `SonicParticleField` / `ThemedRoot` / `UsageStatCard` / `UsageSurfaceCard` |
-| `Features/Analytics/` (10 文件) | `AnalyticsClient` / `AnalyticsRepository` (SQLite) / `AnalyticsUploadCoordinator` / `KeyboardUsageRepository` (跨进程 SQLite WAL) / ... |
-| `Features/ManagedGateway/` (8 文件) | `ManagedLLMClient` / `GatewayGrantCoordinator` (actor) / `GatewayGrantCredentialStore` / `ManagedGatewayAccountAccessPolicy` / `ManagedGatewayScopePolicy` / `ManagedGatewayQuestionRouter` / `ManagedGatewayModels` / `OOBEGatewayGrantCoordinator` |
-| `Localization/SharedL10n.swift` | `NSLocalizedString` against `Shared.strings`;解析 `AppUILanguage` |
-| `Models/` (~50 文件) | 见下 |
-| `Resources/` | `ClipboardSemantics/{*.mlmodel,*.json}` (6 CoreML) / `PolishStyles/{manifest.json,builtin.*.json}` (10 内置样式) / `Typing/English/{english_lexicon.{bin,tsv},english_bigrams.tsv}` |
-| `Services/` (~80 文件) | 见下 |
-| `Typing/` (24 文件) | librime + 拼音 + 英文 autocorrect + 触屏 + 布局 |
-| `Utilities/` (21 文件) | 日志、错误模型、keychain、locale、Han 脚本、PCM/WAV、内存预算等 |
-
-**关键 Services 详解**:
-
-- **`PolishingService.swift:34-105`** —— `public actor`。`polish(_ raw:mode:systemPrompt:context:) -> PolishOutcome(text:qualityDegraded:polishStyleID:polishStylePrompt:)`。**单一融合 LLM 调用**:T1 自纠 → T2 填料清理 → T3 同音纠正 → T4 标点 → T5 结构 → 应用样式。Fun personality 在 heavy 强度切到"仅格式"共享核心。
-- **`LLMClient` 协议** (`Services/LLMClient.swift:91-150`) —— `polish(_:systemPrompt:timeout:)` + `complete(messages:tools:timeout:options:)`。实现:`LLMClientFactory`(OpenAI 兼容)、`AnthropicLLMClient`(Anthropic Messages API)、`ResponsesAPILLMClient`(OpenAI Responses)、`ManagedLLMClient`(账户 grant 范围)、`AIModeSearchFallbackClient`(搜索→纯)。
-- **`Keychain.swift:15`** —— `kSecClassGenericPassword` 读写 + iCloud 同步变体 + `OnFirstUnlockThisDeviceOnly` 本地变体 + XCTest 内存回退。
-- **`AIClipboardSkill.swift:44`** —— 技能模型:`id` / `systemImage` / `titleKey` / `kind`(`.direct/.transform/.export`)/ `isDefault` / `shortcutName` + iCloud 分享 URL + bundled `.shortcut` 资源 / `thinkingEnabled` / `customName/Summary/Prompt` / `requiresShortcut` / `isUserCreated` / `isOfficial` / `managedGatewayTaskKind` / `supportsReplyStyle`。
-- **`ClipboardHistoryStore.swift:10-84`** —— App-Group-backed observable store。合并通过 `ClipboardHistoryPolicy`(去重、清洗、保留)。
-- **`ClipboardSemanticAnalyzer.swift:1-58`** —— 本地:`NLTokenizer/NLTagger` + 6 CoreML 分类器(`ClipboardSemantics/*.mlmodel`)产生 `language/dates/addresses/phoneNumbers/urls/personNames/organizationNames/sentiment/task/question/invitation/complaint/replyableMessage` 意图。
-- **`ClipboardSkillSemanticRanker.swift:11-66`** —— 用语义分析 + 偏好语言对完整目录排序,固定 generic Reply 兜底。Stateless / `Sendable`。
-- **`SpeechHistoryStore.swift:10-78`** —— iCloud-KVS 镜像 observable,prompt-fingerprint dedup。
-- **`LocalASRModelManager.swift:11`** —— macOS Qwen3-MLX 权重 Application-Support 安装管理器。状态持久化到 `installed-manifest.json`。**Mac 专用**(iOS 用系统 SpeechAnalyzer)。
-- **`LocalASRBiasAdapter.swift:9`** —— 分层偏差:`PersonalDictionary.effectiveEntries` + 内置 `BuiltinLexiconIndex.topTerms(...)`;按前台 App bundle id 选 `builtin-computer` vs `builtin-top`(Xcode/VSCode/Android Studio/IntelliJ/AppCode/Sublime/Terminal/iTerm2/Warp)。
-- **`LocalASRTranscriptCorrector.swift:9-65`** —— 确定式 alias→canonical 替换,最长匹配优先,ASCII whole-word 感知。
-- **`PolishStyleLearningService.swift:64`** —— `build(from:)` 从 `SpeechHistoryEntry` 历史生成 `PolishStyleLearningCorpus`(5000 字符 `requiredEffectiveCharacterCount`)。
-- **`AIQuestionService.swift:20`** —— `actor AIConversationStore` 保留 `retainedConversationRounds` 轮 / 对话。
-- **`AIEventExtraction` / `AIAddressExtraction` / `AITodoExtraction`** —— 解析 LLM 输出为 `start|end|title|location` / `origin|destination` / 列表。**全部 fail-closed**。
-- **`HostAppURLRegistry`** —— 白名单 deep-link 路径(`deployrime`、`settings/clipboard`、`skill/run` 等),host + ext 共用。
-- **`KeyboardSetupBridge`** —— `markExtensionAppearance(hasFullAccess:)` + OOBE 实践会话标志。
-- **`AppGroupConfigDarwin`** —— `CFNotificationCenter` 发 `com.osgkeyboard.config.changed`(host 写完后)。
-- **`FlowSessionBridge` + `FlowSessionKeys`** —— 跨进程 mailbox 全部 App-Group key:`flowHostReady` / `flowHostReadyAt` / `flowHeartbeat` / `flowSessionActive` / `flowSessionExpires` / `hostHeavy/At` / `hostGeneration` / `transcriptionResult` / `transcriptionPartial` / `transcriptionError{Kind,PolishWarning}` / `pendingKeyboardUtteranceId` / `lastPiPArmAttemptAt` / `flow.commandPayload.v1` / `flow.commandJournalPayload.v2` / `flow.resultPayload.v1` / `flow.ackPayload.v1` / `flow.startTransaction.v1` / `flow.readyPayload.v1`。
-- **`SettingsCloudSync` / `SpeechHistoryCloudSync` / `UsageStatisticsCloudSync` / `AppCloudSync`** —— KVS 镜像;`ICloudSyncPreferences` + `SyncDeviceID` 保留每设备身份。
-- **`AIHintStore` + `AIHintPool` + `AIHintKeywordExtractor` + `AIHintKeywordCompressor` + `AIHintLocalCatalog`** —— 闲置热词轮播:App-Group-pack loader、合并远端 + 本地卡片、确定性 10 或 22 字符关键词抽取(zh/en)。
-- **`TranscriptionPolishFallback.swift`** —— LLM 润色失败或跳过时,交付原始转写 + 软警告。
-- **`WhatsNewDemoScenario`** —— "What's New" 时间轴 driver,host(peek/consume)和 ext(DEBUG)共用。
-
-**Polish 子系统详细**:
-- `PolishContext` (`Models/PolishContext.swift`):appContext + precedingText + followingText + fieldHints + dictionarySupplement + maxPrecedingChars=600 + maxFollowingChars=200
-- `AppContext` 5 种(code/email/chat/document/unknown)
-- `AppContextDetector` (`:30-116`):3-fallback 链——`textDocumentProxy.documentContextBeforeInput` 启发式(iPad 沙盒安全;键盘读不到前台 bundle id)→ 30 分钟缓存 → 环境 blend
-- `PolishStylePack` (`Models/PolishStylePack.swift:9-87`):`Codable, Equatable, Identifiable, Sendable`;`id/name/prompt/allowsAddedEmoji/kind(.builtin/.user)/createdAt/updatedAt`
-- `PolishStyleLimits`:`maximumUserPacks = 8` / `maximumPromptCharacters = 6_000`
-- 内置样式分两组:`.practical`(light/structured/formal/chat)+ `.fun`(dating/flex/corp/diba/xhs)
-
-**Typing 子系统**(`Typing/`):
-- `LibrimeEngine.swift` —— `@MainActor` 中文 IME,部署 3 套 schema:`osg_pinyin` / `osg_double_pinyin_mspy` / `osg_double_pinyin_sogou`
-- `EnglishSuggestionEngine.swift` —— 3 槽 QuickType(verbatim / correction / completion),`inVocabularyFrequencyGap = 250`
-- `EnglishLexicon.swift` —— mmap 40k 词 `english_lexicon.bin`
-- `EnglishSystemLexicon.swift` —— `#if canImport(UIKit)` 守卫包 `UITextChecker + UILexicon`
-- `EnglishQWERTYProximity.swift` —— 空间编辑代价
-- `TypingSessionController` —— 拥有 typing surface:language/page/shift state、English 引擎、librime 引擎、拼音/英文消歧、autocap、句号快捷、shadow preceding text、`supplementaryWords`、hot reload
-- `TypingSurfaceMetrics` (`Models/TypingSurfaceMetrics.swift`) + `KeyboardChromeLayout` —— 单一源:键行指标、底部动作行分数、总高 281pt,iPad narrow/wide(narrow 54pt / wide 76pt),`wideIPadWidthThreshold = 1100`
-- `TypingInputConfiguration` —— `TypingInputSchema` (3 case) + `DefaultInputMode` (voice/pinyin/english)
-- `PeriodShortcut.swift` —— iOS 句号快捷:letter→number 后第二个空格 0.45s 内变 ". " + arm sentence Shift
-- `TypingAutocapitalization` —— 镜像 `UITextAutocapitalizationType`
-
-**Models (~50 文件) 关键**:
-- `AppGroupConfiguration` —— 完整 Codable blob
-- `SyncedAppSettingsV2` —— per-field `SyncedField` LWW + broken-clock containment (6h skew horizon)
-- `LLMProvider` —— 8 家云预设
-- `AIUserSkill` / `AIAgentSkillLayout` / `OfficialSkillCatalog` / `OfficialSkillDefinition` —— 技能体系
-- `PolishStylePack` / `PolishStyleCatalog` / `PolishStyleLearningCorpus`
-- `PersonalDictionary` / `+Merging` / `+ASRBias` —— 跨设备词典,iCloud 合并;提供 `hotwords` / `asrPromptBias` / `alibabaHotwordEntries` / `vocabularySyncFingerprint`
-- `ClipboardHistoryEntry` / `SpeechHistoryEntry` / `SyncedSpeechHistory`
-- `PolishContext` / `FieldHints` / `AppContext` / `AppUILanguage`
-- `ProviderConfig` / `CloudProviderRole` / `CloudASRModels` / `LocalASRModelCatalog` / `LocalASRCapabilities` / `LocalASRBiasPayload`
-- `TypingInputConfiguration` / `TypingSurfaceMetrics` / `KeyboardChromeLayout`
-- `HandednessPreference` / `KeyboardHapticIntensity` / `PolishIntensity` / `AIResponseLength`
-- `FlowUtteranceRequest` / `FlowUtteranceMode` / `FlowUtteranceChunkConfig` / `FlowHandoffPolicy` / `FlowAck` / `FlowResult` / `FlowCommand` / `FlowReadySnapshot` / `FlowStartTransaction` / `FlowTranscriptionError` / `FlowFieldContext`
-- `MicVoiceAvailability` / `+Keyboard` —— mic 状态 + 派生
-- `EditableInputReference` —— 跨进程最后插入引用(10 min TTL、≤1200 graphemes、schema v1)
-- `EditSessionState` —— 关闭状态机 `.inactive/.preparing/.listening/.processing/.review/.applying/.appending/.failed`
-- `AISessionState` —— `.inactive/.idle/.preparing/.listening/.recognizing/.generating/.ready/.awaitingSend/.inserted/.sent/.failed`
-
-**Utilities (21 文件)**:
-- `OSGLog.swift` —— subsystem + 类别(`flow/clm/config/asr/keyboardExt`)
-- `OSGDiag.swift` —— NSLog + 内存快照(`task_info`)
-- `FlowTrace.swift` —— `[trace] stage=...`
-- `FlowPipelineDiagnostics.swift` / `FlowCaptureTailDrain.swift` / `FlowUtteranceEndCoordinator.swift` / `FlowUtterancePCMStore.swift`
-- `UtteranceStreamChunker.swift` / `UtteranceBatchFallbackPolicy.swift` / `UtteranceTranscriptGuard.swift` / `UtteranceTranscriptStitcher.swift` / `TranscriptOverlapUtilities.swift` / `TranscriptLanguageDetector.swift`
-- `ProgressiveDictationTranscriptAccumulator.swift`(**Mac 排除**)
-- `DictationTextComposer.swift` / `FinalChunkRecovery.swift` / `PCMSampleWavEncoder.swift`(mono Float32 @16kHz → WAV)
-- `PromptXMLEscaping.swift` / `HanScript.swift`(BMP 汉字谓词)
-- `HostMemoryBudget.swift` / `KeyboardExtensionMemoryTelemetry.swift`(**Mac 排除**)
-- `ProviderDisplayName.swift` / `AppVersionDisplay.swift`
-
-**iOS-only 排除(`project.yml:570-586`)**:
-```
-DesignSystem/WaveformView.swift
-DesignSystem/RecordButton.swift
-DesignSystem/RecordButtonGesturePolicy.swift
-Services/KeyboardState.swift
-Services/KeyboardOpenSurfacePolicy.swift
-Models/MicVoiceAvailability+Keyboard.swift
-Models/TypingInputConfiguration.swift
-Models/TypingSurfaceMetrics.swift
-Typing/**
-Utilities/ProgressiveDictationTranscriptAccumulator.swift
-```
-
-### 1.6 iOS 测试覆盖(108 + 16 文件)
-
-**`OSGKeyboardTests/` (108 文件)** —— host + shared + hostsupport 端到端:
-- **ASR/云 ASR**:`CloudASRServiceTests` / `CloudASRHTTPClientTests` / `CloudASRStreamingHelpersTests` / `CloudASRStreamingEventParsingTests` / `CloudASRTests` / `ASRConversionTests` / `LocalASRModelCatalogTests` / `LocalASRDownloadSourceSorterTests` / `LocalASRBiasAdapterTests` / `AlibabaVocabularySyncTests` / `FrequentTermStoreTests` / `PreviewASRControllerStateTests`
-- **语音流水线**:`VoicePipelinePerformanceTests` / `FlowPhysicalAudioStressTests` / `FlowReliabilityTests` / `FlowASRPostProcessorTests` / `ChunkedUtterancePipelineTests` / `FlowBudgetAndMergeTests` / `FlowCaptureTailDrainTests` / `FlowUtteranceEndCoordinatorTests` / `FlowUtterancePCMStoreTests` / `UtteranceBatchFallbackPolicyTests` / `UtteranceStreamChunkerTests` / `UtteranceTranscriptGuardTests` / `UtteranceTranscriptStitcherTests` / `ProgressiveDictationTranscriptAccumulatorTests` / `FlowSessionBridgeTests` / `FlowSessionManagerAnalyticsTests` / `FlowSessionPolicyTests` / `FlowHandoffPolicyTests` / `FlowPiPRecoveryPolicyTests` / `FlowStartTransactionPolicyTests` / `FlowHomePiPStatusPolicyTests` / `FlowKeyboardPoliciesTests` / `KeyboardExtensionMemoryBudgetTests`
-- **Polish/LLM**:`PolishStylePackTests` / `PolishStyleLearningServiceTests` / `PolishPromptComposerQuestionTests` / `PolishOutputValidatorTests` / `IntelligentPolishTests` / `LLMClientTests` / `AIModeLLMClientTests`
-- **AI 特性**:`AIQuestionServiceTests` / `AIEventExtractionTests` / `AIAddressExtractionTests` / `AINoteExportTests` / `AIHintKeywordExtractorTests` / `AIHintPoolTests` / `AIUserSkillTests` / `AIUserSkillStoreTests` / `AIAgentSkillLayoutTests` / `AIHistoryAndUsageTests` / `AssistantFieldActionTests` / `AISessionStateTests` / `AIClipboardPromptTests` / `AnalyticsAIOperationTests` / `AppleNaturalLanguageCapabilityTests` / `SpeechHistoryRevisionTests` / `SpeechHistoryDayDeletionTests` / `SpeechHistoryCloudSyncTests` / `PublicContentRefreshServiceTests`
-- **剪贴板/工具**:`ClipboardSkillSemanticRankerTests` / `ClipboardSemanticAnalyzerTests` / `ClipboardHistoryStoreTests` / `ClipboardHistoryPolicyTests` / `AccountCenterViewModelTests` / `AccountSnapshotLoaderTests` / `AccountAPIClientTests` / `AccountSignInCoordinatorTests` / `AccountCreditPurchaseManagerTests` / `AccountSecurityPrimitiveTests` / `ReferralProfileTests` / `EditTransactionStoreTests` / `EditableInputReferenceTests` / `EditLastInputPromptTests`
-- **iCloud 同步**:`PersonalDictionaryCloudSyncTests` / `PersonalDictionaryMergeTests` / `SettingsCloudSyncTests` / `UsageStatisticsCloudSyncTests` / `KeyboardUsageRepositoryTests` / `KeyboardUsageModelTests` / `KeyboardUsageUploadCoordinatorTests` / `AnalyticsUploadCoordinatorTests` / `AnalyticsUploadSchedulingTests` / `AnalyticsRepositoryTests` / `AnalyticsModelTests` / `AnalyticsAttributionTests`
-- **本地 ASR + 内存**:`LocalASRModelCatalogTests` / `KeyboardExtensionMemoryBudgetTests`
-- **设置 + 工具**:`AppGroupConfigurationTests` / `AppGroupOnboardingStoreTests` / `ConfigurationStoreTests` / `HostAppURLRegistryTests` / `KeyboardTranslationConfigProtectionTests` / `KeychainTests` / `DeviceIntegrityTests` / `MicVoiceAvailabilityTests` / `OpenSourceLicenseCatalogTests` / `TipProductTests` / `EnglishTypingOnDeviceTests` / `TranscriptLanguageDetectorTests`
-- **账户/Managed gateway**:`AccountAPIClientTests` / `AccountSignInCoordinatorTests` / `AccountCenterViewModelTests` / `AccountCreditPurchaseManagerTests` / `AccountSecurityPrimitiveTests` / `AccountSnapshotLoaderTests`
-- **测试支持**:`AnalyticsTestSupport` / `KeyboardUsageTestSupport` / `FakeUbiquitousKeyValueStore`
-
-**`OSGKeyboardExtTests/` (16 文件)**:
-- `EnglishTypingTests` / `RimePersonalDictionaryExporterTests` / `KeyHitTestingTests` / `CandidatePanelExpandTests` / `KeyboardUsageTypingTests` / `LibrimeIntegrationTests` / `ManagedGatewayTests` / `RimeSchemaGeneratorTests` / `KeyboardSurfaceStateTests` / `AnalyticsExtensionPrivacyTests` / `PinyinNextKeyResolverTests` / `TypingTouchTrackerTests` / `FinalChunkRecoveryTests` / `KeyboardStateTests` / `EditHintSchedulerTests` / `ClipboardSuggestionLifecycleTests`
-
----
-
-## 2. macOS 端完整状态
-
-### 2.1 目标基本事实
-
-- Bundle ID: `com.osgkeyboard.mac`(Developer ID + 公证,**沙盒关闭**)
-- 最低系统:macOS 15.0。Swift 6 / strict concurrency
-- 装包名:`OSGKeyboard.app`(target 名 `OSGKeyboardMac` 改 `PRODUCT_NAME`)
-- 本地 ASR:**MLX Audio Qwen3-ASR 0.6B/1.7B 4-bit**(`ThirdParty/mlx-audio-swift/` SPM)
-- 复用 iOS AppIcon (`OSGKeyboard/AppIcon.icon`) + iOS 资产目录(除 `AppIcon.appiconset`)
-
-### 2.2 macOS App 目录结构(`OSGKeyboardMac/`,39 Swift 文件,无子目录)
-
-**入口与生命周期**:
-- `OSGKeyboardMacApp.swift:1` —— `@main struct OSGKeyboardMacApp: App`。**单一** `Window`(`.windowStyle(.hiddenTitleBar)`、`.defaultSize(width: 860, height: 600)`)+ `MacAppDelegate: NSApplicationDelegate + NSPopoverDelegate` 拥有 `NSStatusItem + NSPopover`(340×420 transient)+ 浮动 dictation overlay
-- **不**用 `MenuBarExtra`(`OSGKeyboardMacApp.swift:104-105` 注释:与主 Window 同存时图标会消失)
-- 订阅 `Notification.Name`(`.settingsDidSyncFromCloud` / `.personalDictionaryDidSyncFromCloud` / `.usageStatisticsDidSyncFromCloud` / `.speechHistoryDidSyncFromCloud`)iCloud pull 刷新
-- `onOpenURL` 接 `osgkeyboard://seed-demo`(DEBUG `DemoDataSeeder`)
-
-**侧栏/Shell**:
-- `MacRootView.swift` —— `NavigationSplitView(.balanced)`,侧栏:`OSGLogoWide` 品牌头 + 5 个 `MacSection` 行(`.dashboard/.history/.dictionary/.styles/.settings`)+ "Devices" 页脚;detail 切换 `DashboardView/MacHistoryView/MacDictionaryView/MacPolishStylesView/MacSettingsView`;底部 `MacStatusFooter`
-- `MacDictationViewModel.swift:12` —— `MacSection` enum
-- `MacTheme.swift:114` —— `MacSystemPalette` 双模式(light 暖白 + dark stepped systemGray6→4),通过 `\.themePalette` 注入,颜色方案变更重渲染
-- `MacAppearance.swift` —— `MacAppearancePreference`(system/light/dark)存 `mac.appearancePreference`,`applyToApp` 推到 `NSApp.appearance` 和每个 window
-
-**菜单栏 UI**(Status Item + Popover):
-- `OSGKeyboardMacApp.swift:140` —— variable-length `NSStatusItem` 模板 NSImage,target/action = `togglePopover(_:)`
-- Popover 内容:`NSHostingController(rootView: MacMenuBarPopover())` (`:214`)。未完成 onboarding → "open the main window" 提示;否则 `MacContentView`(品牌 + record 按钮 + 热键提示 + 状态文本 + 滚动转写 ≤120pt + 模式/翻译/连接状态 + footer)
-- Popover 打开:`prepareForPopoverPresentation()` snapshot 前台 App,**OSGKeyboard 成为 key 时不丢失粘贴目标**
-
-**录制 overlay**:
-- `MacDictationOverlayController.swift` —— 一个 borderless non-activating `NSPanel`(level `floatingWindow + 1`、`.canJoinAllSpaces + .fullScreenAuxiliary + .stationary`)
-- 状态机驱动 show/hide(`viewModel.$isRecording/$isPreparingToRecord/$isProcessing` CombineLatest3)
-- 位置 user-draggable(双击重置底部居中),持久化到 UserDefaults(`mac.overlay.hasCustomPosition / centerX / originY`)
-- `MacDictationOverlayView.swift` —— SwiftUI pill(500pt 内容宽)+ 状态点 + 一行转写 + 实时徽章 + `MiniWaveform` + stop 按钮
-- `MiniWaveform` 复用 `OSGKeyboardShared/DesignSystem`
-
-**主窗口 5 大页**:
-- `DashboardView.swift` —— `GeometryReader` 垂直布局:品牌头 + 7 天图(`UsageStatsCluster layout: .split`)+ `dictationStage` 卡片(`viewModel.homePreviewText` 累计预览)+ `BottomDictationBar`(翻译选择器 + record 按钮 + ready chip)
-- `MacHistoryView.swift` —— 按天分组 `SpeechHistoryStore`,每行 copy/delete context menu,"Clear" 带确认
-- `MacDictionaryView.swift` —— `PersonalDictionary` 按 `Entry.Category` 分组,按使用次数 + 术语排序;搜索框过滤;`+` 弹 `MacDictionaryEntryEditor` sheet
-- `MacPolishStylesView.swift` —— `PolishStylePack` 目录(内置 practical+fun + 用户),只读 prompt 详情 / 完整编辑器(`allowsAddedEmoji` 切换 + 2400 字符 prompt 限制);学习卡:语料进度 + "Generate learned style" → `PolishStyleLearningService.generateStyle`
-- `MacSettingsView.swift` —— `NavigationStack + ScrollView` 7 段:support developer (Tip) / general (appearance / interface language / recognition language / iCloud sync) / recognition method (cloud vs local) / cloud ASR provider / local ASR model catalog / polish provider / input (hotkey / auto-paste / accessibility 状态) / legal (Privacy Policy + Third-Party Licenses + Restart Onboarding + version)
-
-**Onboarding**:
-- `MacOnboardingView.swift:21` —— 6 步:`welcome → microphone → accessibility → engine → [.cloudAPI | .localModel]`,根据 `viewModel.config.engineMode` 分支
-- Mic 步调 `AVCaptureDevice.requestAccess(for: .audio)`;accessibility 步打开 `x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility` 并 1s 后重查 `AXIsProcessTrusted`
-- Cloud 步收 LLM provider + key + model;local 步显示默认 Qwen3-MLX 0.6B + 下载按钮 + 进度 + "skip" 提示
-- 状态存 `mac.hasCompletedMacOnboarding`
-- 注释 `MacOnboardingView.swift:7-8` —— "Sherpa identifiers and install records are retained only for migration compatibility":**近期从 Sherpa 换到 MLX**
-- Settings → About 有 "Restart Onboarding" 按钮
-
-**Legal / Support**:
-- `MacLegalSettingsViews.swift` —— `MacPrivacyPolicyView`(`WKWebView` 加载 `OSGKeyboard/Resources/PrivacyPolicy.html`,按 `uiLanguage` 切滚动锚点)+ `MacOpenSourceLicensesView`(`OpenSourceLicenseCatalog.entries(for: .macOS)` 列表)→ `MacOpenSourceLicenseDetailView`(许可证名 / upstream 链接 / 用途 / 文本)
-- `MacSupportDeveloperTipRows.swift` —— StoreKit 2 `TipPurchaseManager.shared` 消耗型打赏行
-
-**DEBUG 工具**:
-- `osgkeyboard://seed-demo` URL 触发 `DemoDataSeeder.seedRichPlaceholderData`
-- `MacAudioRecorder.hasLiveSnapshotSink`(DEBUG seam,见 `MacAudioRecorder.swift:143`)
-
-### 2.3 全局热键 + 辅助功能 + 粘贴注入
-
-**`MacHotkeyService.swift`**:
-- 用**两个** `NSEvent.add*MonitorForEvents(matching: .flagsChanged)`(global + local)
-- 区分左/右 Option(`NX_DEVICELALTKEYMASK = 0x20` / `NX_DEVICERALTKEYMASK = 0x40`);`MacHotkeyTrigger`(`rightOption / leftOption / eitherOption`)
-- 150ms 持有防抖(`scheduleBegin` → `Task.sleep(150ms)` → fire `onPressBegan`),松开取消未发起的 begin
-- `start()` 返回 Accessibility 状态(`globalFlagsMonitor == nil` ⇒ 大概率无权限),DEBUG 下 NSLog
-
-**`MacTextInsertionService.swift`** —— Accessibility 门控合成 ⌘V:
-- `AXIsProcessTrustedWithOptions([kAXTrustedCheckOptionPrompt: true])` 按需
-- `insert(_:autoPaste:targetApp:)`:清 `NSPasteboard.general` → 写转写 → snapshot 原始 pasteboard(**所有 representation,不仅是 `.string`**)→ `CGEvent + kVK_ANSI_V + cghidEventTap` 合成 ⌘V → 等 500ms 后**仅当 change count 仍匹配 post-transcript write**才恢复 snapshot(用户/clipboard-manager 写入期间不覆盖)
-- `FrontmostAppTracker`(`:177`)跟踪 `NSWorkspace.didActivateApplicationNotification`,**OSGKeyboard popover 激活时**保留原始 paste 目标
-- `activate(_:)` 重新激活目标 App,等最多 1s 成为 frontmost
-- `shouldRestorePasteboard` + `restoreItems` 是单测 seam
-
-### 2.4 音频捕获
-
-**`MacAudioRecorder.swift`**:
-- `MacAudioRecording` 协议
-- **`AVAudioEngine.inputNode`** + 4096 帧 tap
-- 输入格式 → 16kHz mono Float32 via `AVAudioConverter`
-- 10 分钟 @16kHz 硬上限 + 30s trim hysteresis(卡住的热键不会无限增长 buffer)
-- RMS 平滑(attack 0.5 / decay 0.15)
-- 权限:`AVCaptureDevice.requestAccess(for: .audio)`;拒/受限抛 `microphoneAccessDenied`
-- `makeSnapshotStream() -> AsyncStream`
-- Sink 安装按 generation 追踪,避免前一 sink 延迟终止处理器卸载新 sink
-- **无输入设备选择器**(`AVAudioEngine.inputNode` 用系统默认;iOS `AudioBufferSnapshot+AVFoundation.swift` 在 `project.yml:568` 排除)
-
-**`MacHallucinationFilter.swift`**:
-- 剥 `` 脚手架、`language XX` 前缀、元数据噪声行(`language/emotion/event` 等)
-- `silencePeakThreshold = 0.0005`(RMS 低于此跳喂 MLX stream)
-- `shouldDiscardHotwordDump`(音频能量低 + 转写主要是词典热词 → 丢假触发)
-
-### 2.5 MLX / 本地 ASR 流水线
-
-**关键文件**:
-- `MacLocalASRService.swift` —— Mac 端公共面:`selectedModelDefinition()` / `isModelInstalled(_:)` / `usesMLXLiveStreaming()` / `transcribe(samples:locale:bias:)`。MLX 后端模型走 `MacMLXStreamingASRProvider`;`.appleSpeech` 后端或未装模型时落 `MacSpeechLocalASR`。Legacy Sherpa 标识符(`sherpaQwen3/sherpaSenseVoice/sherpaParaformer`)只接受以便抛 `qwen3ModelMissing` —— **明确迁移期处理**
-- `MacMLXStreamingASRProvider.swift` —— `actor`,拥有单 `Qwen3ASRModel` 缓存(`cachedModelId / cachedModel / didWarmup`)。`loadModel` 从 `LocalASRModelInstallState.modelRootURL(definition)` 读并 `Qwen3ASRModel.fromModelDirectory(root)`。`warmupIfNeeded` 用 1s 静音预热 Metal kernel。`makeSession` 构建 `StreamingConfig`(0.5s decode interval、0.2s boundary、1s boundary boost、encoder window overlap 1s、max cached windows 8、`delayPreset: .realtime`、language hint 从 `MacQwen3LanguageHint.from(locale:)`、`context: bias?.promptBias`、`temperature: 0`、512 max tokens/pass、2/2 min-agreement passes、`finalizeCompletedWindows: true`)
-- `MacMLXStreamingSession.swift` —— 包装 `mlx-audio-swift` 的 `StreamingInferenceSession`。订阅 `AsyncStream`(`displayUpdate/ended/provisional/confirmed/stats`);`MacHallucinationFilter` 过滤 `display/ended`;`onDisplayUpdate` 转发 `display`;`feed(samples:)` 通过 `FlowCaptureDrainTracker.rms(of:)` 跳过低于 `silencePeakThreshold` 的数据;`stop()` 通过 `CheckedContinuation` 返回最终 `String`;`peakAudioRMS()` 暴露 hotword-dump 守卫用最大 RMS
-- `MacMLXLiveCapture.swift` —— Option 持有本地 ASR 编排:建 streaming session,`TaskGroup` 内 2 个并发任务:(1) 等首 `finishSignal`(`draining.withLock { $0 = true }` → `await FlowUtteranceEndCoordinator.awaitTailCapture(policy: .macMLX)`)——**显式 `break` 单信号**,永不结束的 stream 不会挂住 group;(2) 消费 `AudioBufferSnapshot` stream,100ms chunk(1600 samples @16kHz)喂 MLX,draining 时 `drainTracker.noteAudio`。然后喂余量,调 `session.stop()`,跑 `MacHallucinationFilter.shouldDiscardHotwordDump` 丢热词-only 转写
-- `MacSpeechLocalASR.swift` —— Apple Speech 兜底。16kHz PCM 写临时 WAV(`PCMSampleWavEncoder`),`SFSpeechURLRecognitionRequest` 强制 `requiresOnDeviceRecognition`(locale 无 on-device 模型时立即失败 → 清晰错误"去 系统设置 → 键盘 → 听写 下载")。Chinese locale 应用 `CustomLanguageModelManager.applyCustomLanguageModel(to:locale:bias:)`(与 iOS 共用)。2× 音频长硬超时 + 30s floor,`RecognitionSession` 锁保护单次 resume,识别回调和超时 task 正确竞速
-- `MacCloudASRChunkAdapter.swift` —— 薄 `ASRChunkTranscribing` 适配器,包装 `CloudASRClientFactory.make(store:)` 给 `MacDictationPipeline` 用
-
-**模型目录**(`OSGKeyboard/Resources/LocalASR/local-asr-catalog.json`,2.0.1 状态):
-- `schemaVersion: 1`
-- `defaultModelId: "qwen3-mlx-0.6b-4bit"`
-- `runtimes: []`(**空**——runtime 随 bundle 走,MLX 内置)
-- 2 个模型:
- 1. **`qwen3-mlx-0.6b-4bit`** —— Qwen3-ASR 0.6B,`backend: mlx`,`runtimePlatform: macos`,`sizeBytes: 730000000`(~0.7 GB),locale `zh-CN/en-US`,支持热词(`promptOnly`),`badgeKey: mac.localASR.badge.balanced`,路径 `models/qwen3-mlx-0.6b-4bit`。两个下载源(`hfmirror`/`huggingface`)都在 `mlx-community/Qwen3-ASR-0.6B-4bit`。文件:`config.json` / `generation_config.json` / `preprocessor_config.json` / `model.safetensors`(~708MB) / `model.safetensors.index.json` / `tokenizer_config.json` / `merges.txt` / `vocab.json`
- 2. **`qwen3-mlx-1.7b-4bit`** —— Qwen3-ASR 1.7B,同上形状,`sizeBytes: 1700000000`(~1.7GB),`badgeKey: mac.localASR.badge.quality`。源在 `mlx-community/Qwen3-ASR-1.7B-4bit`
-
-**`local-asr-catalog.json` 中零 iOS 模型**。iOS app 运行时**不**用本目录——iOS 用 iOS 26 `SpeechAnalyzer + DictationTranscriber`,代码路径不同(`OSGKeyboardHostSupport/Services/ASRService.swift`,Mac 排除)。
-
-**模型生命周期**:
-- `loadModel(_:)` (`MacMLXStreamingASRProvider` 第 69-81 行) 缓存 loaded `Qwen3ASRModel` per model id。切模型失效缓存并 re-warm。`warmupIfNeeded` 每个冷模型跑一次
-- 卸载(`MacLocalASRModelSettingsViewModel.deleteModel`)→ 共享 `LocalASRModelManager.deleteModel(_:catalog:)` 删磁盘权重;actor 缓存**不**自动清——下次不同 id 的 `loadModel` 自然替换
-
-**Mac 不支持 iOS 风格的"on-device + cloud hybrid"**——只有 engine-mode 开关(`local` → MLX 或 Apple Speech;`cloud` → 已配云 ASR)。两种模式共用同一 LLM 润色步。
-
-### 2.6 LLM 润色 + 供应商集成
-
-- `MacDictationPipeline.swift:254` —— 跑的是**同一份** `PolishingService(store: store).polishWithOutcome(...)`(iOS `OSGKeyboardShared/Services/PolishingService.swift`)
-- `PolishContext` 用 `appContext`(从目标 App bundle id 通过 `MacAppContextService` 抓)+ `dictionarySupplement`(来自 local-bias `polishFragment`),**和 iOS 同款**
-- `MacSettingsView.swift:111` (`polishProviderSection`) 用 `viewModel.polishSelectableProviders`(`LLMProvider.userSelectablePresets`——**完整集,与 iOS 相同**)。行:provider / API key / baseURL / model(`MacProviderModelRow` "拉模型列表" 动作) / thinking 切换 / `MacProviderToolsRow` "Test connection" / 翻译目标
-- `MacSettingsView.swift:173` (`asrProviderSection`) 用 `viewModel.asrSelectableProviders`(`LLMProvider.asrSelectablePresets`)。Volcengine 有自定义 auth-mode(API key vs App ID + Access Token)通过 `VolcengineASRFields.parse` / `updateMacVolcengine(...)`;其他供应商走通用 `baseURL/apiKey/model` 行 + "Test connection"
-- **Mac 没有 Mac 专用 polish/ASR 供应商集**——供应商面和 iOS 一致(DeepSeek/OpenAI/Anthropic 兼容 LLM;Volcengine/OpenAI Realtime/Bailian/Alibaba 云 ASR)
-- **MLX 供应商仅用于本地 ASR**——**无** Mac 端 MLX LLM 润色
-
-### 2.7 持久化
-
-| 类别 | 实现 | Key 前缀 / 位置 |
-|---|---|---|
-| UserDefaults | 标准 | `mac.*`(`MacHotkeyService:41` / `MacAppearance:56` / `MacDictationViewModel:103-105` / `MacDictationOverlayController:43-45`)—— 热键、auto-paste、appearance、onboarding、overlay 位置、MLX 模型选择、下载源、界面语言 |
-| App Group | 共享 `AppGroupStore` | 与 iOS 同:`PersonalDictionary` / `PolishStyleCatalog+active id` / `translationTarget` / `detected appContext` / `speechHistory` / `usageStatistics` / `engineMode` / `localeId` / LLM API key & baseURL / 云 ASR provider id/apiKey/model/baseURL / iCloud-sync-enabled |
-| Keychain | 共享 `Keychain` | `keychain-access-groups: [$(AppIdentifierPrefix)com.osgkeyboard.shared]`(entitlement)—— 存 LLM API key |
-| iCloud KVS | 5 路全部接好 | `MacICloudSyncBootstrap.swift` 接 `AppCloudSync(makeStore:historyDefaults:)`,暴露 `settingsSync/dictionarySync/polishStyleSync/appCloudSync`,**entitlement 用 `$(TeamIdentifierPrefix)com.osgkeyboard.ios` 和 iOS 同桶** |
-| 语音历史 | `SpeechHistoryStore.shared` + `SpeechHistoryStorage` | UserDefaults 后端 + iCloud `SpeechHistoryCloudSync` |
-| 模型权重 | `LocalASRModelInstallState.rootDirectory()` | 由共享 `LocalASRModelManager` 管 |
-| 临时文件 | `FileManager.default.temporaryDirectory` | Apple Speech WAV(`osg-mac-asr-.wav`,`defer` 删,`MacSpeechLocalASR:71`) |
-
-### 2.8 系统集成
-
-- ✅ 菜单栏 status item + popover
-- ✅ 全局 ⌥ 热键 + 合成 ⌘V
-- ✅ URL scheme `osgkeyboard`(仅 DEBUG `osgkeyboard://seed-demo`)
-- ❌ Login item / launchd(**无** `SMLoginItemSetEnabled`)
-- ❌ 通知(`UNUserNotificationCenter`)—— HUD 本身就是唯一直达面
-- ❌ Touch Bar
-- ❌ Services / Share extensions
-- ❌ Dock badge
-- ✅ `LSApplicationCategoryType: public.app-category.utilities`
-- ✅ `ENABLE_HARDENED_RUNTIME: YES`(Developer ID + 公证必需)
-- ❌ **沙盒关闭** `com.apple.security.app-sandbox: false`(`project.yml:628`)—— Accessibility + 全局事件 tap + ⌘V 注入都禁在沙盒里
-- 发行渠道:**Developer ID + 公证**,**不进** Mac App Store / TestFlight
-- `applicationShouldTerminateAfterLastWindowClosed → false`(菜单栏项保活)
-- `LSUIElement: false`(**不**设——启动时仍显 Dock 图标)
-
-### 2.9 macOS 独占(iOS 没有)的能力
-
-1. **`NSStatusItem + NSPopover` 菜单栏 UI** —— SwiftUI `MenuBarExtra` 故意不用,AppKit 为源
-2. **全局 ⌥ 持有热键**(`NSEvent.add*MonitorForEvents(matching: .flagsChanged)`)区分左/右/任意 Option
-3. **Accessibility 门控 ⌘V 注入**(`CGEvent + cghidEventTap + AXIsProcessTrusted`)
-4. **前台 App 跟踪**(`FrontmostAppTracker`)让 popover 激活 OSGKeyboard 时不丢粘贴目标
-5. **MLX Audio Qwen3 流式 ASR**(Qwen3-ASR 0.6B/1.7B 4-bit)通过 `mlx-audio-swift` SPM
-6. **系统原生 macOS light/dark 调色板**(暖白 light + stepped gray6→4 dark),从 AppKit semantic colors 解析,让 window chrome 跟随 SwiftUI 颜色方案
-7. **浮动 non-activating `NSPanel` HUD**(`floatingWindow + 1`),user-draggable 位置持久化
-8. **Bundle-ID → `AppContext` 映射**(`MacAppContextService.swift`)—— iOS 走 `AppContextDetector` 启发式(键盘读不到前台 bundle id)
-9. **手动 MLX 模型下载/暂停/恢复**,per-file 字节进度
-10. **"Open Storage"** reveal-in-Finder MLX 模型根
-11. **Volcengine 云 ASR auth-mode 切换**(API key vs App ID + Access Token)
-12. **同 `URL scheme: osgkeyboard`** 加 `osgkeyboard://seed-demo` DEBUG seed
-13. **Apple Speech on-device 兜底**(仅当未装 MLX 模型或 backend == `.appleSpeech`)
-14. **`NSApplicationDelegate`-driven AppKit 生命周期**:`applicationShouldTerminateAfterLastWindowClosed → false`、status-item 创建、overlay controller 启动、热键接续
-15. **`MacMLXStreamingASRProvider` actor + 模型缓存 + warmup** 保单 `Qwen3ASRModel` 驻留
-16. **两列 `MacInlinePicker`(供应商行)** vs iOS `.menu` `Picker`
-17. **`MacProviderSettingRow` 显式 200pt label 列** 防长凭证输入挤
-
-### 2.10 macOS 测试覆盖(`OSGKeyboardMacTests/`,3 文件,挂在 Mac App bundle 内)
-
-`project.yml:513-533` 覆盖 `@testable import OSGKeyboard`(host target 名 `OSGKeyboardMac` 但装包为 `OSGKeyboard.app`,所以 override `TEST_HOST`)。
-
-1. **`MacDictationViewModelTests.swift`** —— `testCancellingButtonPreparationKeepsGateClosedUntilStartUnwinds`:替换 recorder 为 `SuspendedMacAudioRecorder`(`start()` 停 `CheckedContinuation`),验证第二次 `toggleRecording()`(取消)不重开 `isPreparingToRecord` 门
-2. **`MacAudioRecorderSnapshotStreamTests.swift`** —— `testReplacingSnapshotStreamDoesNotDeadlock` + `testReplacingSnapshotStreamKeepsTheNewSinkAttached`:回归守卫,防 Option 松开时 lock 重入冻结(`AsyncStream.Continuation.finish()` 在调用线程同步跑 `onTermination`,handler 重新拿 installer 持有的同 `NSLock` 死锁主线程)。第一个测试在 global queue 装 + 2s semaphore 超时,让回归失败而非挂住套件;第二个测读 `MacAudioRecorder.hasLiveSnapshotSink`(DEBUG seam)确认新 stream 顶替前辈
-3. **`MacTextInsertionServiceTests.swift`** —— `testRestoreRequiresTranscriptToStillOwnPasteboard`(pasteboard change-count 守卫)/ `testRestoringOriginallyEmptyPasteboardClearsTranscript` / `testCapturedBundleIdentifierDrivesPolishContext`(Xcode/WeChat/OSGKeyboard 自身经 `MacAppContextService.detectContext(bundleIdentifier:)`)
-
----
-
-## 3. macOS 落后 iOS 的功能(按缺口大小排序)
-
-### 3.1 完全缺失(共享代码已编进 Mac 但零调用)
-
-| 缺失功能 | iOS 路径 | 共享代码状态 | 影响 |
-|---|---|---|---|
-| **剪贴板历史 + AI 技能** | `OSGKeyboard/Views/ClipboardSettingsView`、`OSGKeyboardExt/Views/ClipboardKeyboardViews` | `ClipboardHistoryStore` / `ClipboardSemanticAnalyzer` / `ClipboardSkillSemanticRanker` / `AIClipboardSkill` / `AIClipboardSkillLayoutStore` / `AIClipboardPrompt` / `ClipboardHistoryPolicy` **全部编进 Mac 二进制,无任何 Mac 文件引用** | Mac 用户能说话能润色,但**没法**让"复制即智能" |
-| **AI 提示轮播 / 上下文技能** | `OSGKeyboard/Services/AIHintRefreshService`、`OSGKeyboardExt/Views/AIKeyboardView` 内的 hint carousel | `AIHintStore` / `AIHintPool` / `AIHintKeywordExtractor` / `AIHintKeywordCompressor` / `AIHintLocalCatalog` / `AIHintModels` 全部已编,**无 Mac 调用** | Mac 没有 idle 屏的"想一句"灵感卡 |
-| **OSG 账户(Sign in with Apple + 积分 + 推荐)** | `OSGKeyboard/Views/Account/AccountCenterView`、`AccountPurchaseHistoryView` | `OSGKeyboardHostSupport/Features/Account/` 全部编进 Mac(`AccountAPIClient` / `SignInWithApple` / `DeviceIntegrity` / `HostPrivateAccountKeychain` / `AccountModels` / `OOBEGrantProvisioningCoordinator`),**无 Mac UI 也无调用**,且 Mac entitlement 无 `com.apple.developer.applesignin` | Mac 只能用 tip 打赏;OSG 积分体系是 iOS 独占 |
-| **App Attest** | `OSGKeyboardHostSupport/Features/Account/DeviceIntegrity.swift`(`SystemAppAttestProvider`)| 已编,但 Mac 无对应 entitlement(`com.apple.developer.devicecheck.appattest-environment`)也无调用 | Mac 不在 attestation 流程里 |
-| **一方分析(Analytics)** | `OSGKeyboard/Services/AnalyticsHostService`、`OSGKeyboardExt/Services/AnalyticsExtensionService` | `OSGKeyboardShared/Features/Analytics/` 12+ 文件全部编进 Mac,**零 Mac 调用** | Mac 用户行为完全无遥测;产品迭代失去数据源 |
-| **助手指令(Shortcuts)** | `OSGKeyboard/Services/AIAgentShortcutInstaller`、`AIAgentShortcutRunner`、3 个 bundled `.shortcut`(`OSGExtractTodos/OSGExtractEvents/OSGSaveToNotes`) | 共享 `AIAgentSkill*` / `AIShortcutShareLink` / `AIGenericSkillExport` / `AINoteExport` / `AIMapNavigation` / `AIPhoneNumberActions` 已编,无 Mac 调用;bundled `.shortcut` **Mac 不装**(`project.yml:97-105` 仅 iOS 装) | Mac 没法"说一句把当前文本加到 Notes / 提取 Todo" |
-| **CLM 用户管理 UI** | iOS 后台用 `CustomLanguageModelManager` | Mac **用**了 CLM(`OSGKeyboardMacApp.swift:117` 调 `prepareInBackgroundIfNeeded`;`MacSpeechLocalASR:67-98` 调 `applyCustomLanguageModel`),**但**无 `MacCLMSettingsView` | Mac 用户不知道 CLM 在跑;没法改 phrase 库 |
-| **最后输入编辑(Edit hint / Last-input edit)** | `OSGKeyboardExt/Views/LastInputEditView`、`EditHintScheduler`、`LastInputEditCoordinator` | 已编,无 Mac 调用 | Mac 没法长按"改刚才那句" |
-| **Flow 跨进程会话** | `OSGKeyboard/Services/FlowSessionManager`、`FlowPictureInPictureController`、`FlowTerminationCoordinator`、`FlowAnalyticsOperationRegistry`、`FlowASRPostProcessor`、`FlowDiagnostics`、`OSGKeyboardExt/Services/KeyboardFlowCoordinator`、`OSGKeyboardShared/Services/FlowSessionBridge*` | 已编,Mac 仅借用 `FlowSessionKeys.cloudASRWaitTimeout` 一个常量(`MacDictationViewModel.swift:498`) | **架构性差异**——Mac 单进程、单用户、单次录制;不需要也不该硬塞 |
-| **自定义键盘(Rime/拼音/英文)** | 整个 `OSGKeyboardExt/Typing/` + `OSGKeyboardShared/Typing/**` + `LibrimeEngine` + `RimeResourceInstaller` + `EnglishSuggestionEngine` + `Pinyin*` + `Rime*` | **Mac target 完全排除** `Typing/**`(`project.yml:585`) | **架构性差异**——Mac 不是键盘扩展;librime 是 iOS C/Objective-C++ 框架,Mac 结构性不兼容 |
-| **后台任务** | iOS `UIBackgroundModes: audio` + `BGTaskSchedulerPermittedIdentifiers: [com.osgkeyboard.ios.analytics-sync]` | Mac Info.plist **无** `BG*` keys | Mac app 不能后台运行;iOS 的 analytics 同步、Flow PiP 都没 Mac 对应 |
-| **Settings 子页面** | `TypingInputSettingsView` / `AIAgentSkillsView` / `Account/*` / `HelpFeedbackView` / `ReleaseNotesSheet` / `AppGroupErrorView` / `NewContactSheet` / `KeyboardPreviewSheet` / 各种 DEBUG demo | 无 Mac 对应 | 见上表 |
-| **Universal Links 启动 handoff** | iOS `applinks:osglab.com` + `AppURLHandler` 保留 `sourceApplication` | Mac entitlement 共享同 `com.apple.developer.associated-domains` 但**无** Mac-side 解析 | Mac 不能从网页/邮件 deep-link 进 Onboarding 或 Skill |
-
-### 3.2 部分缺失
-
-| 缺失 | iOS | macOS | 差异点 |
-|---|---|---|---|
-| **Onboarding 深度** | 7 步 + OOBE 4-feature 实践(用 `OOBEClientInfrastructure` 拿服务器体验金) | 6 步(welcome / mic / accessibility / engine / cloud\|local) | 无 Sign in with Apple、无 OOBE 实践 |
-| **设置覆盖面** | 7 个子面板 + Account + AI Agent + Clipboard + Typing Input | 5 个段落(support / general / recognition / ASR / local ASR / polish / input / legal) | iOS 的 AI Agent / Clipboard / Typing 在 Mac 没对应;Mac 的 input 段含热键 + Accessibility 状态(iOS 没有) |
-| **AI Hint / Shortcut 链接** | 提示页 + 3 bundled `.shortcut` | 仅 0(Mac 装包不含 `.shortcut`) | 整套 AI mode 体验 Mac 无 |
-| **Provider 切换粒度** | iOS 通过 iOS `EnginePickerSection` + `ProviderPickerSection` 在 Settings 内 + 主页快捷 | Mac 整段 `recognitionSection` + `asrProviderSection` + `polishProviderSection` 拆开 | iOS 较紧,Mac 较松 |
-
-### 3.3 Mac 自身落后 iOS 之处("应该补"清单)
-
-按"代码复用难度 + 用户价值"排序:
-
-| 优先级 | 功能 | 现状 | 工作量 | 建议 |
-|---|---|---|---|---|
-| **P0** | **剪贴板历史 + AI 技能面板(Mac UI)** | 共享代码全到位,零 Mac UI | **小**——`MacClipboardHistoryView` + `MacAIClipboardSkillView`(仿 `MacHistoryView`)+ 改 `MacSettingsView` 加一个 Clipboard 段 | 立刻做。代码复用 100%,价值高 |
-| **P0** | **CLM 设置页(Mac UI)** | 后台在跑(`OSGKeyboardMacApp:117`),无 UI | **小**——`MacCLMSettingsView`,仿 `MacLocalASRModelSettingsView` 模板 | 立刻做。让用户能编辑 phrase 库 |
-| **P1** | **OSG 账户 + Sign in with Apple + 积分购买** | 共享代码编进 Mac,零 UI;Mac entitlement 缺 `com.apple.developer.applesignin` + 无 `SKIncludeConsumableInAppPurchaseHistory` | **中**——加 entitlement + 改 `MacICloudSyncBootstrap` 已涵盖大部分,加 `MacAccountCenterView`(仿 `AccountCenterView` 但精简)+ 改 Settings | 一周内可交付 |
-| **P1** | **OSG credits 消耗型 IAP(500tks/1500tks/3000tks)** | 共享 `TipPurchaseManager` 已装;iOS 走 `AccountCreditPurchaseManager` | **中**——`MacAccountCenterView` 出来后一气呵成 | 同上 |
-| **P2** | **一方分析(Mac)** | 共享 `AnalyticsClient` 等全编,零调用 | **中-大**——`MacAnalyticsHostService` + 隐私 / opt-in + 后台任务(Mac 上走 `NSProcessInfo.thermalState` 节流 + 用户同意时 `NSTask` 跑)| 半年窗口内 |
-| **P2** | **AI 提示轮播 / 上下文技能(Mac)** | 共享代码全到位 | **中**——加 `MacAIHintPool` 屏 + `MacAIQuestionService` 入口 + Shortcuts bundle 装载 | 与 P0 剪贴板可联动 |
-| **P3** | **助手指令集成** | 共享代码 + iOS bundled `.shortcut` | **中**——Mac 装 `.shortcut` + `AIAgentShortcutInstaller` 调起(`shortcuts://` URL 仍可用)| 与 P2 AI 提示联动 |
-| **P3** | **最后输入编辑** | 共享代码全到位 | **大**——需要 Mac 端保留"刚才插入的引用"(`EditableInputReference`),但 Mac 走 Accessibility ⌘V 注入,**没有 `EditableInputReference` 的来源**——需要新协议 | 架构问题,先做 PoC |
-| **P3** | **后台同步(Analytics 任务)** | 无 | **中**——`NSTask`/`SMAppService` LaunchAgent 拉 analytics;与 P2 联动 | |
-
-### 3.4 Mac 形态上"不应该补"的功能
-
-| 功能 | 原因 |
-|---|---|
-| **Flow 跨进程会话** | Mac 单进程、单用户、单次录制——Flow 设计为键盘扩展 ↔ 宿主 App 的两进程 mailbox。强行移植会引入不必要的 IPC 开销。**Mac 用 `MacDictationPipeline` + `MacDictationViewModel` + `MacDictationOverlayController` 三件套已足够** |
-| **PiP keep-alive** | macOS 无系统 PiP。`MacDictationOverlayController` 的 NSPanel 已占位 |
-| **Rime / 拼音 / 英文 autocorrect** | Mac 不是键盘扩展,没输入面。`LibrimeEngine` 是 iOS C/Objective-C++ xcframework,**Mac 结构性不兼容**。即便 Mac 装键盘扩展(`Designed for iPad` 那种),librime 也需要重编译 |
-| **`UIBackgroundModes: audio`** | Mac 没用——按住说话时 NSPanel 是 non-activating,不需要 audio session 后台保持 |
-| **`UIOpenURLContext.options.sourceApplication`** | iOS 26 only API;Mac 走 `NSWorkspace.didActivateApplicationNotification` 已解决前台 App 跟踪 |
-| **`AVAudioApplication.requestRecordPermission`** | iOS 17+ only;Mac 走 `AVCaptureDevice.requestAccess(for: .audio)` |
-| **App Group 跨进程 mailbox** | Mac 单进程不需要。但 `AppGroup` + iCloud KVS 是用户态多设备同步用的,**保留** |
-| **iOS 风格的 idle 屏** | Mac 主窗口即 dashboard,不需要 idle 屏的"AI hint 轮播";但 `MacAIHintPool` 屏可以放在 Dashboard 顶部,与 P2 联动 |
-| **`LSApplicationQueriesSchemes`** | iOS only,Mac 无 `canOpenURL` 限制 |
-| **`SKIncludeConsumableInAppPurchaseHistory` / Mac storekit 文件** | Mac App Store 与 iOS App Store 独立 SKU;目前 Mac 装包是 Developer ID 渠道,**不进 Mac App Store**,所以 storekit 不需要 |
-
----
-
-## 4. 适合在 Mac 上做开发和移植的功能
-
-### 4.1 应该现在做的(P0)
-
-#### ① 剪贴板历史 + AI 技能面板
-
-**为什么 Mac 适合**:共享代码 100% 到位(`ClipboardHistoryStore` / `ClipboardSemanticAnalyzer` / `ClipboardSkillSemanticRanker` / `AIClipboardSkill` / `AIClipboardPrompt`),iOS 8 个 UI 调试稳定,只需套 Mac 风格。
-
-**具体工作**:
-- 新增 `OSGKeyboardMac/Views/MacClipboardHistoryView.swift`(仿 `MacHistoryView`)
-- 新增 `OSGKeyboardMac/Views/MacAIClipboardSkillView.swift`(仿 `AIAgentSkillsView`,精简为 Read/Edit/Disable)
-- `MacSettingsView.swift` 加 `clipboardSection`(含 toggle 启用历史 / toggle 启用技能候选条 / "Manage Skills" 链接 / "Open History" 链接)
-- `MacDictationViewModel` 订阅 `ClipboardHistoryStore.entries`(`@Published var clipboardEntries`)
-- 复用 `ClipboardCaptureCoordinator` 不可(ext-only)——需要 Mac 端 `MacClipboardMonitor`(`NSPasteboard.general.changeCount` 轮询 + sanitize)
-
-**测试**:`MacClipboardMonitorTests`(用 in-memory pasteboard 模拟)/ `MacClipboardHistoryViewModelTests`
-
-**工作量**:1-2 周
-
-#### ② CLM 设置页
-
-**为什么 Mac 适合**:Mac 后台已经在跑 CLM(`OSGKeyboardMacApp:117`),只是用户管不到。
-
-**具体工作**:
-- 新增 `OSGKeyboardMac/Views/MacCLMSettingsView.swift`(仿 `MacLocalASRModelSettingsView`)
-- 复用 `CustomLanguageModelManager.shared.state`(`@Published idle/preparing/ready/failed`)
-- 复用 `OSGKeyboard/Resources/HostCLM/v1/OSGKeyboardCLM.bin`(已在 Mac 装包)
-- 复用 `PersonalDictionary` 共享存储(用户输入的 phrase 库 + iCloud 同步)
-- 提供 phrase 列表 / 编辑 / 触发"重编译"按钮
-
-**测试**:`MacCLMSettingsViewModelTests`(验 `state` 状态机)
-
-**工作量**:1 周
-
-### 4.2 应该中期做的(P1)
-
-#### ③ OSG 账户 + Sign in with Apple + 积分购买
-
-**为什么 Mac 适合**:共享代码编进 Mac 都没报错,缺的是 entitlement + UI。
-
-**先决条件**:
-- `OSGKeyboardMac.entitlements` 加 `com.apple.developer.applesignin: [Default]`
-- `OSGKeyboardMac/Info.plist` 加 `SKIncludeConsumableInAppPurchaseHistory: true`
-- (选做)把 `OSGKeyboard.storekit` 内容 mirror 到 `OSGKeyboardMac.storekit` 或共享
-
-**具体工作**:
-- 加 `MacAccountCenterView`(仿 iOS `AccountCenterView` 但精简)
-- 复用 `LiveAccountServices` / `AccountSessionCoordinator`(`@MainActor` state machine)
-- 复用 `AccountCreditPurchaseManager`(StoreKit 2 product list → 服务器校验)
-- Settings → 加 "Account" 段,含 sign-in / 积分余额 / 购买历史 / 登出 / 删号
-
-**测试**:`MacAccountSessionCoordinatorTests`(in-memory `AccountAPIClient` fake)
-
-**工作量**:2-3 周
-
-#### ④ Mac App Attest / DeviceCheck
-
-**为什么 Mac 适合**:iOS attestation 后端已经在用,Mac 端可以通过 `DCAppAttestService`(macOS 13+,需要 Mac Catalyst 但当前 `SUPPORTS_MACCATALYST: NO`)或者改用 DeviceCheck `DCDevice.generateToken`(macOS 13+ available without Catalyst)。
-
-**先决条件**:
-- `OSGKeyboardMac.entitlements` 加 `com.apple.developer.devicecheck.appattest-environment: $(APP_ATTEST_ENVIRONMENT)`
-- 若走 App Attest,需要 `SUPPORTS_MACCATALYST: YES`(改大改)或改 DeviceCheck-only
-
-**具体工作**:
-- `MacDeviceIntegrityCoordinator`(仿 iOS,但用 `DCDevice`)
-- `AccountAPIClient` 复用,调 `/v1/integrity/attest`
-
-**工作量**:1-2 周(仅 Mac);+ Catalyst 决策时间
-
-### 4.3 应该长期做的(P2)
-
-#### ⑤ 一方分析(Mac)
-
-**为什么 Mac 适合**:Mac 用户行为和 iOS 同样有价值(知道用户用不用 MLX 模式、热键触发频率、词条覆盖、润色样式流行度)。
-
-**先决条件**:
-- 共享 `AnalyticsClient` / `AnalyticsRepository` 已经完备
-- Mac 上加 `MacAnalyticsHostService`(仿 iOS):监听 `viewModel.$isRecording/$isPreparingToRecord/$isProcessing` 推送事件
-- 隐私 / opt-in:在 onboarding 末加一步
-
-**挑战**:
-- Mac 后台无 `BGTaskScheduler`——改用 `NSProcessInfo.thermalState` + `ProcessInfo.isLowPowerModeEnabled` 节流,**或** `SMAppService` 拉个 LaunchAgent
-- `AnalyticsUploadCoordinator` 的移动策略(threshold 20 / 60s flush)要 Mac 化
-
-**工作量**:3-4 周
-
-#### ⑥ AI 提示轮播 / 上下文技能
-
-**为什么 Mac 适合**:Mac dashboard 缺内容——除了使用统计,闲置时可推"想一句"灵感卡。
-
-**具体工作**:
-- `MacAIHintPool` 嵌在 `DashboardView` 顶部(仿 iOS `AIKeyboardView` 的 carousel 区域)
-- 复用 `AIHintStore` / `AIHintPool` / `AIHintKeywordExtractor` / `AIHintLocalCatalog`
-- 上下文技能入口放 `MacContentView`(popover 内 record 按钮旁的下拉)
-
-**工作量**:2-3 周
-
-#### ⑦ 助手指令集成
-
-**为什么 Mac 适合**:Mac 上有 Shortcuts.app(更成熟),bundled `.shortcut` 装入 Mac App bundle 即可。
-
-**具体工作**:
-- 装 `OSGKeyboard/Resources/Shortcuts/*.shortcut` 进 Mac bundle
-- 复用 `AIAgentShortcutInstaller` / `AIAgentShortcutRun`
-- popover / dashboard 加技能快捷入口
-
-**工作量**:1-2 周
-
-### 4.4 不应该在 Mac 做的(但代码可以清理)
-
-| 共享代码 | 为什么不该在 Mac 跑 | 处置 |
-|---|---|---|
-| `OSGKeyboardHostSupport/Features/Account/*`(Mac 已编) | App Attest 路径 iOS 专属;Mac 没 entitlement | 等 ④ 决策后从 Mac target 排除或接 UI |
-| `OSGKeyboardShared/Features/Analytics/*`(Mac 已编) | 无 Mac 端调用 | 等 ⑤ 决策后从 Mac target 排除或接服务 |
-| `OSGKeyboardShared/Features/ManagedGateway/*`(Mac 已编) | 是账户 grant 体系,与 ③ 绑定 | 等 ③ 决策后处理 |
-| `OSGKeyboardShared/Services/AIClipboard*`(Mac 已编) | 与 ① 绑定 | ① 实现后从"无调用"变"有调用" |
-| `OSGKeyboardShared/Services/AIHint*`(Mac 已编) | 与 ⑥ 绑定 | 同上 |
-| `OSGKeyboardShared/Services/AIUserSkill*` / `AIAgentSkill*`(Mac 已编) | 与 ③⑦ 绑定 | 同上 |
-| `OSGKeyboardShared/Services/Flow*`(Mac 已编) | Mac 架构不兼容 | **立刻**从 Mac target 排除(白付编译器开销) |
-| `OSGKeyboardShared/Services/KeyboardState.swift`(Mac 已排除)| 已经是 Mac 排除 ✓ | 无 |
-| `OSGKeyboardShared/Services/EditTransactionStore.swift` 等 | 与"最后输入编辑"绑定 | 见 P3 |
-
-### 4.5 立即可清的"白编译"清单
-
-```
-OSGKeyboardShared/Services/FlowSessionBridge.swift + 所有 +* 文件
-OSGKeyboardShared/Services/FlowSessionKeys.swift
-OSGKeyboardShared/Services/FlowSessionDarwin.swift
-OSGKeyboardShared/Services/FlowSessionPolicy.swift
-OSGKeyboardShared/Services/FlowStartTransactionPolicy.swift
-OSGKeyboardShared/Services/FlowKeyboardPolicies.swift
-OSGKeyboardShared/Services/FlowHandoffPolicy.swift
-OSGKeyboardShared/Services/UtteranceStreamChunker.swift
-OSGKeyboardShared/Services/UtteranceBatchFallbackPolicy.swift
-OSGKeyboardShared/Services/UtteranceTranscriptGuard.swift
-OSGKeyboardShared/Services/UtteranceTranscriptStitcher.swift
-OSGKeyboardShared/Services/TranscriptOverlapUtilities.swift
-OSGKeyboardShared/Services/ProgressiveDictationTranscriptAccumulator.swift # 已排除
-OSGKeyboardShared/Services/EditTransactionStore.swift
-OSGKeyboardShared/Services/EditUsageMetricsStore.swift
-OSGKeyboardShared/Services/EditLastInputPromptComposer.swift
-OSGKeyboardShared/Services/EditOutputValidator.swift
-OSGKeyboardShared/Models/Flow*.swift (全部)
-OSGKeyboardShared/Models/EditableInputReference.swift
-OSGKeyboardShared/Models/EditSessionState.swift
-OSGKeyboardShared/Models/FlowUtterance*.swift
-OSGKeyboardShared/Models/FlowInactivityDuration.swift
-OSGKeyboardShared/Utilities/FlowCaptureTailDrain.swift
-OSGKeyboardShared/Utilities/FlowUtteranceEndCoordinator.swift
-OSGKeyboardShared/Utilities/FlowUtterancePCMStore.swift
-OSGKeyboardShared/Utilities/FlowTrace.swift
-OSGKeyboardShared/Utilities/FlowPipelineDiagnostics.swift
-OSGKeyboardShared/Utilities/HostMemoryBudget.swift
-OSGKeyboardShared/Utilities/KeyboardExtensionMemoryTelemetry.swift # 已排除
-OSGKeyboardShared/Features/Analytics/ # 全部 12+ 文件
-OSGKeyboardShared/Features/ManagedGateway/ # 全部 8+ 文件
-OSGKeyboardHostSupport/Features/Account/ # 全部
-OSGKeyboardHostSupport/Services/CloudASR/AlibabaVocabularySync.swift # iOS-only API
-OSGKeyboardHostSupport/Services/ASRChunkTranscribing.swift # 需检查
-```
-
-(**建议**):在 `project.yml:558-602` 给 Mac target 的 source list 加 `excludes:`,避免编译这些文件后被链接器裁掉造成的 dead-code 体积。
-
----
-
-## 5. iOS 端需要"修一下"或注意的地方
-
-报告主体是 macOS 差距,但 iOS 端顺手列出 5 个明显可改进点(不修不影响功能,但提升质量):
-
-1. **`OSGKeyboardHostSupport/Features/Account/*` 整块**虽然不通过 Mac UI 暴露,但通过 source include 编进 Mac 二进制——长期应从 Mac target 排除或加 `MacAccountServices` 实际使用
-2. **`OSGKeyboardShared/Features/Analytics/*`** 同上——Mac 编译进二进制但无任何调用
-3. **`OSGKeyboardShared/Features/ManagedGateway/*`** 同上
-4. **`project.yml:570-602`** 的 Mac target source list 很长且依赖手写排除,**建议**拆出 `OSGKeyboardMacExcludes.yml` 维护
-5. **`OSGKeyboardShared/Typing/EnglishSystemLexicon.swift:11`** 是**唯一**有 `import UIKit` 的 Shared 文件——是 iOS ext 独享的 QuickType 数据源。考虑改名/移动到 `OSGKeyboardExt/` 减面
-6. **iOS `FlowSessionManager` ~3500 LOC** 是单一 god object;建议把 `FlowPictureInPictureController` / `FlowTerminationCoordinator` / `FlowAnalyticsOperationRegistry` 三个生命周期拥有者抽离
-7. **iOS `appDelegate` 反弹 `.onOpenURL`** (`AppURLHandler.swift`) 注释里写 `UIOpenURLContext.options.sourceApplication` iOS 26 only——这个 API 在 iOS 26 还在吗?值得 verify(影响 deep-link host-return 白名单)
-8. **`KeyboardViewController` `didReceiveMemoryWarning` (`:390-404`)** 强制回 voice surface——typing 引擎是最大内存消费者,但每次收到警告就退出 typing 体验略激进;考虑加 cooldown
-
----
-
-## 6. 实施建议
-
-### 6.1 短期(未来 1-2 周)
-
-- **任务 A:从 Mac target 排除 `Flow*` / `Edit*` / `Analytics*` / `ManagedGateway*` / `Account*`(HostSupport)所有共享代码**
- - 修改 `project.yml:570-602`,加 `excludes:` 列表
- - 验证 Mac target 仍能 build & test pass
- - 节省 Mac 二进制 ~2-4 MB + 减少编译时间 ~10-15s
-- **任务 B:Mac 剪贴板历史 + AI 技能面板**(P0 ①)
-- **任务 C:Mac CLM 设置页**(P0 ②)
-
-### 6.2 中期(1-2 月)
-
-- **任务 D:OSG 账户 + Sign in with Apple + 积分**(P1 ③)
-- **任务 E:App Attest 决策 + 实施**(P1 ④)
-
-### 6.3 长期(3-6 月)
-
-- **任务 F:Mac 一方分析**(P2 ⑤)
-- **任务 G:AI 提示轮播 / 上下文技能**(P2 ⑥)
-- **任务 H:助手指令集成**(P2 ⑦)
-
-### 6.4 决策项
-
-| 决策 | 选项 | 影响 |
-|---|---|---|
-| **App Attest 路径** | (a) Catalyst + `DCAppAttestService`(macOS 13+)
(b) 仅 `DCDevice.generateToken`(无 attestation)
(c) 不做 | (a) 需开 `SUPPORTS_MACCATALYST: YES` 改大改;
(b) 安全性较弱但改动小;
(c) Mac 不在 attestation 内 |
-| **Mac 走 Mac App Store 还是 Developer ID** | (a) 维持 Developer ID(现状)
(b) 走 Mac App Store | (a) 维持沙盒关闭,Accessibility 自由;
(b) 需重写热键 + 注入方案(沙盒内 Accessibility 拿不到) |
-| **是否上 Rime 替代方案** | (a) 不做(Mac 不是键盘)
(b) 内部输入法,绕过 librime | (b) 工作量极大,且与产品形态不符 |
-| **Mac 是否支持 iOS 风格的"on-device + cloud hybrid"** | (a) 仅 engine-mode toggle(现状)
(b) 支持每句 hybrid(local first,失败 fallback cloud) | (b) 需要重新设计 MLX streaming 端 |
-
----
-
-## 7. 关键引用
-
-| 主题 | 文件 | 行号 |
-|---|---|---|
-| iOS 宿主入口 | `OSGKeyboard/OSGKeyboardApp.swift` | 1 |
-| iOS Flow 单一拥有者 | `OSGKeyboard/Services/FlowSessionManager.swift` | 1 (~3500 LOC) |
-| iOS 键盘入口 | `OSGKeyboardExt/KeyboardViewController.swift` | 33-38 |
-| iOS 键盘生命周期 | `OSGKeyboardExt/KeyboardViewController.swift` | 148-655 |
-| iOS 共享 ASR 协议 | `OSGKeyboardShared/Services/PolishingService.swift` | 34-105 |
-| iOS HostSupport ASR 工厂 | `OSGKeyboardHostSupport/Services/ASRService.swift` | 1 |
-| iOS HostSupport 云 ASR 工厂 | `OSGKeyboardHostSupport/Services/CloudASR/CloudASRClientFactory` | 44 |
-| Mac 入口 | `OSGKeyboardMac/OSGKeyboardMacApp.swift` | 1 |
-| Mac MLX 流式 provider | `OSGKeyboardMac/MacMLXStreamingASRProvider.swift` | 1 |
-| Mac MLX 流式 session | `OSGKeyboardMac/MacMLXStreamingSession.swift` | 1 |
-| Mac MLX 实时捕获编排 | `OSGKeyboardMac/MacMLXLiveCapture.swift` | 1 |
-| Mac 热键 | `OSGKeyboardMac/MacHotkeyService.swift` | 1 |
-| Mac 文本注入 | `OSGKeyboardMac/MacTextInsertionService.swift` | 1 |
-| Mac 音频 | `OSGKeyboardMac/MacAudioRecorder.swift` | 1 |
-| Mac 字幕过滤器 | `OSGKeyboardMac/MacHallucinationFilter.swift` | 1 |
-| Mac 字典流水线 | `OSGKeyboardMac/MacDictationPipeline.swift` | 254 |
-| Mac 模型目录 | `OSGKeyboard/Resources/LocalASR/local-asr-catalog.json` | 1-111 |
-| Mac target 定义 | `project.yml` | 543-674 |
-| Mac target 共享 source 排除 | `project.yml` | 558-602 |
-| Mac entitlements | `OSGKeyboardMac/OSGKeyboardMac.entitlements` | 1 |
-
----
-
-## 8. 附录:版本与里程碑
-
-| 版本 | 日期 | 关键 Mac 相关变更 |
-|---|---|---|
-| 2.0.1 (build 90) | 2026-08-21 | (无 Mac-specific changelog 条目) |
-| 2.0.0 (build 85) | 2026-08-19 | "Optional OSG account"(iOS only,但相关 HostSupport 代码编进 Mac)|
-| 1.8.0 (build 72) | 2026-08-14 | "Mac Styles / Settings follow the same pairing"(设计系统共用)+ "Mac model downloads"(**MLX 模型下装载入**)|
-| Pre-1.8.0 | — | Sherpa → MLX 迁移(`MacOnboardingView.swift:7-8` 注释)|
-
----
-
-**报告完。**
-下次更新窗口:2026-09-XX 复审 P0 ① ② 实施情况 + 决策项 6.4 落地。
diff --git a/docs/personal-dictionary-icloud-kvs-checklist.md b/docs/personal-dictionary-icloud-kvs-checklist.md
deleted file mode 100644
index 74ef92a..0000000
--- a/docs/personal-dictionary-icloud-kvs-checklist.md
+++ /dev/null
@@ -1,28 +0,0 @@
-# Personal Dictionary iCloud KVS — Manual Verification Checklist
-
-Use this on **macOS with Xcode 16+** and at least two devices signed into the **same Apple ID** with iCloud Drive / iCloud enabled.
-
-## Prerequisites
-
-1. In Apple Developer Portal, enable **iCloud** → **Key-value storage** for `com.osgkeyboard.ios`.
-2. Regenerate provisioning profiles after entitlements change.
-3. Run `xcodegen generate` and install a fresh build on each device.
-
-## Scenarios
-
-| # | Steps | Expected |
-|---|--------|----------|
-| 1 | Device A: open Personal Dictionary, enable **Sync via iCloud**, add term `TestWordA` | Toggle stays on; term appears locally |
-| 2 | Device B: open app → Personal Dictionary tab | `TestWordA` appears after pull (may take up to ~1 min) |
-| 3 | Device B: add `TestWordB` | Device A eventually shows both terms |
-| 4 | Both devices: edit same term offline, then go online | Newer edit wins; aliases union when terms match |
-| 5 | Device A: delete a term | Term disappears on Device B after sync |
-| 6 | Device A: disable iCloud sync | Local dictionary remains; Device B stops receiving new edits from A |
-| 7 | Sign out of iCloud on one device | App keeps local dictionary; sync errors may surface in UI |
-| 8 | Keyboard extension on Device A | Uses App Group cache immediately after main-app save — no iCloud wait |
-
-## Notes
-
-- KVS propagation is **eventual**; force-quit and reopen the app to speed up pulls.
-- The keyboard extension never talks to iCloud directly; only the main app syncs.
-- Payload limit is ~1 MB per key; very large dictionaries should show the “too large” error.
diff --git a/docs/screenshots/6.1/01-keyboard-default.png b/docs/screenshots/6.1/01-keyboard-default.png
deleted file mode 100644
index 7914cab..0000000
Binary files a/docs/screenshots/6.1/01-keyboard-default.png and /dev/null differ
diff --git a/docs/screenshots/6.1/02-flow-session.png b/docs/screenshots/6.1/02-flow-session.png
deleted file mode 100644
index 65a244b..0000000
Binary files a/docs/screenshots/6.1/02-flow-session.png and /dev/null differ
diff --git a/docs/screenshots/6.1/03-on-device-asr.png b/docs/screenshots/6.1/03-on-device-asr.png
deleted file mode 100644
index 582c438..0000000
Binary files a/docs/screenshots/6.1/03-on-device-asr.png and /dev/null differ
diff --git a/docs/screenshots/6.1/04-llm-polish.png b/docs/screenshots/6.1/04-llm-polish.png
deleted file mode 100644
index f31a8c6..0000000
Binary files a/docs/screenshots/6.1/04-llm-polish.png and /dev/null differ
diff --git a/docs/screenshots/6.1/05-providers.png b/docs/screenshots/6.1/05-providers.png
deleted file mode 100644
index 347026a..0000000
Binary files a/docs/screenshots/6.1/05-providers.png and /dev/null differ
diff --git a/docs/screenshots/6.7/01-keyboard-default.png b/docs/screenshots/6.7/01-keyboard-default.png
deleted file mode 100644
index cf787cd..0000000
Binary files a/docs/screenshots/6.7/01-keyboard-default.png and /dev/null differ
diff --git a/docs/screenshots/6.7/02-flow-session.png b/docs/screenshots/6.7/02-flow-session.png
deleted file mode 100644
index 7728ca4..0000000
Binary files a/docs/screenshots/6.7/02-flow-session.png and /dev/null differ
diff --git a/docs/screenshots/6.7/03-on-device-asr.png b/docs/screenshots/6.7/03-on-device-asr.png
deleted file mode 100644
index d4d4cbf..0000000
Binary files a/docs/screenshots/6.7/03-on-device-asr.png and /dev/null differ
diff --git a/docs/screenshots/6.7/04-llm-polish.png b/docs/screenshots/6.7/04-llm-polish.png
deleted file mode 100644
index 6e5f2e4..0000000
Binary files a/docs/screenshots/6.7/04-llm-polish.png and /dev/null differ
diff --git a/docs/screenshots/6.7/05-providers.png b/docs/screenshots/6.7/05-providers.png
deleted file mode 100644
index caf375a..0000000
Binary files a/docs/screenshots/6.7/05-providers.png and /dev/null differ
diff --git a/docs/screenshots/README.md b/docs/screenshots/README.md
deleted file mode 100644
index 031cf4f..0000000
--- a/docs/screenshots/README.md
+++ /dev/null
@@ -1,73 +0,0 @@
-# App Store Screenshots
-
-> ⚠️ **PLACEHOLDERS.** The 10 PNGs in this directory are
-> automatically generated blanks produced by
-> `scripts/generate_screenshot_placeholders.py` and must be
-> **replaced with real Simulator screenshots** before App Store
-> submission. They use the correct dimensions (1290×2796 for
-> 6.7", 1179×2556 for 6.1") so the upload validator will accept
-> them, but they contain no real UI.
-
-## Required dimensions (2026)
-
-| Size | Devices | Dimensions | Apple requirement |
-|------|---------|-----------|---|
-| 6.7" | iPhone 17 Pro Max, 17, 16 Pro Max, 16 Plus, 15 Pro Max, 15 Plus | 1290 × 2796 px | **Required** (3-10 images) |
-| 6.1" | iPhone 17 Pro, 17, 16 Pro, 16, 15 Pro, 15, 14 Pro, 14 | 1179 × 2556 px | **Required** (3-10 images) |
-| 5.5" | iPhone 8 Plus (legacy) | 1242 × 2208 px | Optional since 2024 |
-
-iPad screenshots are not required because OSGKeyboard is iPhone-only
-(`TARGETED_DEVICE_FAMILY = 1`).
-
-## Layout
-
-```
-docs/screenshots/
-├── 6.7/ ← 1290×2796 (iPhone 17 Pro Max / 17)
-│ ├── 01-keyboard-default.png
-│ ├── 02-flow-session.png
-│ ├── 03-on-device-asr.png
-│ ├── 04-llm-polish.png
-│ └── 05-providers.png
-└── 6.1/ ← 1179×2556 (iPhone 17 Pro / 17)
- ├── 01-keyboard-default.png
- ├── 02-flow-session.png
- ├── 03-on-device-asr.png
- ├── 04-llm-polish.png
- └── 05-providers.png
-```
-
-## How to capture real screenshots
-
-1. Open `OSGKeyboard.xcodeproj` in Xcode 26+
-2. Run on **iPhone 17 Pro** simulator (6.1" set) and **iPhone 17 Pro Max** simulator (6.7" set)
-3. For each scene:
- ```bash
- # Take a screenshot of the simulator window
- xcrun simctl io booted screenshot ~/Desktop/shot.png
- ```
-4. Process for App Store (Apple rejects frames containing the device bezel — full-screen content only):
- ```bash
- # The simulator screenshot already has a thin device frame.
- # Open in Preview, crop to full screen (⌘+K with ⌥ for precision),
- # export as PNG at 1290×2796 or 1179×2556.
- sips -z 2796 1290 shot.png --out final-6.7.png
- sips -z 2556 1179 shot.png --out final-6.1.png
- ```
-5. Replace the placeholders with the real captures, keeping the
- same filenames so the App Store Connect → Version → Uploads UI
- auto-pairs by file.
-
-## Scenes to capture
-
-The 5 placeholders are intentional scene placeholders. Capture these
-*exact* screens, in this order:
-
-1. **Keyboard at rest** — the iOS keyboard, OSGKeyboard mode, no recording
-2. **Flow session active** — keyboard with the green/orange recording ring,
- partial transcript visible in the host text field
-3. **On-device ASR** — Settings view with the locale list, the on-device
- indicator (iPhone icon) visible next to ≥ 3 supported locales
-4. **LLM polish** — Settings view with the API provider card, a sample
- "polish" transformation shown in the inline preview
-5. **Providers** — API Settings card scrolled to show all 6 provider logos