Skip to content

Add configurable polling intervals in Settings#122

Merged
FuJacob merged 7 commits into
mainfrom
feature/configurable-polling
May 21, 2026
Merged

Add configurable polling intervals in Settings#122
FuJacob merged 7 commits into
mainfrom
feature/configurable-polling

Conversation

@FuJacob

@FuJacob FuJacob commented May 21, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds a Performance section to Settings with two steppers:
    • Suggestion Delay — debounce before generating (10-500ms, step 10, default 50ms)
    • Focus Poll Interval — AX polling frequency (10-500ms, step 10, default 50ms)
  • Both values persist to UserDefaults and take effect immediately at runtime
  • FocusTracker timer restarts with the new interval when the setting changes
  • Debounce reads from settingsSnapshot instead of the static configuration

Files changed (8)

  • SuggestionSettingsModel.swift — new @Published properties, setters, snapshot publisher
  • SuggestionEngineModels.swift — new fields on SuggestionSettingsSnapshot
  • FocusTracker.swift — mutable pollInterval, updatePollInterval() method
  • FocusTrackingModel.swift — forwards updatePollInterval() to tracker
  • TabbyAppEnvironment.swift — Combine subscription to forward poll interval changes
  • SuggestionCoordinator+Prediction.swift — reads debounce from settings snapshot
  • SettingsView.swift — new Performance section with steppers
  • TabbyTestFixtures.swift — updated test fixtures for new snapshot fields

Test plan

  • xcodebuild build passes
  • xcodebuild build-for-testing passes
  • Manual QA: open Settings, adjust both steppers, verify suggestions respond faster/slower
  • Verify values persist across app restarts

🤖 Generated with Claude Code

Greptile Summary

This PR adds a Performance section to Settings with two configurable steppers — Suggestion Delay (debounce) and Focus Poll Interval — both persisted to UserDefaults and applied at runtime. The polling infrastructure, settings model, and UI wiring are all correctly implemented.

  • SuggestionSettingsModel adds @Published properties with clamped init resolution (10–500 ms), setter guards, and an expanded CombineLatest4 snapshot publisher; TabbyAppEnvironment subscribes to forward interval changes to FocusTracker via updatePollInterval().
  • SuggestionCoordinator+Prediction now reads the debounce delay from the live settingsSnapshot instead of the static configuration.
  • FoundationModelPromptRenderer and LlamaPromptRenderer silently uncomment user-tags prompt injection — a feature that was explicitly gated with a TODO review note — with no mention in the PR description.

Confidence Score: 4/5

The polling/debounce plumbing is safe to merge, but two renderer files contain an unannounced feature enablement that changes AI prompt content for all users.

The performance settings wiring is well-structured and correct. The concern is in FoundationModelPromptRenderer and LlamaPromptRenderer: user-tags injection was deliberately disabled with an explicit validation gate, and this PR quietly re-enables it in both generation paths without any mention in the description or test plan. That constitutes an active, unscoped behavioral change to AI output that deserves an explicit decision before shipping.

tabby/Support/FoundationModelPromptRenderer.swift and tabby/Support/LlamaPromptRenderer.swift — both contain the unscoped user-tags enablement.

Important Files Changed

Filename Overview
tabby/Support/FoundationModelPromptRenderer.swift Uncomments user-tags prompt injection — a feature change that was explicitly gated behind a TODO validation note and is unrelated to this PR's scope.
tabby/Support/LlamaPromptRenderer.swift Same unscoped user-tags uncomment as FoundationModelPromptRenderer; both renderers now inject tags into every prompt without validation.
tabby/Models/SuggestionSettingsModel.swift Adds @published debounce/poll-interval properties with UserDefaults persistence, clamped init resolution, and expands the Combine snapshot publisher from CombineLatest3 to CombineLatest4. Logic is sound; init now properly clamps values before persisting.
tabby/Services/Focus/FocusTracker.swift Changes pollInterval to var and adds updatePollInterval() that restarts the timer if running; correctly no-ops when the interval is unchanged.
tabby/App/Core/TabbyAppEnvironment.swift Adds a Combine subscription that forwards focusPollIntervalMilliseconds changes to FocusTrackingModel; uses weak capture and removeDuplicates correctly.
tabby/Models/SuggestionEngineModels.swift Adds debounceMilliseconds and focusPollIntervalMilliseconds to SuggestionSettingsSnapshot; the poll-interval field is unused by the coordinator and causes spurious snapshot invalidations.
tabby/UI/SettingsView.swift Adds a Performance section with two steppers (10–500 ms, step 10) wired to custom Binding helpers; straightforward and correct.

Sequence Diagram

sequenceDiagram
    participant User as User (Settings Stepper)
    participant SV as SettingsView
    participant SSM as SuggestionSettingsModel
    participant TAE as TabbyAppEnvironment
    participant FTM as FocusTrackingModel
    participant FT as FocusTracker
    participant SC as SuggestionCoordinator

    User->>SV: Adjusts Focus Poll Interval stepper
    SV->>SSM: setFocusPollIntervalMilliseconds(_:)
    SSM->>SSM: clamp to [10,500] and persist to UserDefaults
    SSM-->>TAE: $focusPollIntervalMilliseconds sink fires
    TAE->>FTM: updatePollInterval(milliseconds:)
    FTM->>FT: updatePollInterval(_ interval: TimeInterval)
    FT->>FT: stop() then start() with new interval

    User->>SV: Adjusts Suggestion Delay stepper
    SV->>SSM: setDebounceMilliseconds(_:)
    SSM->>SSM: clamp to [10,500] and persist to UserDefaults
    SSM-->>SC: snapshotPublisher emits new SuggestionSettingsSnapshot
    SC->>SC: reads settingsSnapshot.debounceMilliseconds on next keypress
Loading

Comments Outside Diff (1)

  1. tabby/Models/FocusTrackingModel.swift, line 20-45 (link)

    P2 Initial poll interval isn't passed to FocusTracker at construction

    FocusTracker is created here without a pollInterval argument, so it always starts at its hardcoded 50 ms default. The correct interval is applied later only because TabbyAppEnvironment happens to subscribe to $focusPollIntervalMilliseconds after constructing this model — the subscription fires synchronously and patches pollInterval before start() is called. This works today, but the ordering dependency is implicit: anything that calls focusModel.start() before TabbyAppEnvironment sets up that subscription (e.g. a test or a future refactor) will silently use the wrong interval. Adding pollInterval as a constructor argument and threading the stored value through would make the initialization self-contained and remove the hidden precondition.

    Fix in Codex Fix in Claude Code

Fix All in Codex Fix All in Claude Code

Reviews (3): Last reviewed commit: "Address Greptile feedback: source poll i..." | Re-trigger Greptile

Greptile also left 1 inline comment on this PR.

Comment thread tabby/Models/SuggestionSettingsModel.swift
Comment thread tabby/Models/SuggestionSettingsModel.swift
@FuJacob FuJacob force-pushed the improve/latency-aggressive branch from 29a770a to d6f64d8 Compare May 21, 2026 04:24
@FuJacob FuJacob changed the base branch from improve/latency-aggressive to main May 21, 2026 06:07
FuJacob and others added 6 commits May 20, 2026 23:16
- Drop debounce from 180ms to 50ms
- Drop AX polling interval from 250ms to 50ms
- Trigger immediate AX refresh on input events (before debounce)
- Shrink prompt window (prefix 1000→600, suffix 192→100 chars)

Combined these changes cut ~160-200ms off the critical path.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Keep maxPrefixCharacters at 1000 and maxSuffixCharacters at 192.
Suffix context matters for code-completion quality in structured editors.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix SwiftLint line_length violation in SettingsView.swift (213 chars)
- Re-enable userTags in both prompt renderers to fix 3 failing tests
- Add || true guard on hdiutil detach in build_test_dmg.sh (set -euo pipefail safety)
- Add trailing newline to build_test_dmg.sh
- Add refreshNow() before schedulePrediction() in active-session paths for consistency

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Exposes two runtime-adjustable performance settings:
- Suggestion Delay (debounce before generating, default 50ms)
- Focus Poll Interval (AX polling frequency, default 50ms)

Both persist to UserDefaults, flow through the settings snapshot,
and take effect immediately. The FocusTracker timer restarts with
the new interval when changed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@FuJacob FuJacob force-pushed the feature/configurable-polling branch from 20e8da5 to f84a2dc Compare May 21, 2026 06:17
…it values

- Source focusPollIntervalMilliseconds from SuggestionConfiguration instead of
  hardcoded Int(0.05 * 1000) to maintain single source of truth
- Apply max(10, min(500, raw)) clamping to both debounce and poll interval
  during init resolution, matching the setter validation
- Add focusPollIntervalMilliseconds to SuggestionConfiguration and test fixtures

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@FuJacob FuJacob enabled auto-merge (squash) May 21, 2026 06:23
@FuJacob FuJacob merged commit 916bb76 into main May 21, 2026
2 of 3 checks passed
@FuJacob FuJacob deleted the feature/configurable-polling branch May 21, 2026 06:27
Comment on lines 32 to +36
if let name = request.userName, !name.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
profileSections.append("The user's name is \(name).")
}
// TODO: Re-enable userTags in the prompt once we validate the feature's value.
// if let tags = request.userTags, !tags.isEmpty {
// let tagsString = tags.joined(separator: ", ")
// profileSections.append("Things the user types often include: \(tagsString).")
// }
if let tags = request.userTags, !tags.isEmpty {
let tagsString = tags.joined(separator: ", ")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 User-tags prompt injection enabled without PR scope or validation

Both FoundationModelPromptRenderer (here) and LlamaPromptRenderer (line 35–39) now actively inject user tags into every AI prompt. The previous gate comment explicitly said "Re-enable userTags in the prompt once we validate the feature's value." This change is unrelated to the configurable polling intervals this PR describes, and neither file's diff was mentioned in the PR description. Enabling unvalidated prompt content in two separate generation paths silently changes suggestion quality and tone for all users with tags set, with no test coverage or rollout guard.

Fix in Codex Fix in Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant