Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

@../AGENTS.md

`AGENTS.md` is the canonical project instruction file. Keep shared architecture, workflow, safety,
debugging, validation, and documentation rules there so Claude Code and other coding agents receive
the same guidance without duplicated copies drifting apart.
`AGENTS.md` is the canonical coding-agent instruction file. Keep shared workflow, safety, debugging,
validation, and architecture rules there so Claude Code and other agents receive the same guidance
without duplicated copies drifting apart. `ARCHITECTURE.md` is the ten-minute system map, while
`.internal/` contains local, gitignored deep-dive and interview-preparation documentation.
23 changes: 17 additions & 6 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ Start here when you need to understand lifecycle:
and wires cross-subsystem subscriptions. SwiftUI views should observe objects from that graph
rather than creating services directly.

`SuggestionSettingsModel` remains the individually `@Published` UI-facing compatibility surface.
Its `domainSettings` projection groups the same durable values into general, engine, completion,
context, correction, presentation, inline-feature, and shortcut domains. `SuggestionSettingsStore`
keeps the existing flat UserDefaults keys stable, and `SuggestionSettingsSnapshot` is the immutable
behavior boundary consumed by the suggestion pipeline.

This ownership rule prevents duplicate Accessibility observers, duplicate input monitors, runtime
reload races, and mismatched settings state.

Expand All @@ -84,14 +90,17 @@ Read the coordinator in this order:
4. `Cotabby/App/Coordinators/SuggestionCoordinator+Prediction.swift`
5. `Cotabby/App/Coordinators/SuggestionCoordinator+Acceptance.swift`

The coordinator owns orchestration and user-facing state. It should not absorb every rule. Prefer:
The coordinator owns orchestration plus active suggestion and presentation state. It should not
absorb every rule or state transition. Prefer:

- `SuggestionRequestFactory` for pure request construction
- `SuggestionAvailabilityEvaluator` for pure gating decisions
- `SuggestionSessionReconciler` for acceptance and active-tail reconciliation
- `SuggestionTextNormalizer` for backend-independent output cleanup
- `SuggestionWorkController` for generation task identity/cancellation
- `SuggestionInteractionState` for active suggestion session storage
- `SuggestionStreamingState` for latest-wins partial coalescing and monotonic rendering state
- `PostExhaustionAcceptanceState` for the bounded rapid-accept window after a tail is exhausted

This split matters because autocomplete is a state machine. Pure rules are easier to test and reason
about than coordinator mutations.
Expand All @@ -104,7 +113,8 @@ Focus and geometry live in:
- `FocusSnapshotResolver`: reduces raw AX elements into Cotabby-supported focus snapshots.
- `AXTextGeometryResolver`: resolves caret and input geometry.
- `AXHelper`: low-level Accessibility/Core Foundation helper calls.
- `FocusModels`: pure focus values, identities, capabilities, and debug inspection data.
- `FocusModels`: pure focus values, identities, capabilities, stale-result signatures, and the
lightweight `FocusPollingEvent` used by the developer overlay.

Accessibility data is eventually consistent and app-specific. Browser editors, Electron apps,
native AppKit fields, and secure fields expose different AX shapes. Preserve stale-result guards,
Expand All @@ -130,14 +140,15 @@ from Accessibility and Input Monitoring.

Runtime generation is split by responsibility:

- `SuggestionEngineRouter`: selects Apple Intelligence vs Open Source.
- `SuggestionEngineRouter`: selects Apple Intelligence, Open Source, or the configured
OpenAI-compatible endpoint and owns the narrow Apple-to-llama language fallback.
- `FoundationModelSuggestionEngine`: Apple on-device generation path.
- `LlamaSuggestionEngine`: request-to-prompt, llama result handling, and cache reset handoff.
- `OpenAICompatibleSuggestionEngine`: completion/chat transport, SSE streaming, and Ollama preload
behavior for the configured endpoint.
- `LlamaRuntimeManager`: UI-facing runtime state, model selection, warmup, and lifecycle control.
- `LlamaRuntimeCore`: explicitly serialized native boundary around mutable llama.cpp pointers,
prompt tokenization, KV-cache reuse, sampling, an optional deterministic constrained decoder
(`runConstrainedDecode`, gated behind the default-off `cotabbyConstrainedDecoderEnabled`), and
shutdown.
prompt tokenization, KV-cache reuse, built-in sampler delegation, cancellation, and shutdown.
- `BaseCompletionPromptRenderer`: prompt construction for the Open Source path. The llama models are
now *base* (non-instruct) GGUFs, so this renders a pure text continuation: no instruction preamble,
custom rules and context fold into a short conditioning preface (a base model conditions on
Expand Down
31 changes: 22 additions & 9 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,12 @@ power-profile application, engine/model selection, and endpoint connection inval
owns permission reactions, engine runtime start/stop, overlays, model-directory refresh, and
process-lifecycle behavior. Both retain subscriptions because both own different relationships.

[SuggestionSettingsModel.swift](Cotabby/Models/Settings/SuggestionSettingsModel.swift) is the published source
of app behavior. [SuggestionSettingsStore.swift](Cotabby/Support/Settings/SuggestionSettingsStore.swift)
persists non-secret preferences in UserDefaults. Endpoint credentials live in Keychain.
[SuggestionSettingsModel.swift](Cotabby/Models/Settings/SuggestionSettingsModel.swift) is the
individually published UI-facing source of app behavior. Its
[SuggestionSettingsData.swift](Cotabby/Models/Settings/SuggestionSettingsData.swift) projection groups
the same values by product domain without replacing the existing API. The immutable snapshot used by
the pipeline is derived from those domains. [SuggestionSettingsStore.swift](Cotabby/Support/Settings/SuggestionSettingsStore.swift)
keeps the established flat UserDefaults keys stable; endpoint credentials live in Keychain.

## Suggestion State Machine

Expand All @@ -126,17 +129,21 @@ Read the coordinator in this order:
4. [SuggestionCoordinator+Prediction.swift](Cotabby/App/Coordinators/SuggestionCoordinator+Prediction.swift)
5. [SuggestionCoordinator+Acceptance.swift](Cotabby/App/Coordinators/SuggestionCoordinator+Acceptance.swift)

The coordinator owns orchestration and user-facing suggestion state. It delegates rules and mutable
sub-state to smaller boundaries:
The coordinator owns orchestration plus active suggestion and presentation state. It delegates rules
and cohesive mutable sub-state to smaller boundaries:

- [SuggestionAvailabilityEvaluator.swift](Cotabby/Support/Suggestion/SuggestionAvailabilityEvaluator.swift):
pure permission, settings, focus, and runtime gates.
- [SuggestionRequestFactory.swift](Cotabby/Support/Suggestion/SuggestionRequestFactory.swift): pure bounded
request construction and prompt preview.
request construction and the selected backend's developer-debug prompt payload.
- [SuggestionWorkController.swift](Cotabby/Services/Suggestion/SuggestionWorkController.swift):
debounce/generation tasks and monotonically increasing work IDs.
- [SuggestionInteractionState.swift](Cotabby/Services/Suggestion/SuggestionInteractionState.swift):
active session, materialized context, consumed prefix, and known post-insertion AX lag.
- [SuggestionStreamingState.swift](Cotabby/Support/Suggestion/SuggestionStreamingState.swift): latest-wins
partial coalescing, one scheduled drain, and monotonic rendered-text state.
- [PostExhaustionAcceptanceState.swift](Cotabby/Support/Suggestion/PostExhaustionAcceptanceState.swift):
pure state for the bounded Tab-ownership window while an exhausted tail regenerates.
- [SuggestionSessionReconciler.swift](Cotabby/Support/Suggestion/SuggestionSessionReconciler.swift): type-through,
acceptance, and live-host reconciliation.
- [SuggestionTextNormalizer.swift](Cotabby/Support/Suggestion/SuggestionTextNormalizer.swift): backend-independent
Expand All @@ -146,13 +153,17 @@ A native correction path runs before model generation. NSSpellChecker and bundle
can suppress completion while a likely typo is forming, offer a green atomic replacement, or apply
an opt-in automatic fix after Space.

Engines can stream cumulative partials. The coordinator coalesces UI work, accepts only monotonic
extensions, and allows a displayed partial to become an active accept-ready session. The final result
Engines can stream cumulative partials. SuggestionStreamingState coalesces token-rate callbacks into
latest-wins UI work, accepts only monotonic extensions, and lets a displayed partial become an active
accept-ready session. The coordinator still owns scheduling and presentation. The final result
remains authoritative and can replace or suppress provisional text.

Normal sessions support exact type-through, word or phrase acceptance, full-tail acceptance, CJK-aware
segmentation, punctuation handling, optional trailing space, and speculative generation after final
acceptance. Corrections commit atomically rather than exposing partial acceptance.
acceptance. When rapid Tab presses cross the exhausted-tail regeneration gap,
PostExhaustionAcceptanceState can queue at most one unseen accept and has a generation-keyed backstop
that returns Tab ownership to the host. Corrections commit atomically rather than exposing partial
acceptance.

## Focus and Accessibility

Expand Down Expand Up @@ -381,13 +392,15 @@ policies, prompt utilities, normalization, and layout logic should receive focus
| Backend selection or memory issue | SuggestionEngineRouter, AppDelegate, LlamaRuntimeManager |
| Native decode/cache/shutdown issue | LlamaRuntimeCore, CotabbyInference boundary |
| Partial/final output mismatch | streaming policy, SuggestionTextNormalizer, seam guards |
| Rapid Tab leaks to the host after exhausting a tail | PostExhaustionAcceptanceState, acceptance coordinator |
| Acceptance key passes through or is stolen | InputMonitor, acceptance validation |
| Wrong or repeated inserted text | SuggestionInserter, InputSuppressionController, reconciler |
| Clipboard context is irrelevant | ClipboardRelevanceFilter, ClipboardContentDistiller |
| Screenshot context is stale/noisy | VisualContextCoordinator, OCRTextHygiene |
| Emoji or macro conflicts with suggestions | InlineCommandCoordinator, feature trigger machine |
| Permission loop or lost grant | PermissionManager, PermissionGuidanceController, app identity |
| Settings/onboarding window issue | SettingsCoordinator, WelcomeCoordinator |
| Settings persistence or domain mismatch | SuggestionSettingsModel, SuggestionSettingsData, SuggestionSettingsStore |

When a change crosses several rows, keep ownership at these boundaries rather than teaching one
coordinator to perform every step itself.
Loading