fix(patterns): one generic-element filter for all three click detectors - #57
Open
Sarcastic-Soul wants to merge 2 commits into
Open
fix(patterns): one generic-element filter for all three click detectors#57Sarcastic-Soul wants to merge 2 commits into
Sarcastic-Soul wants to merge 2 commits into
Conversation
daily_habits grouped clicks with SQLite's date(), which buckets the stored UTC timestamps by UTC calendar day, while the rest of the package is local-day based. For a user far enough from UTC a single local day straddles a UTC boundary and was counted as two -- and since the habit threshold is days >= 2, that reported a habit out of activity that never repeated, contradicting the module's stated guarantee. Bucket in Python from parse_epoch + local_day_string instead, matching how coverage() already derives local time and keeping the result independent of the SQLite process timezone. The per-day click floor the SQL HAVING enforced moves to MIN_CLICKS_PER_DAY, and rows whose timestamp will not parse are now skipped rather than bucketed at epoch 0. Adds tests/test_patterns.py (the module had no direct coverage). Fixes nossa-y#51 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
_GENERIC_ELEMENTS was defined once but used by only one detector. repeated_clicks retyped all five names in SQL; daily_habits listed just two. All three matched case-sensitively. So the same click stream produced "Clicked 'Scroll Area' 5x" as a repeated click (capitalization slipped past the lowercase SQL list) and "'cell' on 2 days" as a daily habit (cell was missing from the shorter list) -- both of them exactly the noise the list exists to suppress. Make _GENERIC_ELEMENTS the single source of truth: _is_generic() strips and lowercases before matching, and the SQL fragment is derived from the same set rather than retyped. repeated_clicks keeps filtering in SQL so generic rows cannot consume slots in its LIMIT 20; daily_habits and action_sequences use the Python check. Fixes nossa-y#52 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #52
Important
Stacked on #56. Both PRs touch
daily_habits, and building this one onmainproduced a real merge conflict — so it is branched off #56 instead. Merge #56 first; this PR's own change is the second commit (fix(patterns): one generic-element filter…), which is the one to review here. Once #56 lands, this becomes a single-commit diff automatically. Happy to re-cut it againstmainin either order if you'd rather.The bug
patterns.pyopens with what looks like the canonical list of elements the recorder couldn't meaningfully name:Only one of the three click detectors actually used it. The other two re-implemented the filter in SQL, and none of the three agreed:
action_sequencesrepeated_clicksNOT INdaily_habits('scroll area','group')What that produced
One click stream —
cellx5 on each of two days, plusScroll Areax5 (recorder capitalization varies):repeated_clicksClicked 'Scroll Area' (AXScrollArea) 5xdaily_habits'cell' on 2 days, 10x totalaction_sequencesScroll Area -> Scroll Area -> …[AXScrollArea] -> [AXScrollArea] -> …'Scroll Area'slipped through because SQLNOT INis case-sensitive and the list is lowercase.'cell'was reported as a habit becausedaily_habits' shorter list omitted it — whilerepeated_clicks, reading the same rows, filtered that exact name out.Both results are noise in
get_patterns, which is served to agents as automation suggestions. The maintenance trap is the quieter half: the constant sits at the top of the file looking authoritative, so editing it silently fixes one detector out of three.The change
_GENERIC_ELEMENTSbecomes the real single source of truth, with two derived forms:repeated_clickskeeps filtering in SQL — deliberately. It hasORDER BY cnt DESC LIMIT 20, so generic rows filtered late would consume slots that real elements should hold. TheNOT INlist is now generated from the set and matched vialower(trim(element_name)), so it can't drift and it's case-insensitive.daily_habitsdrops its SQL list entirely and calls_is_genericin the loop it already runs. NoLIMITto protect, so the simpler form is fine.action_sequencesswaps its inlinein _GENERIC_ELEMENTSfor_is_generic, picking up strip + case-folding. Behaviour is otherwise unchanged: a generic element still keeps its slot in the sequence and stands in as[AXRole], because the click did happen — it just doesn't have a name worth printing.Tests
Five tests added to
tests/test_patterns.py:test_is_generic_ignores_case_and_padding— every name in the set, upper/title/paddedtest_repeated_clicks_drops_generic_elements_whatever_the_case— the'Scroll Area'leak, plus a real element alongside it to confirm nothing over-filterstest_daily_habits_drops_every_generic_element— the'cell'habittest_action_sequences_stands_in_for_generic_elements— sequence still mined, name replaced by roletest_all_three_detectors_agree_on_what_is_generic— the invariant itself: one capture containing every generic name, and no detector prints any of them. This is the property the three filters used to disagree on, so it's the one guarding against future drift.Full suite: 117 passed.
Scope
Filtering only. No threshold, ranking, or label format changes;
url_patterns,app_switching, andrepeated_textare untouched.