All posts
MeasurementAutomatic timesheetsAccuracyOn-device matchingProduct engineering

Automatic time tracking accuracy: cutting wrong bookings by two thirds

We published the thresholds our automatic timesheet used, then measured them. Eight days later they are gone — derived from your own workspace instead.

TO
The TimerOS Team
Vezoft
9 min read

A week ago we published the thresholds behind automatic task matching — a score of 0.45, a margin of 0.15, sixty seconds of agreement. It felt like the honest thing to do. Then we measured what those numbers did on real workspaces, and it turned out that publishing them was honest about the wrong thing: a threshold we picked once, in our office, cannot be right for your task list.

Since then the matcher has been rebuilt around a single question — how do you know that? — and the fixed numbers are mostly gone. The bar a booking has to clear is now worked out from your own projects, every few minutes, on your own machine. This post is the measurement: what we count, what moved, and the one rule that survived every rewrite.

Scope, before the numbers
Task matching is still marked Beta in the desktop app. It runs in the Windows desktop app only — the web dashboard has no matcher — it is on by default and switchable off, and attributed hours still do not assemble an invoice. The mechanics of the original matcher are written up separately; this post assumes them.

What “accurate” has to mean before you can measure it

Accuracy is a useless word here until you say which mistake you are counting. An automatic timesheet has two, and they cost wildly different amounts. It can miss work it should have recognised, which you notice immediately, because the row is empty. Or it can file time against a task that had nothing to do with it — which looks exactly like a correct row, and which you will not notice at all.

A wrong booking is more expensive than a missing one, because you cannot see it.
The same constraint the first version was built around, now with a number attached.

So the number we report is the second kind. The test feeds the matcher work that belongs to a project the workspace does not have, and counts how often it books anyway. Every answer is wrong by construction, so there is no judgement call in the scoring.

Figure 1 · Leave one project out
1 · The window
Ship the iOS beta to TestFlight — Google Chrome
A real task title, dressed with the chrome a real window carries.
2 · The workspace
iOS App DevelopmentWebsite Design & BuildCloud Migration+ 17 more
The project that owns the title is removed. Everything else stays.
3 · The right answer
Say nothing
The work is not in this workspace. Anything booked here is a false positive, counted.
Run once per title across the twenty project plans that ship with TimerOS — 481 windows in total. The measurement is only meaningful because the right answer is known in advance and is always the same: silence.

A second run does the opposite, and matters just as much: every one of those 481 titles is fed to a workspace that does contain it. There, silence is the failure. A matcher can reach a flawless false-positive rate by never speaking at all, so the two runs are read together or not at all. On today’s build that second run recognises 453 of the 481, is silent on 28, and names the wrong project exactly zero times — and that zero is a hard assertion in the test suite, not an aspiration.

The number, and where it went

Figure 2 · Wrong bookings, as a share of 481 windows describing absent work
False bookings% of windows
16.8
11.4
5.6
v1.6.7v1.6.8v1.8.0
Lower is better. Each step is a named change with a test pinning it — the counts are recorded in the code as budgets that may only go down.

Two thirds of the wrong bookings are gone, and neither step cost recognition of real work. The full breakdown of that last run is worth seeing whole, because the middle slice is the interesting one — it is where a booking goes when the app decides it is not sure enough to file an hour but is sure enough to ask.

Figure 3 · All 481 windows describing work the workspace does not have
481windows
Outcome per window
Silent — correct · 43791%
Offered a wrong project · 174%
Booked the wrong task · 276%
Measured on the shipped build. The 17 wrong chips cost a glance and a dismissal; the 27 wrong bookings are the number that actually matters, because nobody sees those.

Here is what each of the three changes actually was.

1. Refuse to book a window you cannot account for

The old gate asked whether a task scored highly enough. It never asked the reverse question: does this candidate explain the window? A page with nine words on it, of which a task matched two, could clear every threshold on those two words while the other seven — the ones actually saying what the page was — went unexamined.

Booking now needs two ratios, not one. At least half the content words on screen have to be words this candidate recognised, and at least 30% of the task’s own vocabulary has to appear. Fail either and the booking is refused. Crucially it is refused, not discarded: the candidate drops to the tier below and arrives as a one-click chip. Of the wrong bookings this removed, most went silent entirely and a handful came back as questions.

2. Let the workspace set its own bar

This is the change that makes the earlier post out of date. A threshold of 0.45 is a statement about how confusable your projects are — and we have no way of knowing that. Two agencies, one running twenty unrelated client projects and one running four near-identical rebuilds, need different numbers, and the second one needs a number no amount of care in our office would produce.

So the desktop app derives it. Every few minutes it re-scores a deterministic sample of your own task titles against your own projects and asks two questions: what does a right answer look like here, and what is the best wrong answer this workspace can produce? The booking bar is set above the second, with a widening allowance when there are few references to learn from.

Figure 4 · Where a threshold comes from
Picked once, by usDerived from your workspace
What sets itA judgement call, made on our task namesYour task titles, re-scored against your projects
When projects overlapSame bar; more wrong bookingsBar rises above the best wrong answer available
When it cannot be worked outn/a — the number is always thereFalls back to the shipped number and records why
If words cannot separate the workBooks something anywayBooking switches off; everything becomes a suggestion
The fences that govern explanation are derived from the low tail of the workspace's own right answers, not from its wrong ones — the rule being that you may never demand more explanation than your own correct matches actually provide.

That last row is the part we are most attached to. If the tenth percentile of a workspace’s right answers sits below the ninetieth percentile of its best wrong ones, then words simply cannot tell that workspace’s projects apart — and the app says so and stops booking, rather than performing the same guess with a confident face. The timer keeps running. Everything falls to a one-click chip, and the reason is written to the session record.

3. Weigh a shared word instead of deleting it

An earlier fix had struck any word appearing in two projects from the corpus outright, on the reasoning that a shared word distinguishes nothing. It was too blunt in a way that took a while to spot: a workspace with projects called Website Design and Android App would strike the company’s own name, which is correct, and also strike design — so the project whose name contains the word could not use it.

Words are now scored by how far past chance they point at one project, on a scale measured against how many projects you have. With two projects a word split evenly between them is exactly a coin toss, and used to look half-informative. Recognition of real work went up as a result, not down.

0
windows in the false-positive run
0.0%
of them still book (was 16.8%)
0
wrong bookings on work the workspace has
0 days
of session record, on your device

The rule that survived every rewrite

The original matcher read one thing: the window title. It now reads more — the browser’s domain and the first section of the path, the application name, whatever domains and keywords you have declared on a project. That sounds like a straightforward expansion of evidence, and it is the most dangerous change in the whole arc, because those signals identify a place and not a piece of work. Being on your client’s domain says which project you are in. It says nothing whatsoever about which task.

Location can produce a question. Only the words in the window can produce a booking.
Written into the code as an invariant, and tested as one.
Figure 5 · What each channel is allowed to do
You pinned a task
A deliberate human statement
may book hours
Words in the window title
“Fix the invoice sync” vs your task list
may book hours
A task open inside TimerOS
The dashboard says what is on screen
may book hours
The address bar, or the app
play.google.com /console · figma.exe
may only ask
Resemblance to the project
On-device similarity, no shared word
may only ask
None of the above
Nothing recognised this window
stays unassigned
Read top to bottom: the first channel that answers, wins. A channel in the lower half can put a one-click chip on the timer bar and can rank what that chip offers, but it can never file an hour by itself.

The reason this is a hard rule and not a preference is that we broke it, twice, and both times it cost real hours. A project named after the product meant the product’s name appeared in every tab title in the workspace, where it quietly served as the second “independent” word that a confident booking requires. And an association learned from someone’s clicks — two confirmations on github.com — started booking personal repository browsing. Learned signals no longer book anything. They rank a question you still have to answer.

One word is not proof

The clearest evidence for all of this came from measurement rather than reasoning. Over nineteen hours of real use, the timer raised 379 questions. Of those, 264 came from a single ordinary word: monitor on a status-monitoring site, console on a scheduling service, model on GitHub, links in a search console. All of them were offered the same unrelated project.

The logic that produced them was not obviously wrong. A word appearing in exactly one of your tasks is rare, and rare looks like evidence — it is what makes ticket numbers work. But rare inside your workspace is not the same as meaningful out in the world, and naming a project now takes two distinct words. The cases that were already working had two all along; the 264 did not.

Why there is a session record at all
None of the above is available from a bug report. The desktop app keeps a local record of what it saw, what it decided, why, and what you did about it — capped at 30 days, pruned automatically, never transmitted. It is what turns “it feels wrong sometimes” into 264 of 379, which is a fixable statement.

When there are no words at all

Most windows share no word with any task. A page can be plainly about your Android work and say nothing your task list has ever heard of, and every mechanism above will — correctly — stay silent. The last release in this arc added a channel for exactly that case.

The desktop app now carries a small open-source language model, MIT-licensed and bundled inside the installer, that turns text into a vector. Each of your projects gets one, built from its task titles; the page on screen gets one; the app compares them. When a page is near one project and clearly farther from the rest, that project is offered. It is roughly 17 MB of tables and a dot product — no server, no GPU, no request leaving the machine.

What makes it usable is the discipline around it, all of which was fixed before a line of it shipped. It never books. It never overrides a project the words already named. It cannot speak more than once an hour about any one website or application, because it fires precisely where nothing else could, so there is nothing available to check it — the rate limit is the check. Across all 190 pairings of the built-in project plans, 99.9% of the projects it named this way were the right one, with a wrong chip on 0.4% of pages about work the workspace does not contain.

A note on language, since we are strict about this elsewhere: the token matcher is not AI. It is a deterministic scoring function, and it behaves identically whether your tracking mode is Automatic or Rule-based. The resemblance channel is a model, and runs in Automatic mode only — which is what the rule-based mode is for.

What it still gets wrong

  • 5.6% is not zero, and the remaining cases are not one threshold away. They are windows describing work that genuinely resembles work you have, which no fence can separate without also refusing correct answers. The next lever is a bigger vocabulary to explain windows with, not a tighter bar.
  • A booking still consults no location. A page inside your client’s console can book a task from another project on one strong word, because the tier that books deliberately never learned to read the address bar. It is a known gap with a failing test sitting on it.
  • The first minute of any window is silent. Booking waits sixty seconds of sustained agreement; a question waits ten. That is deliberate and it does read as slowness.
  • Declared signals are the cheapest fix and nothing asks you for them. Telling a project which domains and applications belong to it beats every inference in this post, and it is a field on the project that most people never find. That is a product failure rather than a matcher one, and it is the one on this list we find hardest to defend.
Where to find it
Update the Windows desktop app to v1.9.0 or later. Matching is on by default and switchable off in Settings; the timer bar shows what it thinks you are working on, and the Task time panel on the Performance page is where you review, reassign or unassign anything. Included on every plan, Freelancer up, with no tier gate. Download TimerOS for Windows or start a trial.
Send us the window that fooled it
Every improvement above came from one person’s real screen, not from a benchmark. If the timer is booking to the wrong task, or sitting out work it should obviously have caught, the window title and the task name are enough to reproduce it — [email protected].

The thing we would keep, if we had to keep one lesson from the week: a threshold with no provenance is a guess with a decimal point on it. Ours had provenance now — it is derived, it is written down, and when the workspace cannot support one, the app says so instead of pretending.

See it on your own machine.

Fourteen-day trial — add a card at signup, nothing is charged if you cancel before it ends. Install the desktop app and watch a day classify itself.