| M1-57/1 |
2026-10-15 |
The MessageView of the brief is the existing messaging.Message (no second DTO); it gains ContentTruncated. The use cases ListThreadMessages and GetMessage are the port methods History and Get of app.Service, one method each; like the brief's signatures they take the principal from the context (kernel/actor, anonymous when none), while Create keeps its explicit actor. |
Brief scope item 1 forbids a second DTO; one method per use case avoids a wrapper without behavior. |
| M1-57/2 |
2026-10-15 |
One statement per mode in queries/history.sql: the page CTEs read L + 1 rows per side (descending below the anchor, ascending from it), EXISTS gives the opposite side of before_seq (thread_seq >= X) and after_seq (thread_seq <= X), and a one-row flags relation is left joined with the rows, so an empty page still returns its flag in one row. The around_seq window is chosen in Go (domain.PickAround, domain.PageFlags) from those rows; the flags then follow from whether a side returned more rows than it gave. All values stem from the one statement, so from one snapshot. |
Brief notes 3 and 4; the window rule stays a pure, table-tested function instead of SQL arithmetic. |
| M1-57/3 |
2026-10-15 |
The anchors are checked in the REST handler before anything is read and again in History (the same domain.ValidateHistoryRequest), so later callers of the port get the same 400. An anchor at or above 2^53 is out_of_range; with two or three anchors only the mutually_exclusive entries are returned. |
Brief note 2 step 1; public integers stay below 2^53 (06 section 2). |
| M1-57/4 |
2026-10-15 |
Author and mentions of each listed item are read after the history statement with the representation of M1-56 (authors cached per page). ListMentions, GetMessage and GetMessageByClientID stand in the allowlist of TestQueriesCarryVisibleChannelPredicate: each reads by an id whose message the caller already read with the visible predicate or authorized for writing. The mention set of a message changes only with an edit (M1-58), so it needs no shared snapshot with the page. |
Brief note 8 seeds the allowlist with M1-56's statements; M1-70 replaces the test with the repository-wide lint. |
| M1-57/5 |
2026-10-15 |
The tombstone form lives in the one conversion app.view: a row with deleted_at gets empty content and empty mention_ids there, whatever the row or the mentions table hold; attachments, reactions and components stay empty and pinned false in every form. Lists skip the mentions query for a tombstone. |
Brief note 6: one function, also for M1-58. |
| M1-57/6 |
2026-10-15 |
A listed item that passes the visible predicate and fails message.read is dropped and logged at error level with bothy.action and bothy.reason = filter_mismatch only (the resource kind is in the message text; the signals catalogue has no resource-kind key and the handlers drop unknown keys), and counted in bothy.authz.errors. wire.NewMessaging takes the process logger and builds the counter with authz/adapters/telemetry on the global meter provider, as NewAuthzCatalog does. |
07 section 7.4 rule 3, signals.md, alert BothyAuthzError. |
| M1-57/7 |
2026-10-15 |
message.list_content_max_bytes enters the schema (chapter 06, default and minimum as the chapter table), the stale message. rule of tools/configdoc/planned.yaml goes, and the messaging configuration also binds api.page_size_default and api.page_size_max. Files outside the scope list change because the keys and the test world need them: internal/wire (keys, binding, logger), test/messagingworld (options for small test values, the log capture, the tombstone helper and the header comparison of Same404, which ignores Correlation-Id and Date), the generated configuration reference and the config test. |
Brief requirement 9 and "No new configuration key": the key was documented, only not yet active. |
| M1-57/8 |
2026-10-15 |
Host rules of the launcher win over the brief: make check runs with SONAR_SKIP=1 (no local SonarQube in this run), and the branch feat/M1-57-messaging-history is not merged into the base locally; it stays for the integration lane. |
The launcher's task text says "sonar skipped" and names no local merge. |
| M1-57/9 |
2026-10-15 |
Red team: when the visible read of getMessage misses, an existing message is still decided by message.read (the internal lookup GetMessage, allowlisted), so an app with a grant on the channel but without messages:read gets 403 scope_missing as from listThreadMessages; a person without access gets the not_found of an unknown id, and an allow the visible read contradicts stays not_found. |
06 section 4 item 4, 07 section 2.10 and AZ-M33: a missing app scope is 403, not 404, where the app has a grant. |
| M1-57/10 |
2026-10-15 |
The sqlc output of messaging ends in .gen.go like that of instance and audit (sqlc.yaml; the M1-56 files are regenerated under the new names), and the new repository code gets its own package tests (TestRepository_History) and the use cases Get their unit test. |
M1-05/7 requires the suffix for generated Go files; the coverage gate counts code without it, and with the history statements the server module fell to 79.7 % below the 80 % threshold. |
| M1-57/11 |
2026-10-16 |
Port onto main: code and tests are taken byte for byte from the integration squash, apart from the points named here; of the integration fixes of wave 22 only the unions of INT-W22/1, the single declarations of INT-W22/2 and the M1-57 part of the test fixes of INT-W22/5 concern this ticket (section "Integration int/m1-wave22", which holds only their M1-57 parts; INT-W22/3, INT-W22/4 and INT-W22/6 resolve the merge with tickets that are not on main). The integration base also holds M2-24 (apps), M2-39 and M2-40 (terminal), and the wave holds M3-13 (search), M1-58 (editing) and M4-04, which are not on main, so nothing of them is ported: the terminal and apps entries stay out of sqlc.yaml, the host and app route rows stay out of api/routes/real_test.go, the messaging test world keeps the principal source of main without app installations, GetVisibleMessage keeps the whole row (INT-W22/3 belongs to M3-13), and the use case tests of filter_test.go and mentions_test.go give New the cursors and limits of M1-60 and M1-57 but not the Redactor and Audit of M1-58. The ticket adds no migration. The comments of queries/history.sql, predicate_test.go and history_test.go name the English chapter file 04-sync-protocol.md and 03 principles 7 and 12; the multi-byte content of TestGetMessage_FullContentWhenEventTruncated is the Greek letter alpha instead of a German umlaut (still one code point of two bytes), because the language gate (D-W-94) rejects umlauts in tracked files, as M1-60/6 did. The new key of wire/config_test.go names the chapter 06-api.md. The changelog entry, the module README (status line, code list and the new section on reading), the key row of docs/operations/configuration.md (now int and active) and these rulings are written into the English text at the same places; the rule message. of tools/configdoc/planned.yaml goes, and getMessage and listThreadMessages are sorted into the enabled list of api/operations.yaml with their later entries removed. Generated files are regenerated with the generators of the ported tree, not merged. |
main is English and holds M1-56 and M1-60 but not M2-24, M2-39, M2-40, M3-13, M1-58 or M4-04. |