Repository navigation
fix(heartbeat): stop losing, duplicating and misattributing coding activity - #14
Conversation
…tivity The server builds coding sessions per machine from heartbeats and closes a session after a 10 minute gap, so lost, duplicated or misattributed heartbeats skew measured effort. - Record a save only in the project that has the file open (or owns it). FileSaveListener notified every open project, so each save produced one write heartbeat per open project, each named after that project. - Listen on the editor event multicaster instead of per editor. The project service is created lazily by the first fileOpened event, after restored tabs already exist, so typing in those tabs was never tracked. The per-editor listeners also outlived their editors and stacked up when a file was reopened. - Apply settings changes to running project services. Disabling tracking did not stop collection or sending, enabling it after starting disabled never started tracking, and interval changes were ignored. - Flush pending and failed heartbeats (bounded to 2s) when a project closes or the IDE exits instead of clearing them. - Report the machine name the CLI and the other editors use (plain gethostname()). InetAddress.getLocalHost().hostName goes through DNS and can return e.g. "host.lan", which split JetBrains activity into separate sessions from terminal and AI activity on the same computer. The name is resolved once instead of on every heartbeat on the EDT. - Match exclude patterns against the path inside the project, so a project located under e.g. ~/build/ or /ci/out/ is still tracked. - Only apply the same-caret duplicate check to navigation; edits that leave the caret in place were dropped. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01X5wQC8wrU4kY1jBfbzdquX
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
ReviewThe fixes line up well with the described bugs: single-project save attribution, multicaster-based listeners, settings applied live, flush on dispose, a stable hostname, and exclusion relative to the project root. The new tests cover the pure-function parts well. I couldn't build this here, and the PR says the collector, service and listener changes aren't compiled yet, so CI should be treated as the gate. Issues worth addressing
Smaller notes
Test coverageThe utility tests are good. The riskiest changes have no tests:
These would need the IntelliJ test fixtures or MockK. Even a MockK-based test of the Overall this is a solid improvement. I'd like to see items 1–3 resolved or confirmed as non-issues before merge. |
Codecov Report❌ Patch coverage is ❌ Your patch check has failed because the patch coverage (11.49%) is below the target coverage (70.00%). You can increase the patch coverage or adjust the target coverage. @@ Coverage Diff @@
## main #14 +/- ##
==========================================
- Coverage 14.30% 14.21% -0.10%
==========================================
Files 20 20
Lines 755 802 +47
Branches 172 184 +12
==========================================
+ Hits 108 114 +6
- Misses 630 669 +39
- Partials 17 19 +2
Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
|
CI status:
Review items 1–3, traced against the code:
Also checked: Generated by Claude Code |
Why
The server builds coding sessions per machine from heartbeats (
server/tasks/activity.sessions.go). A session closes after a 10-minute gap, and effort issession_end - session_start. A heartbeat that is lost, duplicated, recorded against the wrong project, or reported under a different machine name skews the measured effort.What changed
FileSaveListenersent every save to every open project, so with N projects open one save became N write heartbeats, each named after a different project'sbasePath. Now only the first open project that has the file open records it. If none does, it falls back toProjectLocator.guessProjectForFile.ShellTimeProjectServiceis created lazily, by the firstfileOpenedevent, after the restored tabs already exist.EditorFactoryListener.editorCreatednever fired for those tabs, so typing in them sent nothing until the tab was reopened. The collector now registers its document and caret listeners once, onEditorFactory.eventMulticaster, filtered to this project's editors. It no longer registers per editor. The old per-editor listeners also outlived their editors (leak) and stacked up when a file was reopened.ShellTimeConfigurable.apply()never calledShellTimeProjectService.updateSettings(). Unchecking "enabled" didn't stop caret or document collection, or the sending. Enabling after the IDE started disabled never started tracking. Interval changes were ignored.apply()now updates every open project, andupdateSettings()stops, starts or resumes the collector and the sender.dispose()used to clear the pending queue and the failed-retry queue. It now flushes them first, waiting at most 2 s.InetAddress.getLocalHost().hostNamepassesgethostname()throughgetaddrinfoand a reversegetnameinfoon Unix, so it can return e.g.host.lanor an FQDN. The CLI, VS Code and Neovim all report plaingethostname(). When the names differed, JetBrains activity landed in separate sessions from terminal and AI activity on the same computer: overlapping time was counted twice, and sessions were fragmented. The name now comes from/proc/sys/kernel/hostname, thenhostname, then env vars, withInetAddresslast. It is computed once; before, it was looked up for every heartbeat on the EDT.build/are tracked. The exclude patterns matched the absolute path, so a project located under anybuild/,out/,target/orvendor/directory (e.g./home/ci/build/repo) was never tracked. They now match against the path inside the project.Testing
download.jetbrains.comandwww.jetbrains.com, so./gradlew test/buildPlugincould not fetch the IntelliJ Platform. CI (testing.yml) is the authoritative compile and test run for this PR.SystemUtils.ktandProjectUtils.ktin a scratch Kotlin 2.3 / JDK 17 project. It used compile-only stubs for the three IntelliJ typesProjectUtilsimports, whichshouldExcludedoesn't touch. All 17 tests in the realSystemUtilsTestandProjectUtilsTestpass, including the new ones:EditorEventMulticaster.addDocumentListener/addCaretListener(listener, disposable),EditorFactory.getEditors(document, project),Disposer.newDisposable(parent, name),ProjectLocator.guessProjectForFile,FileEditorManager.isFileOpen). I reviewed them by hand, but they are not compiled yet.Companion PRs fix the same kinds of bugs in the VS Code and Neovim plugins.
🤖 Generated with Claude Code
https://claude.ai/code/session_01X5wQC8wrU4kY1jBfbzdquX
Generated by Claude Code