diff --git a/.github/workflows/benchmarks.yml b/.github/workflows/benchmarks.yml index cfda194..3284e7b 100644 --- a/.github/workflows/benchmarks.yml +++ b/.github/workflows/benchmarks.yml @@ -20,7 +20,7 @@ jobs: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup pinned development toolchain - uses: jdx/mise-action@9e7f7633ff6f6d6048a9418a68d48f288f50eb14 # v4.2.3 + uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0 with: version: 2026.7.6 install: true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 624f1f7..611f89e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,7 +68,7 @@ jobs: uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup pinned development toolchain - uses: jdx/mise-action@9e7f7633ff6f6d6048a9418a68d48f288f50eb14 # v4.2.3 + uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0 with: version: 2026.7.6 install: true diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index e28d9df..5b7b43f 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -20,7 +20,7 @@ jobs: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup mise - uses: jdx/mise-action@9e7f7633ff6f6d6048a9418a68d48f288f50eb14 # v4.2.3 + uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0 with: version: 2026.7.6 install: true diff --git a/.github/workflows/dependency-policy.yml b/.github/workflows/dependency-policy.yml index c6f6791..1d2d0e2 100644 --- a/.github/workflows/dependency-policy.yml +++ b/.github/workflows/dependency-policy.yml @@ -22,7 +22,7 @@ jobs: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup mise - uses: jdx/mise-action@9e7f7633ff6f6d6048a9418a68d48f288f50eb14 # v4.2.3 + uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0 with: version: 2026.7.6 install: true diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml index 4152886..9c24ff2 100644 --- a/.github/workflows/pages.yml +++ b/.github/workflows/pages.yml @@ -32,7 +32,7 @@ jobs: - name: Fetch Book deployment refs run: git fetch --force origin main develop --tags - name: Setup pinned book toolchain - uses: jdx/mise-action@9e7f7633ff6f6d6048a9418a68d48f288f50eb14 # v4.2.3 + uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0 with: version: 2026.7.6 install: true @@ -60,4 +60,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0 + uses: actions/deploy-pages@368f82528645a54fb793d4d04e342629a3f51346 # v5.0.1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b79653e..457f683 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -22,7 +22,7 @@ jobs: with: fetch-depth: 0 - name: Setup pinned release toolchain - uses: jdx/mise-action@9e7f7633ff6f6d6048a9418a68d48f288f50eb14 # v4.2.3 + uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0 with: version: 2026.7.6 install: true diff --git a/.github/workflows/scheduled.yml b/.github/workflows/scheduled.yml index 7f8eb7f..f0bdf0e 100644 --- a/.github/workflows/scheduled.yml +++ b/.github/workflows/scheduled.yml @@ -19,7 +19,7 @@ jobs: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup pinned development toolchain - uses: jdx/mise-action@9e7f7633ff6f6d6048a9418a68d48f288f50eb14 # v4.2.3 + uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0 with: version: 2026.7.6 install: true @@ -66,7 +66,7 @@ jobs: - name: Checkout uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 - name: Setup mise - uses: jdx/mise-action@9e7f7633ff6f6d6048a9418a68d48f288f50eb14 # v4.2.3 + uses: jdx/mise-action@c2a87611a18de5b3828c5652fe268e992400cb5c # v4.3.0 with: version: 2026.7.6 install: true diff --git a/.pinto/issued_ids b/.pinto/issued_ids index 5259cc2..9ff084a 100644 --- a/.pinto/issued_ids +++ b/.pinto/issued_ids @@ -74,3 +74,5 @@ P-73 P-74 P-75 P-76 +P-77 +P-78 diff --git a/.pinto/tasks/P-77.md b/.pinto/tasks/P-77.md new file mode 100644 index 0000000..dd3ef61 --- /dev/null +++ b/.pinto/tasks/P-77.md @@ -0,0 +1,30 @@ ++++ +id = "P-77" +title = "Independently configure sprint start and end datetimes" +status = "done" +rank = "zy" +points = 5 +start_at = "2026-09-11T09:38:00.627760Z" +done_at = "2026-09-11T10:08:31.090702Z" +created = "2026-09-11T09:32:56.920845Z" +updated = "2026-09-11T10:08:31.090702Z" ++++ + +# **Summary** + +Allow users to update a Sprint's planned start or end datetime independently through `sprint edit`, preserving the endpoint they did not supply. Keep the existing paired-period invariant for persisted Sprints and keep `sprint new` requiring both endpoints. + +# **Implementation Plan** + +- Carry independent start/end edit intent from the CLI through the service boundary. +- Merge a supplied endpoint with the existing counterpart before validating and saving. +- Add focused domain/service and CLI integration coverage for preservation and rejection paths. + +# **Acceptance Criteria** + +- [x] Updating only the start datetime changes the start datetime and preserves the existing end datetime. +- [x] Updating only the end datetime changes the end datetime and preserves the existing start datetime. +- [x] Updating both the start and end datetimes continues to succeed. +- [x] Invalid datetime input is rejected. +- [x] An update where the start datetime is later than the end datetime is rejected. +- [x] An update with neither datetime supplied preserves the existing behavior. diff --git a/.pinto/tasks/P-78.md b/.pinto/tasks/P-78.md new file mode 100644 index 0000000..a729978 --- /dev/null +++ b/.pinto/tasks/P-78.md @@ -0,0 +1,69 @@ ++++ +id = "P-78" +title = "Allow independent Sprint Edit schedule endpoints" +status = "done" +rank = "zz" +parent = "P-77" +start_at = "2026-09-11T09:38:00.892159Z" +done_at = "2026-09-11T10:05:04.876284Z" +created = "2026-09-11T09:38:00.432197Z" +updated = "2026-09-11T10:06:29.100040Z" ++++ + +# **Summary** + +Implement independently configurable start and end datetimes for `sprint edit` without weakening the persisted Sprint paired-period invariant. + +# **Planning Estimate** + +5 points. + +# **Dependencies** + +None. Existing datetime parsing, persistence, and paired-period validation are reusable. + +# **Owner** + +Sprint CLI/service implementation sub-agent. + +# **Verification** + +Focused checks passed: + +- `vfy run --json -- cargo test --all-features --locked --test cli sprint_edit_` — passed (6 tests). +- `vfy run --json -- cargo test --lib service::sprint::tests::edit_` — passed (4 tests). +- `vfy run --json -- cargo test --test cli sprint_new_start_` — passed (2 tests). +- `vfy run --json -- cargo clippy --all-targets -- -D warnings` — passed. +- `vfy run --json -- cargo fmt --all -- --check` — passed. + +The full `mise run check` gate compiled the project and reported 4 failures in pre-existing editor/template integration tests (`add`, `retro`, and `review`); no Sprint endpoint test failed. + +Code review found no issues. + +# **Completion Conditions** + +- Independent `sprint edit --start` and `sprint edit --end` updates preserve the omitted endpoint — passed. +- Invalid requests do not mutate or persist the Sprint — passed. +- Existing paired edits and `sprint new` behavior remain valid — passed. +- Focused tests pass — passed. + +# **Decision** + +Decision: Apply independent endpoint behavior to `sprint edit`, not `sprint new`. +Why: The Acceptance Criteria specify updating, while new Sprints have no counterpart to preserve and the persisted model requires a complete period. +Next verification: Focused CLI and service tests exercise endpoint preservation and reject incomplete or inverted effective periods. + +# **Checkpoint** + +What differed from the plan: The service schedule intent uses a seven-argument tuple alias to satisfy the repository's clippy argument-count policy while preserving independent endpoints. +What blocks the next work: Nothing in the selected SBI; the broad gate remains red on four unrelated existing editor/template tests. +Plan, split, or Flow Mode change: No change; the one-SBI Standard split remains sufficient. + +# **Residual Risks and Unverified Items** + +- The full project gate is not green because of unrelated existing integration-test failures. +- Demo data and the repository-wide gate were not added or rerun because the human requested only implementation and tests, not the full Sprint/DoD ceremony. + +# **Risks** + +The existing commit-failure durability caveat in the service contract is unchanged. diff --git a/CHANGELOG.md b/CHANGELOG.md index af53d42..bd7b97e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,16 @@ and releases use [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +## [0.4.4] - 2026-09-11 + +This patch release improves Sprint schedule editing without changing the +persisted paired-period invariant or the existing `sprint new` contract. + +### Fixed + +- Allowed `sprint edit --start` and `--end` to be updated independently while + preserving the endpoint that was not supplied. + ## [0.4.3] - 2026-07-31 This patch release adds Sprint records and strengthens board integrity, diff --git a/Cargo.lock b/Cargo.lock index 5158f5e..7f35fb5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 4 [[package]] name = "aho-corasick" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" dependencies = [ "memchr", ] @@ -19,9 +19,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "android_system_properties" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" dependencies = [ "libc", ] @@ -171,9 +171,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.13.0" +version = "2.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +checksum = "3ded4057c258ba199e2d26386d3af3780957ecaee6c4ef4041c6b4b8b97c0b06" [[package]] name = "block-buffer" @@ -186,9 +186,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.12.3" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cee35f73844aa3014bb606320a6c1f010249dbdf43342fe54b5a4f6a8ed4b79" +checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f" dependencies = [ "memchr", "regex-automata", @@ -209,15 +209,15 @@ checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" [[package]] name = "bytemuck" -version = "1.25.0" +version = "1.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" [[package]] name = "bytes" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" [[package]] name = "castaway" @@ -230,9 +230,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.65" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" +checksum = "005ec2760ca554fae18df7a11195552ec576cd665632a881bc011d5bb2fd4d80" dependencies = [ "find-msvc-tools", "shlex", @@ -246,9 +246,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" [[package]] name = "chrono" @@ -266,9 +266,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.4" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d91e0c145792ef73a6ad36d27c75ac09f1832222a3c209689d90f534685ee5b7" +checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca" dependencies = [ "clap_builder", "clap_derive", @@ -276,9 +276,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.6.2" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f09628afdcc538b57f3c6341e9c8e9970f18e4a481690a64974d7023bd33548b" +checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889" dependencies = [ "anstream", "anstyle", @@ -288,9 +288,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.6.8" +version = "4.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f84a88507dbd05c695f2cb5e8558e747179134005e9893882dec964190ed89" +checksum = "3be2ad0423bdbbb0e25bc89add796f3559706d4a95e1bc98e4d9662a957b6a19" dependencies = [ "clap", ] @@ -304,7 +304,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.5", ] [[package]] @@ -383,9 +383,9 @@ checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" [[package]] name = "crokey" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04a63daf06a168535c74ab97cdba3ed4fa5d4f32cb36e437dcceb83d66854b7c" +checksum = "074bc31bff1084f74e5a145ad5f6ac74469fa312159faa5144f0b1c4506b8d80" dependencies = [ "crokey-proc_macros", "crossterm", @@ -396,22 +396,22 @@ dependencies = [ [[package]] name = "crokey-proc_macros" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "847f11a14855fc490bd5d059821895c53e77eeb3c2b73ee3dded7ce77c93b231" +checksum = "b88c4ff2d5717616c3bb4a31d06b0b241ed811c7c0c41d077d77631bee7caad0" dependencies = [ "crossterm", "proc-macro2", "quote", "strict", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] name = "crossbeam" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8" +checksum = "e71406cd8807725f7ac2f999a4cdd32e98f829fdf65f528343cebf945e41df1e" dependencies = [ "crossbeam-channel", "crossbeam-deque", @@ -422,18 +422,18 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.16" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e" +checksum = "98b0cc327b5bc766e7fda9c9260cc0fa81b43a8e240440422dff70788e3f9ef1" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +checksum = "622f3fc73690be383c7214310406f28a90e6edeadc3cea882f9d71e495b9711a" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -441,27 +441,27 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.20" +version = "0.9.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" +checksum = "dc74980687109a3b14c72fd458107bf0baa1da1a1a805e178d15501ba9b86d9d" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-queue" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "803d13fb3b09d88be9f4dbc29062c66b19bf7170867ceb746d2a8689bf6c7a26" +checksum = "03e8bd762f7479489c70ed6c768ddca99d7296857de437a68dcb2a94365b3fae" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.21" +version = "0.8.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "a31eee39dddec8330830986fcd7625edb5a24ec90ea038215273bbc3adb08ac6" [[package]] name = "crossterm" @@ -469,7 +469,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.2", "crossterm_winapi", "derive_more", "document-features", @@ -512,9 +512,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.23.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +checksum = "ed17f5901b6630b993ca003def43f2f8ef4014fc13b047b57aad617ff32bc2ec" dependencies = [ "darling_core", "darling_macro", @@ -522,26 +522,26 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.23.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +checksum = "6837e2cf7485aaae18f86181d2f0e9a7ed297a025e220aeabf63fdebd3a2ddff" dependencies = [ "ident_case", "proc-macro2", "quote", "strsim", - "syn 2.0.118", + "syn 3.0.5", ] [[package]] name = "darling_macro" -version = "0.23.0" +version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +checksum = "2ac7135c3ef02b2f7833bbeb1be5ba7f966dcde8a87c6b87f65a778d71a02785" dependencies = [ "darling_core", "quote", - "syn 2.0.118", + "syn 3.0.5", ] [[package]] @@ -575,7 +575,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -596,13 +596,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.5", ] [[package]] @@ -616,9 +616,9 @@ dependencies = [ [[package]] name = "either" -version = "1.16.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" +checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" [[package]] name = "endian-type" @@ -644,9 +644,9 @@ dependencies = [ [[package]] name = "error-code" -version = "3.3.2" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" +checksum = "0b5343afd4a8365a643ac588dab4cf234a190c7f6c88c9f6dd6ffe00837661b7" [[package]] name = "euclid" @@ -679,17 +679,11 @@ dependencies = [ "regex", ] -[[package]] -name = "fast-srgb8" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" - [[package]] name = "fastrand" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" [[package]] name = "filedescriptor" @@ -704,9 +698,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.9" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "3e0f1c7c3a72c66fd80abe965175f7523475c0489a87d3ff9d6e8c87d87a9d2d" [[package]] name = "finl_unicode" @@ -761,7 +755,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54f0d287c53ffd184d04d8677f590f4ac5379785529e5e08b1c8083acdd5c198" dependencies = [ "memchr", - "thiserror 2.0.19", + "thiserror 2.0.20", ] [[package]] @@ -789,21 +783,21 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" [[package]] name = "futures-task" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" [[package]] name = "futures-util" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-core", "futures-task", @@ -868,11 +862,11 @@ dependencies = [ [[package]] name = "hashlink" -version = "0.11.1" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "824e001ac4f3012dd16a264bec811403a67ca9deb6c102fc5049b32c4574b35f" +checksum = "a596f1b20ed2cc5ecac41a164aaebc7258057060f06c0cf7a2ba3991ee7990fb" dependencies = [ - "hashbrown 0.16.1", + "hashbrown 0.17.1", ] [[package]] @@ -928,9 +922,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "indexmap" -version = "2.14.0" +version = "2.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +checksum = "cc4e190f5d26ca7051642629da2c52fc03bde85a03197c99408dcd291734c855" dependencies = [ "equivalent", "hashbrown 0.17.1", @@ -947,15 +941,15 @@ dependencies = [ [[package]] name = "instability" -version = "0.3.12" +version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5eb2d60ef19920a3a9193c3e371f726ec1dafc045dac788d0fb3704272458971" +checksum = "2bf84e73fa6f27f299dec58e13223cf70db80da872eb921d4f6138342a0eabc8" dependencies = [ "darling", "indoc", "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.5", ] [[package]] @@ -1000,9 +994,9 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.103" +version = "0.3.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +checksum = "ce57d20d1ea864ce2ac172ab472d409214f4fd359f0b2a2775abdf522e2af99e" dependencies = [ "cfg-if", "futures-util", @@ -1017,7 +1011,7 @@ checksum = "bde5057d6143cc94e861d90f591b9303d6716c6b9602309150bd068853c10899" dependencies = [ "hashbrown 0.16.1", "portable-atomic", - "thiserror 2.0.19", + "thiserror 2.0.20", ] [[package]] @@ -1028,9 +1022,9 @@ checksum = "bf36173d4167ed999940f804952e6b08197cae5ad5d572eb4db150ce8ad5d58f" [[package]] name = "lazy-regex" -version = "3.6.0" +version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bae91019476d3ec7147de9aa291cadb6d870abf2f3015d2da73a90325ac1496" +checksum = "4994ba703f78b083e2f7946dac9251abd83fd43a0365f030e99b69be5b4b9ef9" dependencies = [ "lazy-regex-proc_macros", "once_cell", @@ -1039,14 +1033,14 @@ dependencies = [ [[package]] name = "lazy-regex-proc_macros" -version = "3.6.0" +version = "3.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4de9c1e1439d8b7b3061b2d209809f447ca33241733d9a3c01eabf2dc8d94358" +checksum = "fd97232314824e6dbef1918a871bb93f51070455e3715bf26e19a6d01aa977a0" dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1069,9 +1063,9 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libsqlite3-sys" -version = "0.37.0" +version = "0.38.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f111c8c41e7c61a49cd34e44c7619462967221a6443b0ec299e0ac30cfb9b1" +checksum = "f1d20bef17f513b9b3004532233187769cd072d790971f4e4da0e346eb6401e8" dependencies = [ "cc", "pkg-config", @@ -1080,11 +1074,11 @@ dependencies = [ [[package]] name = "line-clipping" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f50e8f47623268b5407192d26876c4d7f89d686ca130fdc53bced4814cd29f8" +checksum = "e752191d037c44ad111a8caa762921926658402f01cc1253f7bef2020ece4f5e" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.2", ] [[package]] @@ -1110,15 +1104,15 @@ dependencies = [ [[package]] name = "log" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" [[package]] name = "lru" -version = "0.18.0" +version = "0.18.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a860605968fce16869fd239cf4237a82f3ac470723415db603b0e8b6c8d4fb9" +checksum = "ff9840bcc50b71349309900da0ce7279aa336ae71d73250b07998932c7d97c25" dependencies = [ "hashbrown 0.17.1", ] @@ -1135,9 +1129,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.2" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "memmem" @@ -1171,9 +1165,9 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "mio" -version = "1.2.1" +version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +checksum = "4b18443e9c262bfe8fa82f51666e2642c53393f7e5c27b3e1aeab922cff5b9d8" dependencies = [ "libc", "log", @@ -1196,7 +1190,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.2", "cfg-if", "cfg_aliases", "libc", @@ -1209,7 +1203,7 @@ version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.2", "cfg-if", "cfg_aliases", "libc", @@ -1245,7 +1239,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1289,26 +1283,35 @@ dependencies = [ [[package]] name = "palette" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cbf71184cc5ecc2e4e1baccdb21026c20e5fc3dcf63028a086131b3ab00b6e6" +checksum = "ddeed8580d347d2abf3dcf06a5f0b3dc020258338526b277847cd4248a70fc64" dependencies = [ "approx", - "fast-srgb8", "libm", "palette_derive", + "palette_math", ] [[package]] name = "palette_derive" -version = "0.7.6" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5030daf005bface118c096f510ffb781fc28f9ab6a32ab224d8631be6851d30" +checksum = "88537020289b719d81be994ccf1bbf4990f477e2f69ee52fe3e45f43a02e56be" dependencies = [ "by_address", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", +] + +[[package]] +name = "palette_math" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e6eb142958d64335fb0e345c5b9ead2ecd6fc438c307e9d7d3c4fd428dbaf12" +dependencies = [ + "libm", ] [[package]] @@ -1336,9 +1339,9 @@ dependencies = [ [[package]] name = "pest" -version = "2.8.7" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47627dd7305c6a2d6c8c6bcd24c5a4c17dbbf425f4f9c5313e724b38fc9782e9" +checksum = "6d45aeb61b4bf818e12d4205f2466f8c4748f85f4fce0146d1c03d69d753f0ad" dependencies = [ "memchr", "ucd-trie", @@ -1346,9 +1349,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.7" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b4254325ecad416ab689e27ba51da03ba01a9632bc6e108f5fe7c3c4ad29d58" +checksum = "89cc5a242e25ed4e7704d0be240f2cfbe20a8c27e7e252d94835be93d92dc39f" dependencies = [ "pest", "pest_generator", @@ -1356,22 +1359,22 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.7" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c4c0e91ead7a8f7acecbca6f003fc2e8282b1dbe2dd9c9d2f16aba42995e0a7" +checksum = "7abf21475cc3820fe4b2ca2dc2142902f67a02189f3b5b3a229f4febc01a43e5" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] name = "pest_meta" -version = "2.8.7" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9744bc48116fee06334924bb5f2bad41eed5e89bd26e29b0b799f9a3f82c210" +checksum = "adba4db388f687393c18c51348d44a41d870ca9df71a2c98172ea3035dc6936e" dependencies = [ "pest", ] @@ -1403,7 +1406,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared", - "rand 0.8.6", + "rand 0.8.8", ] [[package]] @@ -1416,7 +1419,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1436,7 +1439,7 @@ checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "pinto-cli" -version = "0.4.3" +version = "0.4.4" dependencies = [ "anyhow", "assert_cmd", @@ -1458,7 +1461,7 @@ dependencies = [ "tempfile", "termimad", "terminal_size", - "thiserror 2.0.19", + "thiserror 2.0.20", "tokio", "toml", "unic-langid", @@ -1469,15 +1472,15 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" [[package]] name = "portable-atomic" -version = "1.13.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" [[package]] name = "powerfmt" @@ -1526,9 +1529,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] @@ -1541,9 +1544,9 @@ checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" dependencies = [ "bit-set 0.8.0", "bit-vec 0.8.0", - "bitflags 2.13.0", + "bitflags 2.13.2", "num-traits", - "rand 0.9.4", + "rand 0.9.5", "rand_chacha", "rand_xorshift", "regex-syntax", @@ -1560,9 +1563,9 @@ checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quote" -version = "1.0.46" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] @@ -1591,18 +1594,18 @@ dependencies = [ [[package]] name = "rand" -version = "0.8.6" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c" dependencies = [ "rand_core 0.6.4", ] [[package]] name = "rand" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" dependencies = [ "rand_chacha", "rand_core 0.9.5", @@ -1664,7 +1667,7 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbb175c433c8e28a809d1f5773a2ae96e68c0ce40db865cbab1020bf33ae479c" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.2", "compact_str", "critical-section", "hashbrown 0.17.1", @@ -1674,7 +1677,7 @@ dependencies = [ "palette", "serde", "strum", - "thiserror 2.0.19", + "thiserror 2.0.20", "unicode-segmentation", "unicode-truncate", "unicode-width 0.2.2", @@ -1729,7 +1732,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66e3d19bcc9130ca376277d93b60767ff121ace3be06f5f95f81dd68956407d1" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.2", "hashbrown 0.17.1", "indoc", "instability", @@ -1769,7 +1772,7 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.2", ] [[package]] @@ -1786,9 +1789,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.16" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" dependencies = [ "aho-corasick", "memchr", @@ -1808,16 +1811,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c51c9ae4df8a7fba42103df5c621fa3c37eccf3a3c650879e90fc48b11cc192c" dependencies = [ "hashbrown 0.16.1", - "thiserror 2.0.19", + "thiserror 2.0.20", ] [[package]] name = "rusqlite" -version = "0.39.0" +version = "0.40.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0d2b0146dd9661bf67bb107c0bb2a55064d556eeb3fc314151b957f313bcd4e" +checksum = "23f2a97da3e3873c73cb2a2e71b35c40ff95e0b1eefa8d72d8499a6928c3b5b3" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.2", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -1847,7 +1850,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.2", "errno", "libc", "linux-raw-sys", @@ -1856,9 +1859,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" [[package]] name = "rusty-fork" @@ -1878,7 +1881,7 @@ version = "18.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "53f6a737db68eb1a8ccff86b584b2fc13eca6a7bb6f78ebc7c529547e3ab9684" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.2", "cfg-if", "clipboard-win", "home", @@ -1907,9 +1910,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "self_cell" -version = "1.2.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" +checksum = "2ab42ca02749e120097e328d91d415325bdf43b1c72c4c8badf37375fe40a813" [[package]] name = "semver" @@ -1944,7 +1947,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.5", ] [[package]] @@ -2031,9 +2034,9 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" -version = "1.15.2" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" +checksum = "b9be42f50aa861c555654aa3a37f52f4b1074bacf4e48fe0ef7fa584e80f1f0f" [[package]] name = "sqlite-wasm-rs" @@ -2083,7 +2086,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -2099,9 +2102,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.118" +version = "2.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" dependencies = [ "proc-macro2", "quote", @@ -2110,9 +2113,9 @@ dependencies = [ [[package]] name = "syn" -version = "3.0.3" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" +checksum = "12df2e0110f65b775f769bb17ef989067a1d931b2eb822bd4346631eeada89f9" dependencies = [ "proc-macro2", "quote", @@ -2134,9 +2137,9 @@ dependencies = [ [[package]] name = "termimad" -version = "0.35.1" +version = "0.35.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d53d4b1294b87e81925b7ae7f8f4d000376e3a5b3349d978429665428a793fcb" +checksum = "2374179aac8b77011a1aefe2f44d0c34eb680c9b4bd1c369a88991262c60c85a" dependencies = [ "coolor", "crokey", @@ -2144,7 +2147,7 @@ dependencies = [ "lazy-regex", "minimad", "serde", - "thiserror 2.0.19", + "thiserror 2.0.20", "unicode-width 0.1.14", ] @@ -2154,7 +2157,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9048a889effe34a5cddee0af7f53285198b16dca3be510858d38dfdb3e62a04e" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.2", "parking_lot", "rustix", "signal-hook", @@ -2206,7 +2209,7 @@ checksum = "4676b37242ccbd1aabf56edb093a4827dc49086c0ffd764a5705899e0f35f8f7" dependencies = [ "anyhow", "base64", - "bitflags 2.13.0", + "bitflags 2.13.2", "fancy-regex", "filedescriptor", "finl_unicode", @@ -2251,11 +2254,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.19" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" dependencies = [ - "thiserror-impl 2.0.19", + "thiserror-impl 2.0.20", ] [[package]] @@ -2266,25 +2269,25 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] name = "thiserror-impl" -version = "2.0.19" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", - "syn 3.0.3", + "syn 3.0.5", ] [[package]] name = "time" -version = "0.3.53" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" dependencies = [ "deranged", "libc", @@ -2303,9 +2306,9 @@ checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "tinystr" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" dependencies = [ "displaydoc", "serde_core", @@ -2329,20 +2332,20 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.7.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.5", ] [[package]] name = "toml" -version = "1.1.3+spec-1.1.0" +version = "1.1.6+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53c96ecdfa941c8fc4fcaed14f99ada8ebed502eef533015095a07e3301d4c3c" +checksum = "920602543f0911ab71da12c50d59701da54c196d1a2bf5cb4b75667f137a406a" dependencies = [ "indexmap", "serde_core", @@ -2364,9 +2367,9 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.1.2+spec-1.1.0" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" dependencies = [ "winnow", ] @@ -2465,9 +2468,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.23.4" +version = "1.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" +checksum = "2ef6dac1e96601b4fb3acccccff2139741fcb757cb9a36089bf5be91cfb285ce" dependencies = [ "atomic", "getrandom 0.4.3", @@ -2522,9 +2525,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.126" +version = "0.2.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +checksum = "aecb87a33d3b0c5e3b7aa46336eaf486cffafbd281b195e4c8b80d50df2351bf" dependencies = [ "cfg-if", "once_cell", @@ -2535,9 +2538,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.126" +version = "0.2.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +checksum = "a690d511e3c1a8b3a55e33511e3c2c00c78415cd23650f32b808627f5696b9ed" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -2545,22 +2548,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.126" +version = "0.2.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +checksum = "411e4887f0071ef2d2164a9d5fdf2d20efbef78fccd3a78b0c10a1dc5295e48a" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.5", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.126" +version = "0.2.128" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +checksum = "81941cd78d0c92026c33e5e01312845a4cb1e9af3407f9134b100dd03144103e" dependencies = [ "unicode-ident", ] @@ -2680,7 +2683,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -2691,7 +2694,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -2729,9 +2732,9 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" [[package]] name = "wit-bindgen" @@ -2741,9 +2744,9 @@ checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" [[package]] name = "yaml-rust2" -version = "0.11.0" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "631a50d867fafb7093e709d75aaee9e0e0d5deb934021fcea25ac2fe09edc51e" +checksum = "c6edb26322e610d4f04b7cd34478317685d24d0999437e551fb97c5441151041" dependencies = [ "arraydeque", "hashlink", @@ -2751,22 +2754,22 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.54" +version = "0.8.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19" +checksum = "d35102a9f36d089ccae9e4c6802bc118be4487b80aaffc0ab4e0cf5ce92d2873" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.54" +version = "0.8.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5" +checksum = "146c01f5ab44258da43cf276c74a2763db2ff3969c9c652c3f2de07041d0b2bc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -2777,9 +2780,9 @@ checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" [[package]] name = "zerovec" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" dependencies = [ "serde", "zerofrom", @@ -2787,6 +2790,6 @@ dependencies = [ [[package]] name = "zmij" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/Cargo.toml b/Cargo.toml index 81757ce..9e38392 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pinto-cli" -version = "0.4.3" +version = "0.4.4" edition = "2024" rust-version = "1.89" description = "A lightweight, local-first, Git-friendly Scrum backlog and Kanban board for the CLI and TUI" @@ -46,7 +46,7 @@ rustyline = "18.0.1" # system-library dependency; it remains outside the default build. Keep the # 0.39 series while Rust 1.89 is the MSRV: 0.40 pulls a build script that uses # the unstable `cfg_select` macro. -rusqlite = { version = "0.39.0", features = ["bundled"], optional = true } +rusqlite = { version = "0.40.2", features = ["bundled"], optional = true } serde = { version = "1.0.228", features = ["derive"] } serde_json = "1.0.145" # Small crate used only to obtain terminal width (Unix reuses transitive rustix). @@ -95,4 +95,4 @@ proptest = "1.9.0" # the archived `serde_yaml`. `default-features = false` drops the `encoding` # feature (and its `encoding_rs` dependency): the checks parse UTF-8 `&str` # inputs, so charset detection is unnecessary. See docs/dependencies.md. -yaml-rust2 = { version = "0.11.0", default-features = false } +yaml-rust2 = { version = "0.12.0", default-features = false } diff --git a/README.md b/README.md index c3e13a0..87d3a5d 100644 --- a/README.md +++ b/README.md @@ -19,10 +19,10 @@ plain text so that every change is easy to inspect and recover with Git. ## Installation -The latest published release is `0.4.3`. Install it from crates.io: +The latest published release is `0.4.4`. Install it from crates.io: ```bash -cargo install pinto-cli --version 0.4.3 +cargo install pinto-cli --version 0.4.4 pinto --version ``` diff --git a/deny.toml b/deny.toml index feee8a8..f43ee61 100644 --- a/deny.toml +++ b/deny.toml @@ -26,7 +26,7 @@ wildcards = "deny" highlight = "all" skip = [ { crate = "hashbrown@0.16.1", reason = "Transitive: older release still pulled alongside hashbrown 0.17." }, - { crate = "syn@2.0.118", reason = "Transitive proc-macro dependencies still require syn 2; updated clap, serde, and thiserror use syn 3." }, + { crate = "syn@2.0.119", reason = "Transitive proc-macro dependencies still require syn 2; updated clap, serde, and thiserror use syn 3." }, { crate = "unicode-width@0.1.14", reason = "Transitive: older release still pulled alongside unicode-width 0.2." }, ] diff --git a/docs/book/src/installation.md b/docs/book/src/installation.md index 24fcb0b..347d5db 100644 --- a/docs/book/src/installation.md +++ b/docs/book/src/installation.md @@ -2,10 +2,10 @@ ## Using a released package -Install the latest published 0.4.3 binary with Cargo: +Install the latest published 0.4.4 binary with Cargo: ```bash -cargo install pinto-cli --version 0.4.3 +cargo install pinto-cli --version 0.4.4 pinto --version ``` diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index 0eb774e..cc81d8c 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -1364,7 +1364,7 @@ checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "pinto-cli" -version = "0.4.3" +version = "0.4.4" dependencies = [ "anyhow", "chrono", diff --git a/scripts/verify-package.sh b/scripts/verify-package.sh index bae3831..28641ec 100755 --- a/scripts/verify-package.sh +++ b/scripts/verify-package.sh @@ -43,10 +43,17 @@ if [ "$missing_source" -ne 0 ]; then exit 1 fi +target_dir=$(cargo metadata --no-deps --format-version 1 | + sed -n 's/.*"target_directory":"\([^"]*\)".*/\1/p') +if [ -z "$target_dir" ]; then + echo "could not determine Cargo target directory" >&2 + exit 1 +fi + package_marker="$temporary_dir/package-start" touch "$package_marker" package -package_file=$(find "$root/target/package" -maxdepth 1 -type f -name 'pinto-cli-*.crate' -newer "$package_marker" -print | sort | tail -n 1) +package_file=$(find "$target_dir/package" -maxdepth 1 -type f -name 'pinto-cli-*.crate' -newer "$package_marker" -print | sort | tail -n 1) if [ -z "$package_file" ]; then echo "cargo package did not produce a .crate archive" >&2 exit 1 diff --git a/src/cli/args.rs b/src/cli/args.rs index db0cd3c..8927061 100644 --- a/src/cli/args.rs +++ b/src/cli/args.rs @@ -519,10 +519,12 @@ pub(super) enum SprintCommand { )] clear_goal_achieved: bool, /// Replacement planned start date and time (`YYYY-MM-DD` or `YYYY-MM-DDTHH:MM`, in UTC). - #[arg(long, short = 's', requires = "end", value_parser = parse_utc_datetime)] + /// When supplied alone, preserves the existing end date. + #[arg(long, short = 's', value_parser = parse_utc_datetime)] start: Option>, /// Replacement planned end date and time (`YYYY-MM-DD` or `YYYY-MM-DDTHH:MM`, in UTC). - #[arg(long, short = 'e', requires = "start", value_parser = parse_utc_datetime)] + /// When supplied alone, preserves the existing start date. + #[arg(long, short = 'e', value_parser = parse_utc_datetime)] end: Option>, }, /// Remove a sprint and release any assigned PBIs back to the backlog. diff --git a/src/cli/commands/sprint.rs b/src/cli/commands/sprint.rs index 24ca0c1..113f2ee 100644 --- a/src/cli/commands/sprint.rs +++ b/src/cli/commands/sprint.rs @@ -384,10 +384,7 @@ pub(super) async fn cmd_sprint_with_localizer( end, } => { let id: SprintId = id.parse()?; - let period = match (start, end) { - (Some(start), Some(end)) => Some((start, end)), - _ => None, - }; + let period = (start.is_some() || end.is_some()).then_some((start, end)); let sprint = edit_sprint( &dir, &id, diff --git a/src/service/sprint/lifecycle.rs b/src/service/sprint/lifecycle.rs index 2bf927b..d12b1d9 100644 --- a/src/service/sprint/lifecycle.rs +++ b/src/service/sprint/lifecycle.rs @@ -13,6 +13,8 @@ use std::path::Path; use super::SprintDeletionOptions; +type SprintEditPeriod = (Option>, Option>); + /// Create a sprint on the board in `project_dir` and return the saved [`Sprint`]. /// /// The state is [`crate::sprint::SprintState::Planned`]. `goal` is persisted after the frontmatter @@ -69,9 +71,11 @@ pub async fn create_sprint( /// Update the title, goal, and/or planned period of an existing sprint. /// -/// Fields set to `None` remain unchanged. Return [`Error::NothingToUpdate`] when no field is -/// supplied, [`Error::EmptySprintTitle`] for a blank title, [`Error::InvalidSprintPeriod`] for an -/// inverted period, or [`Error::SprintNotFound`] when the sprint does not exist. +/// Fields set to `None` remain unchanged. Supplying only one schedule endpoint preserves the +/// existing counterpart. Return [`Error::NothingToUpdate`] when no field is supplied, +/// [`Error::EmptySprintTitle`] for a blank title, [`Error::SprintPeriodIncomplete`] when the +/// effective schedule has only one endpoint, [`Error::InvalidSprintPeriod`] for an inverted period, +/// or [`Error::SprintNotFound`] when the sprint does not exist. /// /// # Errors /// @@ -83,14 +87,23 @@ pub async fn edit_sprint( id: &SprintId, title: Option, goal: Option, - period: Option<(DateTime, DateTime)>, + period: Option, goal_achieved: Option, clear_goal_achieved: bool, ) -> Result { let (_board_dir, repo, _config, _lock) = open_board_locked(project_dir).await?; let mut sprint = SprintRepository::load(&repo, id).await?; let now = Utc::now(); - if title.is_some() || goal.is_some() || period.is_some() { + let schedule_update = period.is_some(); + let period = if let Some((start, end)) = period { + match (start.or(sprint.start), end.or(sprint.end)) { + (Some(start), Some(end)) => Some((start, end)), + _ => return Err(Error::SprintPeriodIncomplete(sprint.id.clone())), + } + } else { + None + }; + if title.is_some() || goal.is_some() || schedule_update { sprint.update_details(title, goal, period, now)?; } else if goal_achieved.is_none() && !clear_goal_achieved { return Err(Error::NothingToUpdate); diff --git a/src/service/sprint/tests.rs b/src/service/sprint/tests.rs index 6df877b..3e2c02f 100644 --- a/src/service/sprint/tests.rs +++ b/src/service/sprint/tests.rs @@ -200,7 +200,7 @@ async fn edit_updates_title_goal_and_period() { &sid("S-1"), Some("Updated title".to_string()), Some("Updated goal".to_string()), - Some((start, end)), + Some((Some(start), Some(end))), None, false, ) @@ -213,6 +213,112 @@ async fn edit_updates_title_goal_and_period() { assert_eq!(edited.end, Some(end)); } +#[tokio::test] +async fn edit_preserves_omitted_period_endpoint() { + let dir = init_temp().await; + let original_start = date(2026, 8, 3); + let original_end = date(2026, 8, 14); + create_sprint( + dir.path(), + &sid("S-1"), + "Sprint 1", + None, + Some((original_start, original_end)), + ) + .await + .unwrap(); + + let edited = edit_sprint( + dir.path(), + &sid("S-1"), + None, + None, + Some((Some(date(2026, 8, 5)), None)), + None, + false, + ) + .await + .unwrap(); + assert_eq!(edited.start, Some(date(2026, 8, 5))); + assert_eq!(edited.end, Some(original_end)); + + let edited = edit_sprint( + dir.path(), + &sid("S-1"), + None, + None, + Some((None, Some(date(2026, 8, 18)))), + None, + false, + ) + .await + .unwrap(); + assert_eq!(edited.start, Some(date(2026, 8, 5))); + assert_eq!(edited.end, Some(date(2026, 8, 18))); +} + +#[tokio::test] +async fn edit_rejects_inverted_effective_period_without_mutation() { + let dir = init_temp().await; + let start = date(2026, 8, 3); + let end = date(2026, 8, 14); + create_sprint( + dir.path(), + &sid("S-1"), + "Sprint 1", + None, + Some((start, end)), + ) + .await + .unwrap(); + + let err = edit_sprint( + dir.path(), + &sid("S-1"), + Some("Changed".to_string()), + None, + Some((Some(date(2026, 8, 15)), None)), + None, + false, + ) + .await + .unwrap_err(); + assert_eq!( + err, + Error::InvalidSprintPeriod { + start: date(2026, 8, 15).date_naive(), + end: end.date_naive(), + } + ); + + let repo = FileRepository::new(dir.path().join(".pinto")); + let unchanged = SprintRepository::load(&repo, &sid("S-1")).await.unwrap(); + assert_eq!(unchanged.title, "Sprint 1"); + assert_eq!(unchanged.start, Some(start)); + assert_eq!(unchanged.end, Some(end)); +} + +#[tokio::test] +async fn edit_rejects_one_sided_effective_period() { + let dir = init_temp().await; + create_sprint(dir.path(), &sid("S-1"), "Sprint 1", None, None) + .await + .unwrap(); + + let err = edit_sprint( + dir.path(), + &sid("S-1"), + None, + None, + Some((Some(date(2026, 8, 3)), None)), + None, + false, + ) + .await + .unwrap_err(); + assert_eq!(err, Error::SprintPeriodIncomplete(sid("S-1"))); +} + #[tokio::test] async fn start_moves_planned_to_active_and_persists() { let dir = init_temp().await; diff --git a/tests/cli/common.rs b/tests/cli/common.rs index 8772cfd..edb7e8b 100644 --- a/tests/cli/common.rs +++ b/tests/cli/common.rs @@ -17,6 +17,7 @@ pub(crate) fn pinto(dir: &Path) -> Command { // own command and set `LC_ALL`/`LANG` explicitly. cmd.current_dir(dir) .env("XDG_CONFIG_HOME", dir.join("test-xdg-config")) + .env_remove("VISUAL") .env("LC_ALL", "en_US.UTF-8") .env("LANG", "en_US.UTF-8"); cmd diff --git a/tests/cli/sprint.rs b/tests/cli/sprint.rs index 6ae507f..180fa7f 100644 --- a/tests/cli/sprint.rs +++ b/tests/cli/sprint.rs @@ -366,6 +366,113 @@ fn sprint_edit_updates_goal_title_and_period_then_allows_start() { "active" ); } +#[test] +fn sprint_edit_start_preserves_existing_end() { + let dir = TempDir::new().expect("temp dir"); + pinto(dir.path()).arg("init").assert().success(); + pinto(dir.path()) + .args([ + "sprint", + "new", + "S-1", + "Planning", + "--start", + "2026-07-06", + "--end", + "2026-07-20", + ]) + .assert() + .success(); + + pinto(dir.path()) + .args(["sprint", "edit", "S-1", "--start", "2026-07-08"]) + .assert() + .success(); + + let edited = json_stdout(pinto(dir.path()).args(["sprint", "list", "--json"])); + assert_eq!(edited[0]["start"], "2026-07-08T00:00:00+00:00"); + assert_eq!(edited[0]["end"], "2026-07-20T00:00:00+00:00"); +} + +#[test] +fn sprint_edit_end_preserves_existing_start() { + let dir = TempDir::new().expect("temp dir"); + pinto(dir.path()).arg("init").assert().success(); + pinto(dir.path()) + .args([ + "sprint", + "new", + "S-1", + "Planning", + "--start", + "2026-07-06", + "--end", + "2026-07-20", + ]) + .assert() + .success(); + + pinto(dir.path()) + .args(["sprint", "edit", "S-1", "--end", "2026-07-22"]) + .assert() + .success(); + + let edited = json_stdout(pinto(dir.path()).args(["sprint", "list", "--json"])); + assert_eq!(edited[0]["start"], "2026-07-06T00:00:00+00:00"); + assert_eq!(edited[0]["end"], "2026-07-22T00:00:00+00:00"); +} + +#[test] +fn sprint_edit_rejects_inverted_effective_period_without_mutation() { + let dir = TempDir::new().expect("temp dir"); + pinto(dir.path()).arg("init").assert().success(); + pinto(dir.path()) + .args([ + "sprint", + "new", + "S-1", + "Planning", + "--start", + "2026-07-06", + "--end", + "2026-07-20", + ]) + .assert() + .success(); + + pinto(dir.path()) + .args([ + "sprint", + "edit", + "S-1", + "--title", + "Changed", + "--start", + "2026-07-21", + ]) + .assert() + .failure() + .code(1) + .stderr(predicate::str::contains("invalid sprint period")); + + let unchanged = json_stdout(pinto(dir.path()).args(["sprint", "list", "--json"])); + assert_eq!(unchanged[0]["title"], "Planning"); + assert_eq!(unchanged[0]["start"], "2026-07-06T00:00:00+00:00"); + assert_eq!(unchanged[0]["end"], "2026-07-20T00:00:00+00:00"); +} + +#[test] +fn sprint_edit_rejects_malformed_schedule_input() { + let dir = TempDir::new().expect("temp dir"); + pinto(dir.path()).arg("init").assert().success(); + + pinto(dir.path()) + .args(["sprint", "edit", "S-1", "--start", "not-a-date"]) + .assert() + .failure() + .code(1) + .stderr(predicate::str::contains("invalid value")); +} #[test] fn sprint_edit_rejects_empty_title_and_no_fields_without_mutation() {