Repository navigation
feat: seamless time tracking with a global log surface and draft worklogs - #506
Conversation
…ng (GitHub #496) Add a persisted, user-owned draft worklog model and let timers run without a project. On stop the server books directly when booking information is complete and otherwise persists a recoverable draft. - time_worklog_drafts and time_worklog_bookings tables (SQLite + Postgres) - nullable active_timers.project_id with a SQLite table rebuild migration - TimeWorklogDraftRepository: ownership-scoped CRUD, atomic book with optimistic version, idempotent booking replay - TimerService: optional project/description start, book-or-draft stop with retry recovery; v1 keeps its frozen complete-only contract - v2: stop body identifies the timer, draft CRUD/book endpoints, AI tool schema and response updates
…ery (GitHub #496) Move time logging onto one global surface available from every route and shell, and wire the new book-or-draft timer lifecycle through the UI. - timeLogStore + TimeLogHost mounted once in MainAppOverlays and MobileShell - command palette, item detail, /time, mobile, and hotkeys open the modal in place; dead focus-time-entry-form/item-detail-show-time-entry events removed - Time & Projects sidebar entry becomes a dropdown with a start/stop timer, project search, drafts badge, and navigation group - timer stop books silently when complete and opens the prefilled modal when it produces a draft; FloatingTimer, palette, and mobile share one helper - drafts card on the desktop Time view and mobile timer view; TimeLogModal gains a draft/complete mode with discard
- register the draft PATCH/DELETE/book operations in the audit spec so mutating-route coverage passes - restore the legacy per-id stop helper used by regression tests - resolve an idempotent booking replay before the draft-existence check so a retried book returns the original worklog - skip the booking-permission probe when no permission service is wired
|
Size Change: 0 B 🆕 Total Size: 0 B |
Review: changes requestedReviewed head 1dbc20d. Four findings:
Validation: Existing focused service and HTTP tests passed; two added HTTP regression checks reproduced findings 1 and 3. Browser and PostgreSQL suites were not run. The regression checks were added only in an isolated test worktree; no production changes were made. Posted as a comment because GitHub does not allow a formal request-changes review from the PR author's account. |
…mer identity - book a draft only when the caller may book on the project; a nonmember now gets the same 404 as a direct worklog create instead of booking - merge draft PATCH fields with the stored draft so omitted date/duration are preserved; allow a preserved sub-minute interval on the draft path - pass the displayed timer id to the stop call so a stale tab cannot end a newer timer - reset the time-log picker and draft caches on identity change and ignore in-flight responses from a previous account
|
All four findings addressed in 91e0d79 (core) and 19bef255 (core-tests).
Re-ran: |
Re-review of 91e0d79The project booking authorization fix and intended-timer ID fix check out. The identity reset and merge-PATCH fixes still have two gaps:
Validation: The updated focused service/HTTP suite passes. The original restricted-project HTTP regression now passes; the original immediate-stop PATCH regression still fails. Frontend checks pass for stale picker-response rejection and passing the displayed timer ID, but fail for an in-flight draft open crossing reset. Browser and PostgreSQL suites were not run. Commands run from the isolated review directory: ./core-tests-pr506-r2/overlay.sh ./core-pr506-r2 -- -tags=test -count=1 -run "Test(StopActiveTimer|DraftOwnership|BookDraft|UpdateDraft|DraftsInvisible|V2TimerDraft|V2Draft)" ./internal/services/... ./tests/...
./core-tests-pr506/overlay.sh ./core-pr506-r2 -- -tags=test -count=1 -run TestPR506Review ./tests/...
VITEST_RUNTIME=bun ./core-tests-pr506-r2/run-overlay-script.sh ./core-pr506-r2 scripts/run-frontend-tests.sh src/lib/stores/timeLogStore.review.test.js src/lib/stores/timerStore.test.jsReview-only regression checks remain in isolated test worktrees; no production changes were made. |
… intervals - openDraft checks the identity generation after its fetch so a logout/login during the request cannot reopen the previous account's draft - draft validation permits a preserved start_time == end_time interval, which a timer stopped within its starting second legitimately produces; finalized worklogs still require a positive span
|
Both gaps fixed in 8c817e2 (core) and 82749c08 (core-tests).
Re-ran |
The active-timer poll ran every 30s even in background tabs. Guard it with the shared canRunBackgroundSync() check and resync once when the page becomes usable again.
Closes #496.
Time logging was blocking: the command palette's log-time entries navigated to
/time(their events had no listeners), the timer stop silently booked aworklog, and a timer could not start without a project. This makes time logging
one in-place surface and lets the server fall back to a recoverable draft when
booking information is incomplete.
Behavior
TimeLogHostis mounted in the desktop andmobile shells. The command palette, item detail,
/time, mobile, and thehotkey open the modal in place instead of navigating.
active timer, then the last-used project.
at start; supplied associations are still authorization-checked.
a description) books silently. Otherwise the server persists a user-owned
draft worklog and the modal opens prefilled to complete it.
Drafts (N)badge in the Time & Projects sidebar menu,plus a drafts card on the desktop Time view and the mobile timer view.
timer, project search, drafts badge, and navigation group.
Server
time_worklog_draftsandtime_worklog_bookingstables (SQLite + Postgres),with an optimistic
versionand a per-source-timer unique index.active_timers.project_idis now nullable, with a SQLite table-rebuildmigration that preserves running timers and the timer-per-user unique index.
same 404 as a missing one. Booking consumes exactly one owned draft atomically
and replays idempotently by caller key.
incomplete v2 timer gets a validation error and the timer is retained.
/time/worklog-drafts(list/get/patch/delete/book); the stop bodyidentifies the intended timer and a retry recovers the original result.
start_timeraccepts an optional project/description;stop_timerreports booked vs draft.
Tests
Go tests live in the sibling
core-testsrepo on the matchingfeat/496-seamless-time-trackingbranch. Coverage: migration upgrade (runningtimers preserved, nullability, uniqueness, draft tables), book-or-draft stop,
retry recovery, draft ownership 404s, idempotent booking replay, optimistic
version conflicts, and black-box v2 draft lifecycle/ownership.
Not yet covered: Playwright browser tests for the shell integration and the
sidebar submenu.