feat: implement work add, work check, issue daily-sync (PR 5/5) #16

Merged
David merged 1 commit from feat/work-and-daily-sync into main 2026-05-14 00:24:31 +02:00
Owner

Summary

PR 5 of 5. Finishes the YouTrack subcommand port: work add, work check, and issue daily-sync are now wired end-to-end. With this PR every command surface from the Go CLI is implemented.

New api helpers (src/yt/api.rs)

  • add_work_item: POST {duration:{presentation:"Xm"}, text} to /api/issues/<id>/timeTracking/workItems. Rejects 0 minutes and empty description before sending.
  • check_work: list for:me issues, fetch each issue's work items with a minimal date-only field selector, return <id>: <summary> rows for issues with no work in the [today_start_ms, today_end_ms) window. The window is a parameter so tests can drive the comparison from any clock; the work check command supplies UTC midnight.
  • daily_sync: orchestrates work -> comment -> state in order. Returns Vec<String> of completed actions on success; on partial failure returns DailySyncFailure { actions, error } so the caller can render what landed before the failure point.

Command modules

  • work add <id> <minutes> <description...>: joins description words, rejects 0 minutes, prints Work item added successfully..
  • work check [--json]: computes today's UTC window via OffsetDateTime::now_utc().replace_hour(0)... (matches Go's Truncate(24h)). Prints All issues have work logged for today. or - <id>: <summary> per row. --json emits the array.
  • issue daily-sync <id> [--minutes N] [--comment T] [--state S] [--work W]: maps the four flags into DailySyncOptions. On success: Daily sync completed for <id>. + action lines. On partial failure: Partial sync for <id>: + completed actions, then the underlying error (non-zero exit).

Verification

  • cargo fmt --check clean.
  • cargo clippy --all-targets -- -D warnings clean.
  • cargo test --all-targets: 136 tests pass (24 new on this branch).
  • No "not yet implemented" strings remain in src/.

Test coverage on this PR

  • Wiremock body_json matchers assert the exact request shape for every helper (work item duration string, daily-sync sequence, etc).
  • Input-validation paths: minutes=0, empty description, nothing-to-do, minutes-without-description.
  • Partial-failure path: daily_sync returns actions = ["logged 30 minutes"] plus the 500 error when the comment POST fails after work succeeds.
  • check_work skips an issue whose work-item sub-fetch fails (logged via tracing) rather than aborting.
  • today_utc_window_ms invariant: 24h length, midnight alignment.

Behavioral notes

  • work check uses UTC midnight as the "today" boundary, matching Go's Truncate(24h). Local-time alignment is out of scope here but called out for follow-up if a user complains about late-evening edits being counted toward the wrong day.
  • daily-sync on partial failure both prints the actions that landed AND exits non-zero, so scripts wrapping it can see the error code while operators see what state the issue is in.

Sequence

This is PR 5 of 5. The 5-PR sequence (PRs #12, #13, #14, #15, this one) ports every stubbed subcommand to a real implementation backed by the YouTrack API. CLAUDE.md is updated to drop the "milestone 1" status line.

## Summary PR 5 of 5. Finishes the YouTrack subcommand port: `work add`, `work check`, and `issue daily-sync` are now wired end-to-end. With this PR every command surface from the Go CLI is implemented. ## New api helpers (`src/yt/api.rs`) - `add_work_item`: POST `{duration:{presentation:"Xm"}, text}` to `/api/issues/<id>/timeTracking/workItems`. Rejects 0 minutes and empty description before sending. - `check_work`: list `for:me` issues, fetch each issue's work items with a minimal `date`-only field selector, return `<id>: <summary>` rows for issues with no work in the `[today_start_ms, today_end_ms)` window. The window is a parameter so tests can drive the comparison from any clock; the `work check` command supplies UTC midnight. - `daily_sync`: orchestrates work -> comment -> state in order. Returns `Vec<String>` of completed actions on success; on partial failure returns `DailySyncFailure { actions, error }` so the caller can render what landed before the failure point. ## Command modules - `work add <id> <minutes> <description...>`: joins description words, rejects 0 minutes, prints `Work item added successfully.`. - `work check [--json]`: computes today's UTC window via `OffsetDateTime::now_utc().replace_hour(0)...` (matches Go's `Truncate(24h)`). Prints `All issues have work logged for today.` or `- <id>: <summary>` per row. `--json` emits the array. - `issue daily-sync <id> [--minutes N] [--comment T] [--state S] [--work W]`: maps the four flags into `DailySyncOptions`. On success: `Daily sync completed for <id>.` + action lines. On partial failure: `Partial sync for <id>:` + completed actions, then the underlying error (non-zero exit). ## Verification - `cargo fmt --check` clean. - `cargo clippy --all-targets -- -D warnings` clean. - `cargo test --all-targets`: 136 tests pass (24 new on this branch). - No `"not yet implemented"` strings remain in `src/`. ## Test coverage on this PR - Wiremock body_json matchers assert the exact request shape for every helper (work item duration string, daily-sync sequence, etc). - Input-validation paths: minutes=0, empty description, nothing-to-do, minutes-without-description. - Partial-failure path: `daily_sync` returns `actions = ["logged 30 minutes"]` plus the 500 error when the comment POST fails after work succeeds. - `check_work` skips an issue whose work-item sub-fetch fails (logged via tracing) rather than aborting. - `today_utc_window_ms` invariant: 24h length, midnight alignment. ## Behavioral notes - `work check` uses UTC midnight as the "today" boundary, matching Go's `Truncate(24h)`. Local-time alignment is out of scope here but called out for follow-up if a user complains about late-evening edits being counted toward the wrong day. - `daily-sync` on partial failure both prints the actions that landed AND exits non-zero, so scripts wrapping it can see the error code while operators see what state the issue is in. ## Sequence This is PR 5 of 5. The 5-PR sequence (PRs #12, #13, #14, #15, this one) ports every stubbed subcommand to a real implementation backed by the YouTrack API. `CLAUDE.md` is updated to drop the "milestone 1" status line.
feat: implement work add, work check, issue daily-sync
All checks were successful
Check / fmt + clippy + build + tests (pull_request) Successful in 8s
Create release / Create release from merged PR (pull_request) Has been skipped
a6734418bf
PR 5 of 5. Finishes the YouTrack subcommand port. New `yt::api` helpers: `add_work_item` posts `{duration:{presentation:"Xm"}, text}` to `/api/issues/<id>/timeTracking/workItems` and validates minutes > 0 and non-empty description before sending; `check_work` lists assigned issues via `query=for:me`, fetches each issue's work items with a minimal `date`-only field selector, and returns the `<id>: <summary>` rows for issues with no work logged inside the `[today_start_ms, today_end_ms)` UTC window (passed in by the caller for testability, defaulted to UTC midnight by `work check`); `daily_sync` orchestrates the composite work-then-comment-then-state flow, returning the action log on success and a `DailySyncFailure { actions, error }` on partial failure so callers can show what landed before the failure point. The command modules are thin: `work add` joins the multi-word description, prints `Work item added successfully.`, refuses 0 minutes upfront. `work check` computes today's UTC window via `OffsetDateTime::now_utc().replace_hour(0)` (matches Go's `Truncate(24h)`), prints either `All issues have work logged for today.` or `- <id>: <summary>` per row, supports `--json` for the array. `issue daily-sync` maps the four optional flags into `DailySyncOptions`, prints `Daily sync completed for <id>.` plus the action log on success; on partial failure, prints `Partial sync for <id>:` with the actions that completed, then surfaces the underlying error so the exit code is non-zero. Tests cover every body shape via wiremock body_json matchers, every input-validation path (minutes=0, empty description, nothing-to-do, minutes-without-description), partial-failure with action-list preservation, per-issue work-fetch failure being skipped rather than fatal in `check_work`, plus a `today_utc_window_ms` invariant test (24h length, midnight alignment). 136 tests pass; clippy clean; fmt clean. CLAUDE.md status line updated since every subcommand is now wired end-to-end. The 5-PR sequence is complete.
David merged commit 6adbe023ff into main 2026-05-14 00:24:31 +02:00
David deleted branch feat/work-and-daily-sync 2026-05-14 00:24:31 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
pandoras-box/youtrack-cli!16
No description provided.