Every Hour Accounted For
ForkTex Engineering · September 5, 2026 · 3 min
Most software engagements bill in hours and report in totals. A client receives a number at the end of the month and takes it on trust, because there is nothing else they could do with it.
A bare number is unauditable. It cannot be questioned, verified, or connected to anything that was delivered. It is a figure that either matches expectations or starts an awkward conversation, and neither outcome involves anyone learning anything.
So we do not ship bare numbers. Every ForkTex engagement includes hourly-grained work analytics: time recorded per project and per person, with each entry describing what was done. Not only how long it took.
This is a commitment made to clients, not an internal reporting convenience.
An hour with no activity is not an entry
The core constraint is that hours and activities travel together. An entry with a duration and no description of the work is incomplete and the system does not accept it.
That rule exists because the alternative degrades predictably. When description is optional, it is supplied under deadline pressure roughly never, and within two months the log is a column of numbers again. The value was never in the tracking. It was in the description, and making it optional is the same as removing it.
The shape of an entry is unremarkable:
{
"projectId": "…",
"date": "2026-08-14",
"hours": 6.5,
"activities": [
{ "info": "Stock ledger: FEFO consumption across partial lots" },
{ "info": "Fixed receipt totals diverging from line sums on VAT rounding" }
],
"links": [{ "url": "…", "label": "PR 412", "kind": "review" }]
}The links matter more than they look. They connect a claimed hour to something externally verifiable. a pull request, a deployment, a document. A client who wants to check does not have to ask us.
Totals are computed, never stored
Weekly, monthly, quarterly and yearly figures are aggregations over entries. No period total is written down as its own value.
This is a database design decision with a direct client-facing consequence. A stored total stops matching its entries the first time one is corrected, and entries do get corrected, because people misremember a day or log against the wrong project. The moment a stored monthly figure and the sum of its days disagree, the client is looking at two numbers with nothing to say which one is true.
Recomputing costs a query. Storing costs credibility, once, in a conversation you cannot win.
Included, never optional
Work analytics ships with every engagement. It is not an add-on, not a premium tier, and not something provided on request.
Making it conditional would turn a differentiator into a negotiation, and, more to the point, it would make the *default* engagement the opaque one. A client who does not know to ask would receive less visibility than one who does, which is precisely backwards.
The reporting side is deliberately decoupled: given a payload and a configuration, produce a document. It does not reach into a database or hold a session, so the same code path serves an API request, a scheduled monthly report, or a one-off export from a JSON file. A report is a view over data, never a separate record of it.
Why "journal"
We renamed this from "work logs". The word matters more than it sounds.
A journal is a chronological, append-only, audit-shaped record. It is the term accounting has used for centuries for exactly this structure, and it sets the right expectation about what the thing is: not a developer's private notes, but a record an auditor could read.
That framing also settles design arguments. Can an entry be edited retroactively? A journal says corrections are entries, not overwrites. Should totals be stored? A journal says the entries are the truth and everything else is derived.
What it is actually for
The obvious use is billing transparency, and that is real. But the more valuable use is that it makes a specific conversation possible.
When a client asks why something took three weeks, the answer is not a defensive summary written from memory. It is the record: these were the days, this is what was worked on, here are the pull requests. Sometimes that vindicates the estimate. Sometimes it shows the estimate was wrong, and why. An integration that was underspecified, a dependency that behaved differently than documented.
Both outcomes are useful. Neither is available to a team that reports a total.