Repository navigation
docs: message edit/delete, AI assistant bridge, push decision matrix, contract function how-to - #680
Merged
Merged
Conversation
…on matrix, contract function how-to (codebestia#618, codebestia#619, codebestia#622, codebestia#635) concepts-message-edit-delete.md (codebestia#618): edit_message inserts a new linked message rather than mutating the original (editsMessageId always points at the root, not the previous edit in a chain, not a literal backward linked list despite how the read-side comment on supersededRows describes it); delete_message nulls ciphertext, hard deletes every message_envelopes row, and conditionally soft-deletes an attached file only if no other live message still references it. Documents the sender-only authorization rule for both; that a deleted message's envelope can never reach a device via /sync (both because the envelopes are already gone and because of an explicit isNull(messages.deletedAt) filter); that an edit's sibling-device requirement runs through the exact same findMissingSiblingDeviceIds send_message uses; the honest limit that delete cannot un-reveal plaintext a recipient already decrypted; and that delete_message actually double-emits message_deleted to two different room names every connected socket joins, so a client receives it twice, not once, a real finding from tracing the two io.to() calls against index.ts's connection setup, not something I assumed from the existing code comments alone. concepts-ai-assistant-bridge.md (codebestia#619): traces client event to rate-limit bucket to the hardcoded http://localhost:8000/chat call to the reply being persisted and fanned out through the same message_envelope pipeline as any other message. States plainly that the reply is stored unencrypted (same storage column named ciphertext, actually plaintext) and that the user's message leaves Clicked's infrastructure entirely and reaches OpenAI, per the AI agent's own api-chat.md. Documents the single try/catch collapsing every distinct failure mode, connection refused, non-2xx, malformed JSON, DB error, into one generic error with no way for a client to tell them apart. concepts-push-decision-matrix.md (codebestia#622): the full filter chain in pushFilter.ts as a table, confirming from presence.ts's isOnline that online status is checked per user via a single presence hash, not per device, so one online device suppresses push to every other device that user owns, before the separate per device isDeviceConnected check even runs. Layers in the 2 second coalescing window and 30 second per-device rate limit both push paths share via queueCoalescedPush, confirms the push payload carries no message content, and notes a real asymmetry: dispatchOfflinePush always passes an explicit recipientDeviceIds list but sendPushForMessage never does, so that filter stage is a no-op for the file-message path today. howto-add-contract-function.md (codebestia#635): worked example of a read-only function (is_initialized) and a state-changing one (set_paused, which also gates transfer and emits a new event) on token_transfer, chosen specifically because it is the one contract with an in-place upgrade path; both examples were actually compiled against the real contract and its existing test suite (8/8 passing) before being written up, not written from reading the code alone. Documents regenerating test_snapshots/ and that the CI check that fails if it's forgotten is simply cargo test's own storage-footprint mismatch detection, not a separate check. Covers the frontend side (building, simulating, signing, submitting via soroban.ts's existing transferToken pattern) and when the backend's Stellar listener (codebestia#331) needs its topic filter and decode logic extended for a new event, linking the events reference (codebestia#351). Added index entries for all four docs in docs/README.md, matching the existing per-doc one-line-description convention.
|
@Themancalledpg Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
This was referenced Oct 22, 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 #618
closes #619
closes #622
closes #635
Four documentation issues, each traced against the current source rather than written from the issue description alone.
apps/backend/docs/concepts-message-edit-delete.md (#618)
edit_message inserts a new linked message rather than mutating the original. Found while tracing it: editsMessageId always points at the root message, not the previous edit in a chain, so an edit chain is not literally the backward linked list the read-side comment on supersededRows describes it as. delete_message nulls ciphertext, hard-deletes every message_envelopes row, and conditionally soft-deletes an attached file only if no other live message still references it.
Covers: the sender-only authorization rule for both; that a deleted message's envelope can never reach a device via /sync (both because the envelopes are already gone and because of an explicit isNull(messages.deletedAt) filter); that an edit's sibling-device requirement runs through the exact same findMissingSiblingDeviceIds send_message uses; the honest limit that delete cannot un-reveal plaintext a recipient already decrypted.
A real finding, not assumed from the existing code comments: delete_message double-emits message_deleted to two different room names, and every connected socket joins both at connect time, so a client receives the event twice per delete, not once. Documented, with the recommendation that a client dedupe on messageId.
apps/backend/docs/concepts-ai-assistant-bridge.md (#619)
Traces the full path: client event, rate-limit bucket, the hardcoded http://localhost:8000/chat call, the reply persisted and fanned out through the same message_envelope pipeline as any other message.
States plainly that the reply is stored unencrypted (the column is named ciphertext, but what's in it is plaintext) and that the user's message leaves Clicked's infrastructure entirely and reaches OpenAI, per the AI agent's own api-chat.md. Documents the single try/catch collapsing every distinct failure mode (connection refused, non-2xx, malformed JSON, DB error) into one generic error with no way for a client to tell them apart.
apps/backend/docs/concepts-push-decision-matrix.md (#622)
The full filter chain in pushFilter.ts as a decision table. Confirmed from presence.ts's isOnline that online status is checked per-user via a single presence hash, not per-device, so one online device suppresses push to every other device that user owns, before the separate per-device isDeviceConnected check even runs.
Layers in the 2-second coalescing window and 30-second per-device rate limit both push paths share via queueCoalescedPush, confirms the push payload carries no message content, and notes a real asymmetry: dispatchOfflinePush always passes an explicit recipientDeviceIds list but sendPushForMessage never does, so that filter stage is a no-op for the file-message path today.
contracts/docs/howto-add-contract-function.md (#635)
Worked example of a read-only function (is_initialized) and a state-changing one (set_paused, which also gates transfer and emits a new event) on token_transfer, chosen specifically because it's the one contract with an in-place upgrade path. Both examples were actually compiled against the real contract and its existing test suite (8/8 passing, verified locally and reverted before committing, not written from reading the code alone).
Documents regenerating test_snapshots/ and that the CI check that fails if it's forgotten is simply cargo test's own storage-footprint mismatch detection, not a separate check. Covers the frontend side (building, simulating, signing, submitting via soroban.ts's existing transferToken pattern) and when the backend's Stellar listener (#331) needs its topic filter and decode logic extended for a new event, linking the events reference (#351).
Also
Added index entries for all four docs in docs/README.md, matching the existing per-doc one-line-description convention.