小橙子 8ac3ea395a
feat(solo): wire LSP error count into Tier-1 signals (Phase 5) (#27)
* feat(solo): wire LSP error count into Tier-1 signals + cleanup suggestion

Phase 5 of editor-lsp-foundation (tasks 41-45):

- src/server/services/soloSuggestions.ts: SoloSignalsTier1 gains
  lspErrorCount field + new ruleLspError. Rule fires on positive
  integer counts only (defensive against undefined / 0 / non-integer
  / negative). Score 80 — high enough to outrank other 'cleanup'
  candidates (todo-marker / sync-upstream / stash) via the existing
  per-category dedup pass, and to outrank test-gap / ship / finish-wip /
  release base scores so the user is told to clear type errors first
  whenever the LSP reports any.
- src/server/services/soloSignalsService.ts: gatherSoloSignalsTier1 now
  accepts an optional `getLspErrorCount` provider. Wrapped in
  Promise.allSettled so an LSP failure / missing prereq silently
  leaves lspErrorCount undefined — the rule then sits the round out
  rather than nagging users about a setup problem they didn't ask
  about. Existing callers don't pass the option, so no behavioral
  drift on the current call sites; the manager will inject a real
  provider when the LspManager singleton from Phase 3 is wired into
  the Solo welcome-card pipeline.
- desktop/src/i18n/locales/{en,zh,zh-TW,jp,kr}.ts: 3 keys ×
  5 locales = 15 entries for solo.suggest.lspError.{title,detail,
  taskPrompt}. The other LSP-related i18n keys mentioned in the
  spec (lsp.indicator.*, editor.unsupportedEncoding, editor.conflict.*,
  editor.unsavedClose.*) are deferred to the component-integration PR
  that swaps Phase 2/3's hardcoded English for translated labels —
  shipping keys without callers would create dead code that drifts
  out of sync before integration lands.
- src/server/services/soloSuggestions.test.ts: 6 new tests for
  ruleLspError covering undefined / 0 / non-integer / negative gates,
  positive-count emission, per-category dedup outranks
  todo / sync-upstream when LSP errors exist.
- src/server/services/soloSuggestions.i18n.test.ts: fixtureWithEverySignal
  now includes lspErrorCount, so the existing en.ts contract scan
  automatically validates the 3 new keys.

Tested:
- bun test src/server/services/soloSuggestions.test.ts (44/44, +6 new)
- bun test src/server/services/soloSignalsService.test.ts
- bun test src/server/services/soloSuggestions.i18n.test.ts
- 61/61 across 3 Solo-related test files
- bun run lint (desktop tsc --noEmit clean — 5 locales × TranslationKey
  mapped Record gates the additions)

Note: pre-existing e2e/business-flow failures (~44) on origin/main
remain untouched.

_Requirements: 14.1, 14.2, 14.3, 14.4, 14.5, 15.6_

* test(desktop): add i18n contract test for solo.suggest.lspError

Phase 5 follow-up: change-policy CI gate refused PR #27 because the
desktop locale changes shipped without a matching desktop test —
production code in `desktop/src/i18n/locales/*.ts` requires a
desktop-side test. The server-side i18n contract test
(soloSuggestions.i18n.test.ts) only validates en.ts, leaving the
4 other locales unchecked at the desktop boundary.

This test mirrors that contract on the desktop side: 5 locales × 3
keys = 15 presence checks, plus interpolation checks for {count} and
absence-of-params check for the static detail key. 18/18 passing.

The test imports each locale as a named export (the file's `export
const zh: Record<TranslationKey, string>` is repeated across all
five locale files, so `zh-TW.ts` needs `import { zh as zhTW }` to
disambiguate at the call site).

Tested:
- bun run test src/i18n/lspError.test.ts (18/18 pass)

---------

Co-authored-by: 你的姓名 <you@example.com>
2026-06-12 08:33:32 +08:00
..