feat(client)!: non_exhaustive API types with request initializers; Card.external_links and CardFile.uid; external links without a second request; cargo-semver-checks in CI - #27
Merged
Conversation
Every response model in `models.rs` (all but the closed `FileRef` enum) and
`KaitenError` can no longer be constructed with a struct literal or matched
exhaustively outside the crate, so adding fields and variants is a minor
change from here on. Fields stay `pub` and readable. Nothing in this
workspace needed a change — the CLI already matches errors with a wildcard
and builds models only from JSON.
BREAKING CHANGE for downstream code that wrote `Card { .. }` literals or
matched `KaitenError` without `_`.
…d/CardFilter are #[non_exhaustive] `CreateCard::new(board_id, title)` covers the two required fields (its `Default`, which produced an invalid empty request, is gone); `UpdateCard::default()` / `CardFilter::default()` stay for the all-optional ones. Optional fields are set by assignment on the value, so new ones can be added as `Option<T>` without touching any signature. The 22 literal constructions in the CLI, MCP server and tests were rewritten accordingly. BREAKING CHANGE for downstream struct literals of the three request types.
…hrough CLI and MCP
`Card.external_links` is what `GET /cards/{id}` (and `GET /cards`, for cards
that have any) embeds. `CardFile.uid` is the file's UUID on both storages —
the `uid` key on the classic one, the string `id` on the newer one — and
replaces the url parsing as the primary source of `FileRef::Uid`;
`FileRef::matches` accepts the wire uid as well, so every uid shown can be
passed back to `card file get` / `download_file`. `card file list --json`
and the MCP `FileView` now print the model's `uid` instead of a derived
one (classic files gain the key). Additive on top of the non_exhaustive
models — `cargo semver-checks` classifies it as minor.
`card view` prints the card's `external_links` and MCP `get_card` returns
them from the card response itself; `--include external_links` and
`include: ["external_links"]` stay accepted for compatibility (the CLI
value still adds the top-level `external_links` key to the `--json`
envelope), but trigger no request. `comments` remains an opt-in second
request. Tests pin `.expect(0)` on `/cards/{id}/external-links` for the
view/get paths; README and help texts follow.
…FileRef doc follows uid
…th the `breaking` label `kaiten-client`'s public API is compared with its latest crates.io release. Additive changes pass; a breaking change fails unless the PR carries the `breaking` label, which turns the check into a declared major (0.y) bump. Pull requests only, so master between a declared break and its release stays green.
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## chore/refresh-card-fixtures #27 +/- ##
===============================================================
+ Coverage 86.70% 86.94% +0.23%
===============================================================
Files 33 33
Lines 4897 4941 +44
===============================================================
+ Hits 4246 4296 +50
+ Misses 651 645 -6 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…; review follow-ups With the crates.io release as baseline every PR after a merged breaking change would fail until the release is published, and a label added after the run would not count. The job now compares the PR with its base commit (`fetch-depth: 0`) and reads the `breaking` label through `gh` when it runs. cargo-semver-checks does not report a removed derive (`CreateCard: Default` here) — documented as a blind spot to list by hand. Also: `get_card include: ["external_links"]` keeps the `[]` shape on a card without links (pinned, CLI counterpart pinned too); an empty string never becomes a file uid; docs no longer say the sections are "fetched".
dsociative
force-pushed
the
feat/26-non-exhaustive-models
branch
from
August 27, 2026 16:27
a7b2170 to
c368cd2
Compare
… releases and only ever extended; gates before every commit
…ernal_links"]` instead of removing it Same path as `--comments`: still accepted, still a no-op, marked deprecated in --help and the MCP param description, one stderr line when used; README lists the deprecated forms, all of which go away only in a CLI major.
…ecation stays in help, stderr and the MCP param description
This was referenced Aug 27, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #26, closes #24. Stacked on #25 (base branch
chore/refresh-card-fixtures; retarget tomasteronce #25 is in).What changes
#[non_exhaustive]on every response model and onKaitenError(FileRefstays a closed enum). Fields remainpuband readable; only struct literals and exhaustivematches outside the crate stop compiling. From here on, adding fields and variants is a minor change —cargo semver-checksclassifies the follow-up field additions in this very PR as minor.CreateCard::new(board_id, title)(its misleadingDefaultis gone),UpdateCard::default(),CardFilter::default(), then field assignment; all three are#[non_exhaustive], so new inputs are added asOption<T>without touching signatures. The 22 literal constructions in the CLI, MCP and tests were rewritten.Card.external_links,CardFile.uid(theuidkey on the classic storage, the stringidon the newer one).FileRef::Uidnow comes fromuid(url parsing is the fallback) andFileRef::matchesaccepts the wire uid, so every uid shown can be passed back tocard file get/download_file.card viewprints the card's links and MCPget_cardreturns them from the card response — zero extra requests (verified live with-vv: one request forcard view, two with--include comments).--include external_links/include: ["external_links"]stay accepted (CLI/MCP contracts are only extended) but are deprecated like--comments: help annotation, one stderr line, no-op; README lists the deprecated forms, removed only in a CLI major.commentsremains opt-in.uidwrappers in the CLI (FileListEntry) and MCP (FileView) replaced by the model field; docs updated (Card,CardFile,ExternalLink, crate docs, README Versioning).cargo semver-checkson pull requests against the PR's base commit — minor by default, major with thebreakinglabel, read live when the job runs (this PR carries it; probe PRs [probe — do not merge] semver gate must fail on a breaking change without the breaking label #28/[probe — do not merge] semver gate (base-commit baseline) must fail without the breaking label #29 showed the red path without it). The check against the last release stays a local step before tagging. Known blind spot documented: a removed derive (CreateCard: Defaulthere) is not reported by cargo-semver-checks 0.50.AGENTS.md(+CLAUDE.mdimporting it): the project's working rules, including the compatibility rule for the binary's contracts and the commit gate.semver
cargo semver-checks check-release -p kaiten-client --baseline-rev v0.5.0→ major, exactly two lints:struct_marked_non_exhaustive(25 structs) andenum_marked_non_exhaustive(KaitenError). With--release-type major: clean. No version bump in this PR — the release decides the number (next0.yper Cargo's rules).Migration for downstream users of
kaiten-clientCard { .. }/ other model literals → deserialize (serde_json::from_value) or read fields;match err { KaitenError::Api { .. } => .. }needs a_ =>arm.CreateCard { board_id, title, ..Default::default() }→let mut req = CreateCard::new(board_id, title); req.description = ..;(CreateCard::default()no longer exists).UpdateCard { .. }/CardFilter { .. }→::default()+ assignments.User-visible behaviour
card viewshowsExternal links:whenever the card has any;--jsoncarriesexternal_linksinside the card object.card file list --jsonprintsuidfor classic-storage files too (the API's own value); MCPFileView.uidlikewise.get_cardincludesexternal_linkswhen non-empty withoutinclude.Verification
299 tests green, clippy/fmt clean; live smoke on dstest (read-only on the frozen fixture card, mutations on the playground card:
CreateCard::new/UpdateCard/CardFilterpaths, temporary card deleted); a throwaway PR without the label proves the semver job goes red on a breaking change.