merge: bring local uncommitted work onto the cloud-agent branch

Merges the wip/local-snapshot-20260901 safety commit (94 uncommitted files:
in-flight Account/Mac/Tip work, clipboard-semantics improvements, and the
feature-preview recording tooling) into the newer cloud-agent line.

Conflict resolutions:
- docs/*: honour the branch's deliberate removal in eac44da; drop the now
  dangling docs/archive/README.md that only pointed at the deleted files.
- CHANGELOG: both sides describe separate [Unreleased] features; keep both.
- ClipboardSkillSemanticRanker: take the branch's reply routing, which retires
  ranking playfulReply/acceptInvitation/... as separate skills in favour of
  ordinary/formal/playful variants inside one reply action. Keep the local
  link/phone/CJK-density work, which is orthogonal and still referenced.
- OfficialSkillCatalogTests: keep the local "installed exactly once" assertion;
  it matches the test's own comment and does not hardcode the default set.
- ClipboardSkillSemanticRankerTests: keep both sides' disjoint test functions;
  retarget the two link/phone cases at the branch's row, which no longer
  surfaces the retired specialised reply skills.

Verified: xcodebuild build succeeds and the 77 ranker/catalog/analyzer tests
pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-01 12:34:23 -07:00
co-authored by Claude Opus 5
79 changed files with 3459 additions and 1178 deletions
@@ -304,8 +304,12 @@ public actor ClipboardSemanticAnalyzer {
private static let resourceDirectory = "ClipboardSemantics"
private static let manifestName = "clipboard-semantic-models"
private static let maximumSemanticSegments = 8
private static let maximumSegmentCharacters = 500
// Segment length tracks the training corpus, whose records are single
// sentences (zh max 33 characters, en max 138). Feeding a whole multi-
// sentence paste as one blob puts the classifiers far out of distribution
// and dilutes bag-of-words confidence below their routing thresholds.
private static let maximumSemanticSegments = 24
private static let maximumSegmentCharacters = 180
private static let minimumSentimentConfidence = 0.65
private static let minimumSentimentMargin = 0.15
@@ -923,9 +927,6 @@ public actor ClipboardSemanticAnalyzer {
}
private func semanticSegments(in text: String) -> [String] {
if text.count <= Self.maximumSegmentCharacters {
return [text]
}
let tokenizer = NLTokenizer(unit: .sentence)
tokenizer.string = text
var segments: [String] = []