Skip to content

M1-53 Channels and forum tags

No Date Ruling Reason
M1-53/0 2026-10-14 Base int/m1-wave15 at e0113d6, branch feat/M1-53-chat-channels in its own worktree; the branch is not merged locally, the integration lane ports it. The migrations take the next free numbers of the base, 00026 and 00027. Text this ticket adds is English. The port methods take the caller as a actor.Actor like the space methods of M1-52 (M1-52/5), not from the context. Instructions of the launcher, which override the brief's merge step and language; the brief asks to follow the conventions of the space part.
M1-53/1 2026-10-14 Validation of channels (D1): names and tag names are trimmed and checked with kernel/textcheck like space names; a control or bidi character gives the field code of the textcheck rule (control_character, bidi_control, invalid_utf8) instead of invalid_characters, the codes M1-52 already uses; the topic is checked, not trimmed. forum_default_sort on a text channel is /forum_default_sort not_allowed_for_kind, after_channel_id and after_tag_id that are no live sibling are not_a_sibling, a body without a member is a field error with the empty pointer (required), not_a_forum carries the empty pointer (06 FieldError: empty for a rule of the whole request) instead of /. Create and move lock the space row (the order of the channels of a space), every other channel change locks the channel row, tag changes lock their channel row; the lock order space then channel is the same everywhere. moveChannel first checks channel.read in filter mode, so an invisible channel is 404 like an unknown one before channel.reorder on the space can answer 403. One validator and one set of codes for all display names (SEC-T07); a channel of a visible space must not reveal its existence through the space-level action (AZ-P4).
M1-53/2 2026-10-14 Archived channels (D2): the chat source reports Archived for an archived channel and for every channel of an archived space; kernel/authorize maps the decision reason channel_archived to the problem code channel_read_only (also for an archived space, which shares the reason). Manage changes of an archived channel or a channel of an archived space and of their tags are 409 conflict; archiving a channel of an archived space is still allowed. archiveChannel runs as the rights change channel.archive with the channel as target, publishes ChannelArchived and records the reason. 07 sections 4.1 and 4.5 and 06 name channel_read_only with reason: channel_archived; before this ticket the reason fell back to forbidden.
M1-53/3 2026-10-14 Forum tags (D3): every tag create, update, move and delete bumps channels.version once and appends channel.updated with the channel; an update without change and deleting a deleted tag write no event and record no_change. A tag of another channel, an unknown one and (for update and move) a deleted one are 404. Audit details are {op, tag_id}; the tag name is not recorded, and channel.create records {space_id, channel_id, kind} and channel.update changed_fields without names. 03 forum_tags and 05 section 6; audit details carry only codes and ids (SEC-AUD-5, M1-52/10), so the names the brief lists cannot be recorded.
M1-53/4 2026-10-14 listSpaceChannels drops a row that passed the SQL filter but not authorize without counting it; the alarm filter_mismatch of bothy.authz.errors is not wired for any list yet. Same as listSpaces of M1-52; wiring the counter for all lists is cross-cutting work outside this ticket.
M1-53/5 2026-10-14 Channel defaults: history_backfill_limit is channel.history_backfill_limit_default; app.New refuses a default outside 0 to channel.history_backfill_limit_max. The column has no database default. 03 names the key as default and the upper bound as an application check.
M1-53/6 2026-10-14 This ticket creates threads (and the insert of the main thread in queries/threads_main.sql of the chat repository) although the plan row of M1-55 names the table, because R-DM-11 needs the main thread at channel creation. The structure source keeps Thread empty until M1-55. chat owns threads; M1-55 builds the thread use cases on the same table.
M1-53/7 2026-10-14 Channels created after a snapshot (D8): no change to the cache of M1-28; TestNewChannelVisibleWithoutNewPermissionVersion holds it against the real chat source. The archived cap and the reason channel_archived already existed (M1-29/8, M1-52/6). The test passed without a change in authz.
M1-53/8 2026-10-14 Test fixtures: the foreign key of grants.target_channel_id needs a channels row behind every channel grant. authz/adapters/fake.Structure gets OnPutChannel, and every PostgreSQL test world over the fake (authz, agents, identity) mirrors each fake channel into spaces and channels there; the authz repository tests create real channel rows. The chat HTTP world uses the real structure source of wire. The same class as the space fixtures of M1-52/8; one hook covers every fake channel instead of patching each grant fixture.
M1-53/9 2026-10-14 Test first held for the domain rules, the problem mapping and the migration (red runs: build failure and the wrong problem code); the HTTP tests over PostgreSQL were written after the use cases. Honest record of the order of work.
M1-53/10 2026-10-15 Port onto main: code and tests are taken byte for byte from the integration squash; no integration fix belongs to this ticket. The integration base also holds M2-39 (terminal hosts, migration 00025), which is not on main, so nothing of M2-39 is ported: the host stubs and host route rows of api/routes/real_test.go, the host test of the fake and the host tables of migrations/privileges_test.go stay out, and TestGrantChecks of the authz repository keeps the random host id of main beside the new channel row. The migrations are renumbered to follow 00024_grants_space_fk_validate.sql without a gap: 00026_chat_channels.sql becomes 00025_chat_channels.sql and 00027_grants_channel_fk_validate.sql becomes 00026_grants_channel_fk_validate.sql, with the numbers in their comments, in migrations/authz_groups_grants_test.go, in the changelog and in the module README; TestChatChannelsMigration_Down migrates down to version 24, and the checksums are recorded anew. The code comments, the SQL comments and the comments of 00025_chat_channels.sql name the English chapter files and headings (02-architecture.md, 03-data-model.md, 05-actions.md, 03 principle 5, 7 and 12, "by the application" and "Configuration values of this chapter"), and the new keys of wire/config_test.go name the chapter 03-data-model.md. The later entries removed from api/operations.yaml and the rules removed from tools/configdoc/planned.yaml use the key milestone of main. The changelog entry, the module README (status line, code list and the new section on channels and forum tags) and the key rows of docs/operations/configuration.md are written into the English text at the same places. The main-only case TestEmptySourcesKnowNothing now checks emptyThreads instead of emptyChannels, which this ticket removes because chat is now the channel source. Generated files are regenerated, not merged. main is English and does not hold M2-39; the migration numbers of main must follow without a gap.