When the user taps Send and the subject or body mentions an attachment — the word "attach" or a variant (attached, attachment(s), attaching, attaches; word-bounded, case-insensitive) — but nothing is attached, a dialog asks "Need to attach anything?":
Yes — returns to the compose screen and pulses the attach button to draw the eye.
No — sends the message as-is.
Dismissing the dialog (tap outside / back) cancels the send without highlighting.
The check lives in ComposeViewModel (send() → trySend(checkAttachments)), runs only after the account/recipient validations, and is bypassed exactly once when the user answers "No". Reply prefill carries no quoted body in this app, so the scan cannot false-positive on quoted original text.
Tests
ComposeViewModelTest: prompt on body/subject mention, variant + lookalike coverage ("attack"/"attachable" don't trigger), no prompt when an attachment is present, and one test per dialog outcome (send anyway / attach instead / dismiss).
ComposeScreenTest (E2E): full flow — Send → dialog → Yes (nothing sent) → Send → No → message delivered.
Local gate: assembleDebug, testDebugUnitTest, lintDebug, ktlintCheck, detekt, and compileDebugAndroidTestKotlin all green; emulator E2E left to CI.
## Summary
Closes #79.
When the user taps **Send** and the subject or body mentions an attachment — the word "attach" or a variant (attached, attachment(s), attaching, attaches; word-bounded, case-insensitive) — but nothing is attached, a dialog asks **"Need to attach anything?"**:
- **Yes** — returns to the compose screen and pulses the attach button to draw the eye.
- **No** — sends the message as-is.
- Dismissing the dialog (tap outside / back) cancels the send without highlighting.
The check lives in `ComposeViewModel` (`send()` → `trySend(checkAttachments)`), runs only after the account/recipient validations, and is bypassed exactly once when the user answers "No". Reply prefill carries no quoted body in this app, so the scan cannot false-positive on quoted original text.
## Tests
- `ComposeViewModelTest`: prompt on body/subject mention, variant + lookalike coverage ("attack"/"attachable" don't trigger), no prompt when an attachment is present, and one test per dialog outcome (send anyway / attach instead / dismiss).
- `ComposeScreenTest` (E2E): full flow — Send → dialog → Yes (nothing sent) → Send → No → message delivered.
- Local gate: assembleDebug, testDebugUnitTest, lintDebug, ktlintCheck, detekt, and `compileDebugAndroidTestKotlin` all green; emulator E2E left to CI.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Closes #79.
When the user taps Send and the subject or body mentions an attachment — the word "attach" or a variant (attached, attachment(s), attaching, attaches; word-bounded, case-insensitive) — but nothing is attached, a dialog asks "Need to attach anything?":
The check lives in
ComposeViewModel(send()→trySend(checkAttachments)), runs only after the account/recipient validations, and is bypassed exactly once when the user answers "No". Reply prefill carries no quoted body in this app, so the scan cannot false-positive on quoted original text.Tests
ComposeViewModelTest: prompt on body/subject mention, variant + lookalike coverage ("attack"/"attachable" don't trigger), no prompt when an attachment is present, and one test per dialog outcome (send anyway / attach instead / dismiss).ComposeScreenTest(E2E): full flow — Send → dialog → Yes (nothing sent) → Send → No → message delivered.compileDebugAndroidTestKotlinall green; emulator E2E left to CI.🤖 Generated with Claude Code