diff --git a/drumate/procedures/channel/channel_post_message.sql b/drumate/procedures/channel/channel_post_message.sql index 0b5ba39a..d0bbf07e 100644 --- a/drumate/procedures/channel/channel_post_message.sql +++ b/drumate/procedures/channel/channel_post_message.sql @@ -193,7 +193,15 @@ DECLARE _is_duplicate INTEGER DEFAULT 0; -- the top — matching Slack/Teams/Discord threads & Google-Docs/Figma comments. -- Mirrors hub's symmetric guard on its read-pointer advance. Normal messages -- (file_thread_id NULL) take this branch exactly as before → no regression. - IF _file_thread_id IS NULL THEN + -- + -- _entity_id is the peer of a P2P conversation and only chat.post sends it. + -- A folder-scoped post on the owner's own desk arrives through channel.post + -- with no entity_id: there is no peer conversation to bump, and inserting + -- NULL into time_channel.entity_id (NOT NULL, primary key) raised an + -- SQLEXCEPTION *after* the channel row was written. The EXIT HANDLER then + -- returned the error JSON instead of the message row, so the client never + -- received message_id and the attachment card never loaded until reload. + IF _file_thread_id IS NULL AND _entity_id IS NOT NULL THEN INSERT INTO time_channel(entity_id, ref_sys_id,message,ctime) SELECT _entity_id, _ref_sys_id,_message, _ctime ON DUPLICATE KEY UPDATE ref_sys_id= _ref_sys_id, ctime =_ctime ,message=_message; END IF; diff --git a/hub/procedures/channel/channel_get_message.sql b/hub/procedures/channel/channel_get_message.sql new file mode 100644 index 00000000..8961af27 --- /dev/null +++ b/hub/procedures/channel/channel_get_message.sql @@ -0,0 +1,22 @@ +DELIMITER $ + +-- ========================================================= +-- channel_get_message +-- +-- One workspace or folder chat message by id, for the mobile push worker to +-- quote in a banner at delivery time. Mirrors `p2p_get_message` in the +-- drumate schema: the push queue carries only identifiers, so the text is +-- read here, once, when the notification is composed. A trashed message is +-- not returned, so a deleted message is never quoted after the fact. +-- ========================================================= +DROP PROCEDURE IF EXISTS `channel_get_message`$ +CREATE PROCEDURE `channel_get_message`( + IN _message_id VARCHAR(16) CHARACTER SET ascii +) +BEGIN + SELECT message_id, author_id, message, thread_id, attachment + FROM channel + WHERE message_id = _message_id AND status != 'trashed'; +END $ + +DELIMITER ; diff --git a/patches/changelog.txt b/patches/changelog.txt index 514a46e4..c148d317 100644 --- a/patches/changelog.txt +++ b/patches/changelog.txt @@ -1,3 +1,44 @@ +2026-09-22 (Personal desk, folder chat: a message with a file attached was + stored but the sender saw no file card, and sometimes no message, until the + page was reloaded. channel.post writes into the owner's own database, whose + channel_post_message is the drumate variant. Its non-hub branch is written + for P2P and ends by bumping time_channel with the peer id, which channel.post + never sends because a folder post has no peer. The insert failed on the NOT + NULL primary key, the EXIT HANDLER returned {SUCCESS:0} in place of the + message row, and the client, which keys its attachment fetch on the + message_id in that row, kept an optimistic bubble that could never complete. + Reproduced on stage with a test account: the channel row was present with its + sbox attachment, the response carried _db_err "Column 'entity_id' cannot be + null" and no message_id. Skip the time_channel bump when there is no peer; + the P2P path, which always has one, is unchanged.) + drumate/procedures/channel/channel_post_message.sql + +2026-09-21 (Deployed the task_delete cascade onto stage. The routine has carried + the cascade in this repository since 569976f (2026-08-21) and is identical on + preview and main, but stage was still running the version that deletes only + the parent row: a task with subtasks left every child behind as an orphan, + reachable by no board and removable by nothing. + The stale copy is detectable from a client without reading the database: the + old body ends in SELECT ROW_COUNT() AS affected, and ROW_COUNT() is BIGINT, so + `affected` arrives over the wire as a string; the current body assigns it to a + DECLARE ... INT local first, so it arrives as a number. Same server, same + session, task.comment_delete already answered with a number while task.delete + answered with a string -- which is what proved this was a deployment gap and + not a logic bug. + Applied to all 1492 entity databases (989 hub, 450 drumate, 53 organization). + NOTE for whoever patches next: bin/patch.js on the stage host is older than + this repository and its `common` target reads + type IN ('drumate','hub') -- it silently skipped all 53 organization + databases, which had to be patched by listing them explicitly. 150 further + databases still carry the old routine; none has an entity row and none holds + any task row, so nothing reachable by the product runs them. + The factory template caches in /tmp were rebuilt too, because a new workspace + is cloned from them and they still held the old routine -- patching the live + databases alone would have left every workspace created afterwards broken. + Verified on a freshly provisioned hub: affected came back a number, the + subtask id was returned, and the subtask was actually gone.) + common/procedures/task/task_delete.sql + 2026-09-17 (Chat attachments: a member whose role is Chat could not attach a file. The upload answered 403 and the chat showed nothing -- no chip, no error -- while the same account attached files normally in a workspace where @@ -38,6 +79,7 @@ yellow_page/procedures/mfs/chat_upload_grant_repair.sql yellow_page/patches/2026-09-17-chat-upload-grant-repair.sql + 2026-09-14 (Reverted: media.copy stays in the notification surfaces after all. Earlier today these four routines were changed to drop media.copy from the feed, the unread feed, the badge and push, because the rows Lexis was seeing @@ -73,6 +115,15 @@ templates/factory/drumate.sql templates/factory/seed/yp.sql +2026-09-09 (channel_get_message reads one workspace chat message by id, for the + mobile push worker: the push queue and Redis carry identifiers only, so the + banner text (sender, workspace, excerpt) is read at delivery time through a + stored procedure. Direct messages already had p2p_get_message on the sender's + database; workspace and folder chat had no single-row read. Skips a trashed + message so a deleted message is never quoted after the fact. Applied on stage + to every database holding channel_post_message, 1620 schemas, 0 failures.) + hub/procedures/channel/channel_get_message.sql + 2026-09-09 (Workspace search returns results again: seo_search_unified called vhost() unqualified. vhost is a yellow_page function -- it exists only in yp, never in a hub or a diff --git a/patches/manifest.txt b/patches/manifest.txt index cbd94bc5..a689a5e5 100644 --- a/patches/manifest.txt +++ b/patches/manifest.txt @@ -372,3 +372,4 @@ yellow_page/procedures/analytics/feature_mark.sql yellow_page/procedures/analytics/session_stats.sql yellow_page/procedures/analytics/cohort_retention.sql + hub/procedures/channel/channel_get_message.sql