Done on the tracker is a claim; the proof lives in Git
A card flipped to Done with nothing behind it. Now a work item only counts as done when its evidence hashes actually resolve in the repository.
TL;DR
A Done label means nothing without Git proof, so I stopped trusting tracker status alone. I built a gate that only allows Done when visible evidence lines with commit hashes and tags actually resolve in the repo. Strict rules like WIP limits, date handling, and separating docs keep the board honest and verifiable.
I once watched a task card flip to Done on a project tracker. Green label, checked box. Then I opened the detail view: no commit link, no release tag, just the word "done" in a comment. The honest question wrote itself: what proved it?
At the time I still believed the status column was the single source of truth. If the tracker says finished, it is finished. I even considered writing a script that just watches for the status change.
That idea died quickly. A tracker status is a claim. The proof lives in Git. So I built a gate with strict rules: a work item may only move to Done when its evidence, written as visible plain-text lines like "Bukti: <hash> — <what> (<date>)" with commit hashes and tags, actually resolves in the repository.
Why a Git hash is absolute proof
Git is a content-addressable filesystem; object identity is derived from the content itself [5]. That property is exactly why a resolvable hash works as proof: the bytes verifiably exist in the repo, verbatim, and no metadata manipulation can fake them.
The evidence has to be written somewhere the system can read. Work-item descriptions end up as visible plain text after server-side sanitization: the API auto-generates a description_stripped field [1], and the content validator runs nh3.clean against a strict allowlist [4]. Evidence lines must be visible text, never HTML comments that get swept away.
Protocol steps like list, classify, comment, activity, and relation run through MCP tool calls [2], so every interaction follows one consistent structure instead of ad-hoc scripts.
Inside the Done gate
A session starts by classifying mirror records out of WIP counting and active-task selection, using the Docs Mirror module and the docs-mirror label. The WIP hard limit sits at 2, and blocked_by chains are always reported as they are, so nothing hides inside an invisible dependency.
Porting a plan task demands both ISO dates, start_date and target_date, and I never infer them from the system clock or commit timestamps. Porting stays idempotent through a plan-path back-link, so re-running never duplicates cards.
The Done gate itself runs extract_evidence_refs over the description plus evidence comments, then calls verify_git_refs. That function is the module's single subprocess boundary, and it is read-only. One unresolved ref blocks Done automatically and names the exact hash that failed. Actual start and completion dates are read only from work-item activity timestamps, never from commit metadata that can drift from reality.
Classification matters as much as verification. Mirror records are bookkeeping, not work: they describe documents, so counting them as WIP would punish the team for its own documentation. The gate separates them at session start, and that separation is what keeps the WIP number honest.
Tested against a real repository
I wrote 59 focused tests covering the helpers against a real scratch repository, not polished mocks. The scenarios that matter are failures: a hash that does not resolve, evidence that is missing, activity that never got recorded. A later session, Task 7, verified the whole docs-mirror skill set live end-to-end.
I prefer rules this strict. Writing the commit hash into the description every time I close a task is a small daily cost. But knowing every Done card is a verifiable fact beats the productivity theater of a board that just believes itself.
If your tracker and your repository disagree about reality, decide now which one you will trust. I decided mine a while ago: status lives in the tracker, proof lives in Git, and Done only counts when the two agree.