Skip to content

fix: preserve chatMessage subject on reads and add message send --subject - #90

Merged
sionsmith merged 2 commits into
osodevops:mainfrom
amanjoshicodes:fix/message-subject
Sep 6, 2026
Merged

fix: preserve chatMessage subject on reads and add message send --subject#90
sionsmith merged 2 commits into
osodevops:mainfrom
amanjoshicodes:fix/message-subject

Conversation

@amanjoshicodes

Copy link
Copy Markdown
Contributor

Problem

ChatMessage in src/models/message.rs has no subject field, so the subject of a channel root message — which Graph returns on both GET .../messages/{id} and the list endpoint — is silently dropped during deserialization. A message posted with a subject reads back without one:

$ teams message send --team TEAM_ID --channel CHANNEL_ID --subject "Release plan" --body "Details inside."   # (not possible before this PR)
$ teams message get --team TEAM_ID --channel CHANNEL_ID MESSAGE_ID --output json | jq .data.subject
null        # v0.6.0 — Graph returns "Release plan", the CLI loses it

There was also no way to set a subject on send.

Change

  • ChatMessage gains subject: Option<String> (skip-serializing when absent, so messages without a subject gain no "subject": null noise). message get and message list now return what Graph stores.
  • SendMessageRequest gains the top-level subject, exposed as teams message send --subject TEXT. Channel sends only: chat messages have no subject, so clap rejects --subject with --chat or without --channel (exit 2) before anything is sent.
  • Docs: README, docs/command-reference.md, docs/man/teams.1, CHANGELOG entry under Unreleased. No version bump (per the release-automation convention).

Tests

  • Model round-trips: subject survives deserialize/serialize; absent subject stays omitted; SendMessageRequest serializes subject at the top level.
  • CLI regressions: message send --help advertises --subject <SUBJECT>; --subject with --chat and --subject without --channel both fail with exit 2.
  • cargo fmt -- --check, cargo clippy --all-targets --all-features -- -D warnings, cargo test --all-targets (313 tests) all green.

Live verification (real tenant, delegated auth)

Send with the patched binary, then read back three ways:

$ teams message send --team TEAM_ID --channel CHANNEL_ID --subject "CLI subject test" --body "[TESTING] ..." --output json | jq '{id: .data.id, subject: .data.subject}'
{ "id": "MESSAGE_ID", "subject": "CLI subject test" }

$ teams message get --team TEAM_ID --channel CHANNEL_ID MESSAGE_ID --output json | jq .data.subject
"CLI subject test"                  # patched binary; raw Graph GET agrees

$ teams-v0.6.0 message get --team TEAM_ID --channel CHANNEL_ID MESSAGE_ID --output json | jq .data.subject
null                                # released binary on the same message

message list on the same channel shows the subject on the patched binary as well.

@sionsmith

Copy link
Copy Markdown
Contributor

Reviewed and verified locally:

  • Reproduced the drop live: a chat message posted with a subject through raw Graph reads back with no subject on the released v0.6.0 binary and with "subject": "…" on this branch (message list --chat).
  • Microsoft's Teams messaging overview states the subject is "applicable only to messages sent within a channel", so the channel-only guard on --subject is the right call.
  • fmt, clippy (-D warnings), and the full test suite pass.

Extended the branch with one commit (from @aberoham's follow-up) so the fix reaches every output format, not just JSON: the human table gains a Subject column, and the plain TSV list now collects columns across all rows, so a subject that first appears on a later row is no longer dropped by the first-row header derivation. Tests cover both. Merging once CI is green.

sionsmith added a commit to amanjoshicodes/ms-teams-cli that referenced this pull request Sep 6, 2026
Windows gives the main thread 1 MiB; Linux and macOS give it 8 MiB.
Building clap's command tree for this many subcommands needs just under
1 MiB in an unoptimized build (measured with `ulimit -s` on 2026-09-06:
main fails below 1024 KiB on macOS too), so adding a single flag to
`message` (osodevops#90, osodevops#91) made every debug and test invocation on Windows,
`--help` included, die with `thread 'main' has overflowed its stack`,
and `cargo test --all-targets` failed on windows-latest only.

A build script now passes `/STACK:8388608` to the MSVC linker (and
`--stack` to the GNU one). This is the approach rustup takes for the
same clap behaviour (clap-rs/clap#5134), and unlike a `.cargo/config`
rustflags entry it survives CI setting `RUSTFLAGS`. The reservation is
address space, not committed memory.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WG7vFLjFZHqAUMRS1zkWRE
sionsmith added a commit that referenced this pull request Sep 6, 2026
Windows gives the main thread 1 MiB; Linux and macOS give it 8 MiB.
Building clap's command tree for this many subcommands needs just under
1 MiB in an unoptimized build (measured with `ulimit -s` on 2026-09-06:
main fails below 1024 KiB on macOS too), so adding a single flag to
`message` (#90, #91) made every debug and test invocation on Windows,
`--help` included, die with `thread 'main' has overflowed its stack`,
and `cargo test --all-targets` failed on windows-latest only.

A build script now passes `/STACK:8388608` to the MSVC linker (and
`--stack` to the GNU one). This is the approach rustup takes for the
same clap behaviour (clap-rs/clap#5134), and unlike a `.cargo/config`
rustflags entry it survives CI setting `RUSTFLAGS`. The reservation is
address space, not committed memory.


Claude-Session: https://claude.ai/code/session_01WG7vFLjFZHqAUMRS1zkWRE

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
@sionsmith
sionsmith merged commit 0874525 into osodevops:main Sep 6, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants