Skip to content

fix(query): configurable flood pulsetime to fix missing time with high poll intervals#875

Open
behdadmansouri wants to merge 2 commits into
ActivityWatch:masterfrom
behdadmansouri:fix/flood-pulsetime-configurable
Open

fix(query): configurable flood pulsetime to fix missing time with high poll intervals#875
behdadmansouri wants to merge 2 commits into
ActivityWatch:masterfrom
behdadmansouri:fix/flood-pulsetime-configurable

Conversation

@behdadmansouri

Copy link
Copy Markdown

Summary

Fixes ActivityWatch/activitywatch#1177

Users who raise the watcher polling interval above 5s lose trackable time because the default 5s pulsetime in flood() no longer fills the inter-poll gaps. A user working 8h could see only 7.5h in the dashboard.

Changes:

  • queries.ts: Add optional floodPulsetime field to BaseQueryParams (default 5); pass it as an explicit second argument to every flood() call inside canonicalEvents() and browserEvents()
  • stores/settings.ts: Add floodPulsetime: number (default 5) — persisted like all other settings
  • stores/activity.ts: Include floodPulsetime: useSettingsStore().floodPulsetime in both fullDesktopQuery and multideviceQuery param objects
  • views/settings/DeveloperSettings.vue: Add a number input for "Flood pulsetime (seconds)" with descriptive help text explaining the polling_interval + 1 rule

Test plan

  • Default behaviour unchanged: floodPulsetime defaults to 5, all existing queries behave identically
  • User with 30s polling sets "Flood pulsetime" to 31 in Developer Settings → gaps are filled, reported time matches clock time
  • Setting persists across reloads

Note: This pairs with ActivityWatch/aw-core#139 which exposes pulsetime as a parameter of the flood() query function. This web UI change works independently (the query language already forwards extra args correctly).

🤖 Implemented with Claude Code — AI assistance disclosed per contribution guidelines.

Previously the import UI showed nothing on success (the null response was
silent) and displayed only a generic fallback message on failure regardless
of what the server reported.

- Add a dismissible success alert shown after a successful import
- Extract the server error message from the response body and display it;
  fall back to the generic message if no structured error is present
- Add import_success flag to data() to drive the new success alert

Fixes ActivityWatch/activitywatch#394

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@greptile-apps

greptile-apps Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR makes the pulsetime argument of flood() configurable via a new Developer Settings field so users with high watcher polling intervals (e.g. 30 s) can avoid losing trackable time. It also bundles an unrelated fix in Buckets.vue that corrects a dismissabledismissible typo, adds a success alert for bucket imports, and extracts server-side error messages.

  • queries.ts / stores/settings.ts / stores/activity.ts: adds floodPulsetime (default 5) to BaseQueryParams, the settings store, and both fullDesktopQuery / multideviceQuery call sites, threading it through every flood() invocation in canonicalEvents and browserEvents.
  • views/settings/DeveloperSettings.vue: exposes the new setting as a number input with v-model.number, min="1", and descriptive help text explaining the polling_interval + 1 rule.
  • views/Buckets.vue: corrects the dismissible spelling, adds an import_success success alert, and surfaces err?.response?.data?.message for richer import-failure feedback.

Confidence Score: 4/5

Safe to merge once backward compatibility of the extra flood() argument with existing aw-server deployments is confirmed.

The core feature is straightforward and the default (5 s) preserves existing behaviour exactly. The main uncertainty is whether passing a second positional argument to flood() on aw-server versions that predate aw-core#139 silently ignores it or throws a runtime error — a regression that would break all activity queries for those users. The store also lacks a lower-bound guard, so a zero or negative pulsetime would propagate unchecked into generated queries.

src/queries.ts — the flood() argument change is the highest-risk line; confirm the query language handles extra args gracefully on older server versions.

Important Files Changed

Filename Overview
src/queries.ts Adds optional floodPulsetime to BaseQueryParams and threads it through every flood() call in canonicalEvents and browserEvents; defaults to 5 via nullish coalescing when omitted.
src/stores/settings.ts Adds floodPulsetime: number (default 5) to the State interface and initial state; persists through the existing server/localStorage mechanism without any store-level bounds validation.
src/stores/activity.ts Injects floodPulsetime from the settings store into both fullDesktopQuery and multideviceQuery parameter objects.
src/views/settings/DeveloperSettings.vue Adds a number input with v-model.number for floodPulsetime under Developer Settings; includes min="1" HTML constraint and helpful description text.
src/views/Buckets.vue Unrelated fix: corrects dismissabledismissible typo, adds a success alert for bucket imports, and surfaces err?.response?.data?.message for richer error feedback.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant User as User (Developer Settings)
    participant DeveloperSettings as DeveloperSettings.vue
    participant SettingsStore as stores/settings.ts
    participant ActivityStore as stores/activity.ts
    participant Queries as queries.ts
    participant Server as aw-server

    User->>DeveloperSettings: Set floodPulsetime (e.g. 31)
    DeveloperSettings->>SettingsStore: "update({ floodPulsetime: 31 })"
    SettingsStore-->>SettingsStore: persist to server/localStorage

    Note over ActivityStore: On activity view load
    ActivityStore->>SettingsStore: useSettingsStore().floodPulsetime
    SettingsStore-->>ActivityStore: 31
    ActivityStore->>Queries: "fullDesktopQuery({ ..., floodPulsetime: 31 })"
    Queries->>Queries: "canonicalEvents() pulsetime = 31"
    Queries->>Queries: "browserEvents() pulsetime = 31"
    Queries-->>ActivityStore: query string with flood(..., 31)
    ActivityStore->>Server: POST /query with flood(bucket, 31)
    Server-->>ActivityStore: events with gaps filled up to 31s
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant User as User (Developer Settings)
    participant DeveloperSettings as DeveloperSettings.vue
    participant SettingsStore as stores/settings.ts
    participant ActivityStore as stores/activity.ts
    participant Queries as queries.ts
    participant Server as aw-server

    User->>DeveloperSettings: Set floodPulsetime (e.g. 31)
    DeveloperSettings->>SettingsStore: "update({ floodPulsetime: 31 })"
    SettingsStore-->>SettingsStore: persist to server/localStorage

    Note over ActivityStore: On activity view load
    ActivityStore->>SettingsStore: useSettingsStore().floodPulsetime
    SettingsStore-->>ActivityStore: 31
    ActivityStore->>Queries: "fullDesktopQuery({ ..., floodPulsetime: 31 })"
    Queries->>Queries: "canonicalEvents() pulsetime = 31"
    Queries->>Queries: "browserEvents() pulsetime = 31"
    Queries-->>ActivityStore: query string with flood(..., 31)
    ActivityStore->>Server: POST /query with flood(bucket, 31)
    Server-->>ActivityStore: events with gaps filled up to 31s
Loading

Reviews (1): Last reviewed commit: "fix(query): make flood pulsetime configu..." | Re-trigger Greptile

Comment thread src/queries.ts
return [
// Fetch window/app events
`events = flood(${queryBucket(bid_window)});`,
`events = flood(${queryBucket(bid_window)}, ${pulsetime});`,

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 Extra argument may break flood() on older aw-server versions

The PR description notes this change pairs with aw-core#139, which adds pulsetime as a named parameter to flood(). Users running an aw-server version that predates that change will suddenly receive a query with an unexpected second positional argument. Depending on how the query language dispatcher handles extra args, this could result in a TypeError or similar runtime error that causes all activity queries to fail — with no fallback.

Before landing, it is worth confirming that the aw-core query interpreter silently ignores extra positional arguments when flood() is called with the old signature, and documenting the minimum required aw-server version if not.

Comment thread src/stores/settings.ts
showYearly: false,
useMultidevice: false,
requestTimeout: 30,
floodPulsetime: 5,

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.

P2 No store-level bounds validation for floodPulsetime

The HTML input enforces min="1", but the store's update() action accepts any value passed to it directly (e.g. from tests, direct API calls, or a malformed server response). A value of 0 or a negative number would silently produce flood(query_bucket("..."), 0) in the generated query string, which likely has undefined behaviour in the query language. Adding a clamp or guard (e.g. Math.max(1, value)) inside the setter or the update action would be more defensive.

…h high poll intervals

Users who raise the watcher polling interval above 5s lose trackable time because the
default 5s pulsetime in flood() no longer fills the inter-poll gaps. A user working 8h
could see only 7.5h in the dashboard.

Changes:
- queries.ts: add optional floodPulsetime to BaseQueryParams (default 5);
  pass it as the explicit second argument to all flood() calls in
  canonicalEvents() and browserEvents()
- settings.ts: add floodPulsetime: number (default 5, persisted like
  other settings)
- stores/activity.ts: thread useSettingsStore().floodPulsetime into both
  fullDesktopQuery and multideviceQuery param objects
- DeveloperSettings.vue: add a number input for floodPulsetime with
  guidance text explaining the poll_interval + 1 rule

The default of 5 is fully backward-compatible. Users who increased their
polling interval can now set this to polling_interval + 1 in Developer
Settings and immediately recover the missing time.

Pairs with aw-core fix: ActivityWatch/aw-core#139
Fixes ActivityWatch/activitywatch#1177

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@behdadmansouri behdadmansouri force-pushed the fix/flood-pulsetime-configurable branch from e2eb207 to 637abe9 Compare June 18, 2026 00:20
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.

Fundamental flaw in duration calculation

1 participant