minorsecond / minorsecond/AXTerm
Connected Chat “Atomic Messages” + Outbox Queue
Nobody has claimed this yet.
- Dominant language
- Swift
- Stars
- 0
- Forks
- 0
- Avg merge
- 4d 2h
- Merged PRs (30d)
- 1
Description
Goal
Fix the classic packet-radio annoyance where back-to-back sends feel glued together, ambiguous, or out of order. In AXTerm, user sends must be treated as atomic “messages” with a per-session outbox queue, explicit send/delivery state, and a clean “send line vs send block” compose UX — while remaining 100% interoperable with legacy stations.
Non-Goals
- Do not require protocol changes on the remote station.
- Do not implement encryption.
- Do not require AXDP support (but design should be compatible with adding AXDP message TLVs later).
UX Requirements (Apple-ish, calm, clear)
Conversation UI
- Each user send is rendered as a distinct bubble (or distinct message row) immediately upon Send.
- Message rows show a subtle state indicator:
Queued→Sending→Sent(link-acked) →Failed- If retransmits occur: show
Retrying…without being noisy.
- If the user sends two messages quickly, they should never visually merge in the UI.
Compose Box
- Provide a compose box supporting multi-line entry.
- Keyboard behavior:
- Enter: Send Line (default behavior for packet “chat”)
- Shift+Enter: Insert newline (compose without sending)
- Provide a segmented control or dropdown:
- Send Line
- Send Block
- “Send Block” sends the entire compose buffer as a single atomic message (with line endings preserved).
Accessibility
- State indicators must have accessible labels (“Queued”, “Sending”, “Sent”, “Retrying”, “Failed”).
- No color-only meaning.
Technical Requirements
Data Model
Create a persistent model (GRDB) to represent atomic outbound messages:
OutboundMessage
id(UUID)sessionId(connected session identifier)destCallsign(string)createdAt(date)payload(string or data)mode(line|block)state(queued|sending|sent|retrying|failed)attemptCount(int)lastError(string nullable)bytesTotal(int)bytesAcked(int) (best-effort; see below)sentAt(date nullable)ackedAt(date nullable)
Note: If we cannot reliably compute
bytesAcked, keep it at 0 and still progress state based on link-layer signals we do have.
Outbox Queue (per connected session)
Implement an OutboxManager that:
- Maintains a FIFO queue per
sessionId. - Immediately inserts a message row as
queuedon Send. - Transmits messages serially per session (no interleaving sends from the same session).
Flush / Serialization Policy
Add a setting (with a sane default) controlling when to send the next queued message:
Setting: Connected Send Serialization
- Default: Practical
- Options:
- Conservative: send next message only after prior message is fully link-acked (or otherwise considered safely out of the retransmit window).
- Practical (default): send next after at least one successful link-ack event after starting prior message (or after a short “TX drain” heuristic).
- Fast: send next immediately (still queued + atomic in UI).
Implementation must be robust even if the underlying stack doesn’t expose perfect “fully acked” signals.
Line Ending / Delimiting Rules (Legacy Safe)
- Ensure each atomic send ends with exactly one CR (unless user explicitly includes final newline in block mode).
- In Line mode:
- Always append CR.
- In Block mode:
- Normalize internal newlines to CR where appropriate for legacy packet terminals OR preserve as-entered but ensure final CR.
- Choose one approach and document it; must not create CRCR spam.
Error Handling
- No hard-fail if a message can’t be sent immediately (e.g., session is temporarily not writable).
- On send failure:
- Mark message
failed, storelastError. - Provide a contextual action: Retry, Copy, Delete.
- Mark message
- If a session disconnects with queued messages:
- Keep them queued and show “Waiting for reconnect” OR mark them failed with a clear reason (pick one behavior; prefer “queued until reconnect” if feasible).
Observability / Logging
- Add structured logs for:
- queue insert
- dequeue / start send
- retransmit detected
- ack signal observed
- completion / failure
- Logging must not spam; use debug-level for per-frame chatter and info-level for state transitions.
Integration Points
Packet Engine / TNC Layer
- Provide a single “send atomic payload” entry point used by UI:
sendOutboundMessage(sessionId, data)which enqueues and returnsOutboundMessage.id.
- Add hooks/callbacks from the AX.25 connected session layer for:
didStartTransmit(messageId)(best-effort)didObserveAckProgress(sessionId)(best-effort)didFailTransmit(messageId, error)didDisconnect(sessionId, reason)
If the underlying layer cannot tag frames to a specific message, use a session-scoped heuristic:
- While a message is
sending, any ack progress event advances it towardsent. - Conservative mode may require “idle window” detection (no outstanding unacked I-frames for N ms) if available.
UI Details (Delicious)
- Use subtle SF Symbols for state (examples; final choice up to you):
- Queued:
clock - Sending:
paperplane - Retrying:
arrow.triangle.2.circlepath - Sent:
checkmark - Failed:
exclamationmark.triangle
- Queued:
- Hover / secondary-click on an outbound bubble shows actions: Retry / Copy / Delete.
- “Sent (link-acked)” tooltip explains: “Acked by link layer; remote display not guaranteed.”
Acceptance Criteria
- Sending two messages back-to-back results in two distinct UI rows with independent state.
- Messages are sent in order for a given connected session.
- Default “Practical” serialization prevents obvious “glued together” behavior on typical links.
- “Fast” mode preserves current behavior but still keeps UI atomic.
- Compose UX works:
- Enter sends line
- Shift+Enter inserts newline
- Send Block sends the entire buffer
- Failures are visible and recoverable (Retry works).
- No regressions for basic connected send/receive with non-AXDP stations.
Test Plan
Unit Tests
- Outbox queue ordering per session.
- State machine transitions:
- queued → sending → sent
- queued → failed
- sending → retrying → sending → sent
- Line ending normalization rules for line vs block.
Integration Tests (Simulated / Harness)
- Simulate high-loss link with delayed acks:
- verify message 2 does not start until policy allows.
- Simulate disconnect mid-queue:
- verify queued messages remain and UI indicates pending / failed per chosen behavior.
Manual QA
- Connect to a legacy station/BBS and send:
- two rapid lines
- a multi-line block
- rapid sends during busy channel
- Verify readability and that AXTerm never merges sends in the UI.
Implementation Notes / Suggested Files
Models/OutboundMessage.swiftDatabase/Migrations/XXXX_createOutboundMessage.swiftNetworking/OutboxManager.swiftUI/ConnectedChatView.swift(or equivalent)UI/ComposeBar.swiftSettings/ConnectedSendSerializationSetting.swift
Future-Proofing (AXDP Ready)
- Keep
OutboundMessage.idstable and ready to map to future AXDPMSG_ID. - When both sides support AXDP, swap the “delimited text send” encoder to
AXT1/MSGTLVs while keeping the same UI + outbox pipeline.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start by reading the suggested entry points: Models/OutboundMessage.swift, Database/Migrations/XXXX_createOutboundMessage.swift, Networking/OutboxManager.swift, UI/ConnectedChatView.swift, UI/ComposeBar.swift, and Settings/ConnectedSendSerializationSetting.swift. Trace the current connected-send path before deciding how these pieces fit. Done means the acceptance criteria and listed unit, integration, and manual tests are satisfied without regressions for legacy stations.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- swift
- Domain
- database, frontend, networking
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 25/100