Summary
SMSService.send() reports an SMS as sent when the modem response contains only OK and no +CMGS: <mr> submit reference. The returned SMS is saved with reference=0, which is indistinguishable from a legitimate TP-MR value of zero and breaks delivery-report correlation/debugging.
Evidence
Affected code:
callstack/sms/service.py:155-161 treats send_data(..., expect=["+CMGS:", "OK"]) as successful when the final OK is seen.
callstack/sms/service.py:163-169 initializes reference = 0 and leaves it at zero if no +CMGS: line is present.
callstack/sms/service.py:171-180 then saves/emits a sent SMS with reference=0.
callstack/protocol/executor.py:302-312 uses exact success matching, so +CMGS: 42 is not actually the success terminator; the later OK is what makes the send successful.
No-hardware reproduction from main (6926b369cdba):
PYTHONPATH=. uv run --no-project --with pyserial-asyncio python - <<'PY'
import asyncio
from callstack.events.bus import EventBus
from callstack.protocol.executor import ATCommandExecutor
from callstack.protocol.urc import URCDispatcher
from callstack.sms.service import SMSService
from callstack.transport.mock import MockTransport
async def main():
bus = EventBus()
transport = MockTransport()
service = SMSService(ATCommandExecutor(transport, URCDispatcher(bus)), bus)
transport.feed('> ')
transport.feed('OK')
sms = await service.send('5551234', 'Hello')
print('sms_reference:', sms.reference)
print('sms_status:', sms.status)
print('written_commands:', [w.strip() for w in transport.all_written])
asyncio.run(main())
PY
Actual output:
sms_reference: 0
sms_status: sent
written_commands: ['AT+CMGS="5551234"', 'Hello\x1a']
Expected behavior
For text-mode AT+CMGS, Callstack should require an explicit parsed +CMGS: <mr> line before reporting a successful sent SMS. Absence of the submit reference should be treated as a send/protocol error, not silently normalized to 0.
Important nuance: +CMGS: 0 can be a legitimate modem reference, so the fix should use a sentinel such as reference: int | None while parsing rather than using 0 as both "missing" and "real zero".
Suggested fix direction
- Parse
+CMGS: into reference: int | None = None.
- After a successful final
OK, raise SMSSendError if reference is None.
- Preserve valid
+CMGS: 0 as a real successful reference.
- Add focused tests for both
OK without +CMGS and explicit +CMGS: 0.
Acceptance criteria
send() raises SMSSendError when the submit response lacks +CMGS: <mr> even if it ends in OK.
send() still accepts and returns reference=0 when the modem explicitly returns +CMGS: 0.
- Sent-message persistence and
SMSSentEvent emission happen only after an explicit reference is parsed.
- Existing SMS prompt/body timeout and GSM 03.38 encoding tests remain green.
Verification gates
git diff --check
PYTHONPATH=. uv run --no-project --with pytest --with pytest-asyncio --with pytest-aiohttp --with pyserial-asyncio --with aiosqlite pytest tests/test_sms_service.py -q
PYTHONPATH=. uv run --no-project --with pytest --with pytest-asyncio --with pytest-aiohttp --with pyserial-asyncio --with aiosqlite pytest tests/ -q
Duplicate check
Searched existing issues/PRs for CMGS missing reference, SMS send reference 0, and OK no +CMGS. Existing issue #13 covered exact final-result matching for SMS body truncation, not submit-reference absence. I did not find an existing open issue or PR for this behavior.
Summary
SMSService.send()reports an SMS as sent when the modem response contains onlyOKand no+CMGS: <mr>submit reference. The returnedSMSis saved withreference=0, which is indistinguishable from a legitimate TP-MR value of zero and breaks delivery-report correlation/debugging.Evidence
Affected code:
callstack/sms/service.py:155-161treatssend_data(..., expect=["+CMGS:", "OK"])as successful when the finalOKis seen.callstack/sms/service.py:163-169initializesreference = 0and leaves it at zero if no+CMGS:line is present.callstack/sms/service.py:171-180then saves/emits a sent SMS withreference=0.callstack/protocol/executor.py:302-312uses exact success matching, so+CMGS: 42is not actually the success terminator; the laterOKis what makes the send successful.No-hardware reproduction from
main(6926b369cdba):Actual output:
Expected behavior
For text-mode
AT+CMGS, Callstack should require an explicit parsed+CMGS: <mr>line before reporting a successful sent SMS. Absence of the submit reference should be treated as a send/protocol error, not silently normalized to0.Important nuance:
+CMGS: 0can be a legitimate modem reference, so the fix should use a sentinel such asreference: int | Nonewhile parsing rather than using0as both "missing" and "real zero".Suggested fix direction
+CMGS:intoreference: int | None = None.OK, raiseSMSSendErrorifreference is None.+CMGS: 0as a real successful reference.OKwithout+CMGSand explicit+CMGS: 0.Acceptance criteria
send()raisesSMSSendErrorwhen the submit response lacks+CMGS: <mr>even if it ends inOK.send()still accepts and returnsreference=0when the modem explicitly returns+CMGS: 0.SMSSentEventemission happen only after an explicit reference is parsed.Verification gates
Duplicate check
Searched existing issues/PRs for
CMGS missing reference,SMS send reference 0, andOK no +CMGS. Existing issue #13 covered exact final-result matching for SMS body truncation, not submit-reference absence. I did not find an existing open issue or PR for this behavior.