Commit Graph
3383 Commits
Author SHA1 Message Date
cursor[bot] 85acba42e1 Skip flaky TestExtractConcurrency (#37834)
Automatic Merge
2026-08-04 21:30:12 +03:00
Nick MisasiandCursor Agent c7eff70026 ABAC: plugin-keyed resource types, trusted plugin PAP/CEL APIs, and AuthZEN-style decision API (#37509)
* MM: add v0.5 plugin access control policy model, registry, and decision outcomes

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* MM: add plugin access control PDP/PAP app-layer methods with fail-closed semantics

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* MM: expose plugin access control API surface (EvaluateAccessControl + PAP/CEL methods)

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* MM: add store-layer round-trip tests for v0.5 plugin access control policies

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* MM: extract plugin access control app code into plugin_access_control.go

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* MM: add atomic type-guarded AccessControlPolicyStore.DeleteIfType

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* MM: plugin PAP hardening — atomic typed delete, indistinguishable 404s, audit every attempt

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* MM: close plugin Get-by-ID TOCTOU via GetPolicyOfType; stamp save audit operation at entry

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Fix gob RPC poisoning from native attribute select options

NativeUserAttributeFields stored bool-select options as
[]map[string]string inside PropertyField.Attrs (map[string]any). gob
requires concrete types inside interface values to be registered, and
[]map[string]string is not registered in client_rpc.go, so encoding the
GetAccessControlFieldsAutocomplete reply failed and net/rpc shut down
the shared plugin API connection, breaking every subsequent plugin API
call.

Build the options from gob-registered containers ([]any/map[string]any)
instead; JSON output is byte-identical. Add gob round-trip regression
tests covering every plugin access control API reply payload: the
autocomplete response including native attribute fields (fails against
the old code), policies with JSON-decoded Props, visual AST condition
values of every runtime shape, expression check errors, query users
responses, and evaluation decisions.

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Move plugin access control gob-safety tests into their own file

Pure move: plugin_access_control_test.go crossed 1000 lines; the gob-safety
helper and TestPluginAccessControlGobSafety now live in
plugin_access_control_gob_test.go, unchanged.

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* MM: resolve plugin policy existence when ABAC is unavailable (Option B)

Every evaluation-impossible branch of EvaluatePluginAccessRequest
(service nil / unlicensed / flag off / user load or subject build
failure / evaluator infra error / unknown outcome) now performs a raw
open-core store read on the already-validated resource ID: no stored
row returns no_policy so the caller can safely apply legacy behavior;
any stored row (with a Warn on a foreign-type anomaly) or a failed
read returns unavailable so the caller must fail closed. This lets the
plugin drop its local policy index entirely.

Strengthens the EvaluateAccessControl doc contract accordingly and
reworks the fail-closed test matrix with with/without-row splits per
branch, a foreign-type-row case, a store-read-error case on the store
mock, and passthrough rows pinning that the fallback read never runs
when the evaluator answers.

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Clarify EvaluateAccessControl failure-mapping doc

A failure with a definitive store miss maps to no_policy under the
Option B semantics, so 'failures never map to allow or no_policy' was
inaccurate. State precisely: never allow; no_policy only on positively
determined non-existence; deny for defensive failures on a resolved
policy; unavailable otherwise.

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Tighten ABAC comments

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Document AccessDecision.Outcome as plugin-API-only

The evaluator populates Outcome on every lane, but the only production
reader is the app layer's EvaluatePluginAccessRequest, which maps it
into PluginAccessControlDecision; core channel/team enforcement reads
the collapsed Decision bool alone. State that on the field.

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Retrigger CI to rebuild enterprise image with updated enterprise branch

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Document that only the evaluator's plugin lane sets AccessDecision.Outcome

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* ABAC: key plugin resource types as plugin_id:type and drop the static registry

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* ABAC: replace AccessDecision.Outcome with the AuthZEN decision context reason

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* ABAC: drop type-scoped policy get/delete and check the type in the app layer

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* ABAC: compare plugin policy type ownership exactly instead of case-insensitively

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* ABAC: bound the whole policy type to the Type column width

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* ABAC: require an allow before treating a decision as the no-policy fallback

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* ABAC: treat a colliding foreign-type policy row as no_policy, not a deny

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* ABAC: gate plugin policy reads on a raw store read before normalization

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* ABAC: pin policy type immutability on save in the store tests

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* ABAC: reuse a single unavailable-error constructor in the existence fallback

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* ABAC: re-check plugin policy ownership on the normalized get read

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* ABAC: confirm plugin policy ownership before surfacing a normalization error

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-08-04 09:59:48 -04:00
Andre Vasconcelos cd920652fb Bumping prepackaged github version to v2.8.0 (#37831) 2026-08-04 15:30:17 +03:00
unified-ci-app[bot] f4185af07a Update latest minor version to 11.11.0 (#37827)
Automatic Merge
2026-08-04 13:30:11 +03:00
Pablo Vélez 7130ae598f MM-70054 - Enable Team Membership ABAC feature flag by default (#37781)
Automatic Merge
2026-08-04 09:30:09 +03:00
Jesse Hallam ae0bec4d67 Add MFI plugin signature public key behind feature flag (#37793)
* Add MFI plugin signature public key stub behind feature flag

* Replace MFI plugin signing key stub with actual public key

* Add federal-signed test plugin fixtures and verify against MFI key

* Rename federal test plugin fixtures to match mfiPluginPublicKey

* Test verifyPlugin's feature-flagged MFI key path
2026-08-03 11:57:16 -03:00
cursor[bot] c17064f72c Fix flaky TestImportValidateDirectPostImportData (#37795)
Automatic Merge
2026-08-02 01:00:09 +03:00
Pablo Vélez 5152df2418 Mm 70056 missing info banner team abac team admin (#37790)
* [MM-70056] Return imported parent policies so team admins see the system policy banner

* [MM-70055] Resolve parent policy expressions for team admins in the mode-flip modal

* [MM-70057] Let team admins read team sync jobs for their own team policy
2026-07-31 20:25:32 +02:00
Alejandro García Montoro 3019460ac5 MM-69445: Markdown fixes (#37388)
* Cap markdown block nesting depth

blockQuoteStart and listStart recursed once per nested '>' or
list marker on a line, guarded only by an indentation check that
bounds CommonMark indentation, not nesting count. A single
crafted line (e.g. "- " repeated tens of thousands of times)
forced recursion and slice-copy work that grew super-linearly
with nesting depth, letting an authenticated user burn a
disproportionate amount of server CPU per request.

Thread a depth counter through blockStart, blockStartOrParagraph,
blockQuoteStart, and listStart, and stop opening new nested
blocks past maxNestingDepth. This bounds nesting whether it is
built by one line or accrued across many, and caps the recursion
and copy cost to a small constant regardless of input size.

Also apply the existing maxLen byte cap directly inside Parse(),
not just Inspect(), so any caller of Parse is protected
uniformly.

* Add tests for markdown block nesting depth cap

Cover the new maxNestingDepth guard: rejecting a well-formed
block quote/list item once the depth limit is reached, capping
recursion for a single deeply-nested line, and honoring depth
already accrued by open ancestor blocks. Also cover Parse()'s
byte-length cap and add benchmarks showing the nesting-depth
cases now scale roughly linearly with input size instead of
super-linearly.

* Reject oversized posts before markdown parsing

createPost, updatePost, and patchPost ran
PostWithProxyRemovedFromImageURLs, which parses the raw message,
before the message-length check inside Post.IsValid ever ran.
An oversized message paid the full parsing cost even though it
was always going to be rejected.

Add rejectOversizedMessage, mirroring the rune-count check in
Post.IsValid, and call it in all three handlers before any
markdown processing touches the message.

* Add tests for oversized post/patch rejection

Cover the rejectOversizedMessage check added for createPost,
updatePost, and patchPost: a message over MaxPostSize is
rejected with a 400 in all three handlers before it reaches
markdown processing.

* Sync markdown parse cap with real max post size

Parse and Inspect capped input at a hardcoded ~128KB, assuming a
64KB max post size. The real configured limit comes from the
posts.message column and can be raised past that default, so on
installations with an enlarged column, markdown parsing (mentions,
links, formatting) was silently skipped for legitimately-sized
posts.

Have the markdown package pull its cap from a function registered
via SetMaxPostSizeFunc, defaulting to the old conservative constant
only if nothing was ever registered. PlatformService.New registers
PlatformService.MaxPostSize itself once the store is ready, so the
cap always reflects the real limit regardless of which code path
parses markdown first, and the store layer stays unaware that
markdown parsing exists.

* Add tests for markdown max post size sync

Cover SetMaxPostSizeFunc raising the cap and being called on every
MaxLen invocation. Also verify, end to end against a real database,
that PlatformService.New wires markdown.MaxLen to the real
configured max post size.

* Fix tests

* Address review comments

- Simplify setting the maximum number of runes for a post
- Simplify comments
- Rename variables and functions to reflect whether they represent bytes
  or runes.
2026-07-31 17:05:13 +00:00
Ben CookeandMattermost Build 466b5bb783 redact logs (#37757)
Co-authored-by: Mattermost Build <build@mattermost.com>
2026-07-30 23:12:52 -04:00
rogetiandMatt Rogers c6db95f35d Preserve relative redirect_to on root SiteURL (#37725)
* Preserve relative redirect_to on root SiteURL

fullyQualifiedRedirectURL cleaned an empty SiteURL path to "." and required
the redirect target to have that as a prefix, so on a root SiteURL every
same-origin relative redirect (e.g. the /oauth/authorize resume after SSO
login) was discarded and only the site origin returned. Normalize an empty
prefix path to "/". Add root-SiteURL cases to TestFullyQualifiedRedirectURL.

Signed-off-by: Matt Rogers <mrogers@palantir.com>

* Use segment-aware path check for redirect prefix

Address review: replace the empty-prefix normalization with pathWithinPrefix,
which compares on path segments. This fixes the root SiteURL case (empty path)
and also stops a non-root prefix like /mm from matching /mmfoo, and preserves
same-origin absolute targets with only a query/fragment (?foo=bar, #section).

Signed-off-by: Matt Rogers <mrogers@palantir.com>

* Add /mmfoo regression test for segment-aware prefix check

Signed-off-by: Matt Rogers <mrogers@palantir.com>

---------

Signed-off-by: Matt Rogers <mrogers@palantir.com>
Co-authored-by: Matt Rogers <mrogers@palantir.com>
2026-07-30 16:38:21 -04:00
Pablo Vélez 5ef9e70d95 Mm 69832 abac review nits (#37640)
* MM-69831 - Add configurable interval for the ABAC membership sync schedulers

* MM-69832 - Hoist the ManageSystem permission check in createAccessControlPolicy

* MM-69832 - Add policy-scoped search term to the ABAC team invite candidate endpoint

* MM-69832 - Switch strict ABAC invite picker to server-side policy-scoped typeahead

* MM-69832 - Rename the ABAC invite candidate search param to term for consistency

* MM-69832 - Gate ABAC invite full-name term search behind ShowFullName for non-admins

* MM-69832 - Add API and jest coverage for the ABAC invite term search and full-name privacy gate

* MM-69832 - Clean up comments
2026-07-30 15:17:18 +02:00
Jesse Hallam d4d216e93e Add ClusterInterface.Shutdown to surface skipped cluster sends (#37753)
* Add ClusterInterface.Shutdown to surface skipped cluster sends

* Defer cluster interface shutdown so it runs on every exit path
2026-07-30 14:48:59 +02:00
Devin Binnie b021e5be06 [MM-69734] Add no session data, ensure session attributes are not leaked through evaluation trace to non sysadmins (#37600) 2026-07-30 08:42:38 -04:00
Pablo Vélez 8a9aacb0fb MM-69831 - Add configurable interval for the ABAC membership sync schedulers (#37623)
* MM-69831 - Add configurable interval for the ABAC membership sync schedulers

* Validate ABAC sync job interval with a 60s minimum
2026-07-30 13:15:18 +02:00
mattermost-code 0b034ca9a7 Fix flaky TestExtractConcurrency (#37527)
Automatic Merge
2026-07-30 12:30:09 +03:00
+3 cdbc92639c Translations update from Mattermost Weblate (#37689)
* Translated using Weblate (Polish)

Currently translated at 98.6% (7582 of 7687 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/pl/

* Translated using Weblate (Polish)

Currently translated at 98.7% (7592 of 7687 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/pl/

* Translated using Weblate (Hungarian)

Currently translated at 58.2% (4479 of 7687 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/hu/

* Translated using Weblate (Hungarian)

Currently translated at 58.2% (4479 of 7687 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/hu/

* Translated using Weblate (Polish)

Currently translated at 98.9% (7604 of 7687 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/pl/

* Translated using Weblate (Ukrainian)

Currently translated at 76.8% (5904 of 7687 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/uk/

* Translated using Weblate (Hungarian)

Currently translated at 58.2% (4479 of 7687 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/hu/

* Translated using Weblate (Russian)

Currently translated at 74.9% (5759 of 7687 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ru/

* Translated using Weblate (Dutch)

Currently translated at 93.6% (7200 of 7687 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/nl/

* Translated using Weblate (Polish)

Currently translated at 98.9% (7607 of 7687 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/pl/

* Translated using Weblate (Polish)

Currently translated at 99.1% (7618 of 7687 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/pl/

* Translated using Weblate (Dutch)

Currently translated at 92.1% (3136 of 3402 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/nl/

* Translated using Weblate (Dutch)

Currently translated at 93.6% (7200 of 7687 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/nl/

* Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (3402 of 3402 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/zh_Hans/

* Translated using Weblate (Chinese (Simplified Han script))

Currently translated at 100.0% (7687 of 7687 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/zh_Hans/

* Translated using Weblate (Dutch)

Currently translated at 92.2% (3137 of 3402 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/nl/

* Translated using Weblate (Dutch)

Currently translated at 93.7% (7210 of 7687 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/nl/

* Translated using Weblate (English (Australia))

Currently translated at 87.0% (2961 of 3402 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/en_AU/

* Translated using Weblate (English (Australia))

Currently translated at 87.0% (2961 of 3402 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/en_AU/

* Translated using Weblate (English (Australia))

Currently translated at 87.0% (2961 of 3402 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/en_AU/

* Translated using Weblate (English (Australia))

Currently translated at 87.0% (2961 of 3402 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/en_AU/

* Translated using Weblate (English (Australia))

Currently translated at 87.0% (2961 of 3402 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/en_AU/

* Translated using Weblate (English (Australia))

Currently translated at 87.0% (2961 of 3402 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/en_AU/

* Translated using Weblate (English (Australia))

Currently translated at 87.0% (2961 of 3402 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/en_AU/

* Translated using Weblate (English (Australia))

Currently translated at 87.0% (2961 of 3402 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/en_AU/

* Translated using Weblate (English (Australia))

Currently translated at 87.0% (2961 of 3402 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/en_AU/

* Translated using Weblate (English (Australia))

Currently translated at 87.0% (2961 of 3402 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/en_AU/

* Translated using Weblate (English (Australia))

Currently translated at 87.0% (2961 of 3402 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/en_AU/

* Translated using Weblate (English (Australia))

Currently translated at 89.6% (6895 of 7687 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/en_AU/

* Translated using Weblate (Polish)

Currently translated at 99.5% (7651 of 7687 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/pl/

* Translated using Weblate (Lithuanian)

Currently translated at 59.5% (4574 of 7687 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/lt/

* Translated using Weblate (Polish)

Currently translated at 99.5% (7656 of 7687 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/pl/

* Translated using Weblate (Polish)

Currently translated at 99.7% (7664 of 7687 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/pl/

* Translated using Weblate (Polish)

Currently translated at 92.8% (3160 of 3402 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/pl/

* Translated using Weblate (Polish)

Currently translated at 100.0% (7687 of 7687 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/pl/

* Translated using Weblate (Russian)

Currently translated at 75.0% (5766 of 7687 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ru/

* Translated using Weblate (Polish)

Currently translated at 93.8% (3192 of 3402 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/pl/

* Translated using Weblate (Russian)

Currently translated at 75.1% (5776 of 7687 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ru/

* Translated using Weblate (German)

Currently translated at 87.0% (2962 of 3402 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/de/

* Translated using Weblate (German)

Currently translated at 93.0% (7150 of 7687 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/de/

* Translated using Weblate (Polish)

Currently translated at 94.4% (3212 of 3402 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/pl/

* Translated using Weblate (Dutch)

Currently translated at 92.2% (3138 of 3402 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/nl/

* Translated using Weblate (Hungarian)

Currently translated at 58.2% (4479 of 7687 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/hu/

* Translated using Weblate (Polish)

Currently translated at 96.2% (3273 of 3402 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/pl/

* Translated using Weblate (Russian)

Currently translated at 77.8% (2649 of 3402 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/ru/

* Translated using Weblate (Russian)

Currently translated at 75.6% (5818 of 7687 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ru/

* Translated using Weblate (Polish)

Currently translated at 96.5% (3283 of 3402 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/pl/

* Translated using Weblate (Polish)

Currently translated at 97.0% (3303 of 3402 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/pl/

* Translated using Weblate (Ukrainian)

Currently translated at 77.8% (5981 of 7687 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/uk/

* Translated using Weblate (Polish)

Currently translated at 97.3% (3313 of 3402 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/pl/

* Translated using Weblate (Polish)

Currently translated at 98.2% (3343 of 3402 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/pl/

* Translated using Weblate (Russian)

Currently translated at 76.1% (5851 of 7687 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ru/

* Translated using Weblate (Russian)

Currently translated at 76.1% (5854 of 7687 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ru/

* Translated using Weblate (Polish)

Currently translated at 98.8% (3363 of 3402 strings)

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/pl/

* Translated using Weblate (Russian)

Currently translated at 76.7% (5903 of 7687 strings)

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/ru/

* Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translation: Mattermost/server
Translate-URL: https://translate.mattermost.com/projects/mattermost/server/

* Update translation files

Updated by "Cleanup translation files" hook in Weblate.

Translation: Mattermost/webapp
Translate-URL: https://translate.mattermost.com/projects/mattermost/webapp/

---------

Co-authored-by: master7 <marcin.karkosz@rajska.info>
Co-authored-by: ritchierope <ritchierope@users.noreply.translate.mattermost.com>
Co-authored-by: s0k0l -_- <salarysalo@gmail.com>
Co-authored-by: Dmitriy Q <krotesk@mail.ru>
Co-authored-by: Tom De Moor <tom@controlaltdieliet.be>
Co-authored-by: Sharuru <mave@foxmail.com>
Co-authored-by: Roy Orbitson <roy-orbitson@devo.net.au>
Co-authored-by: Matthew Williams <Matthew.Williams@outlook.com.au>
Co-authored-by: Simonas Audickas <simonas.audickas@gmail.com>
Co-authored-by: jprusch <rs@schaeferbarthold.de>
Co-authored-by: Bohdan <bshumylo@yahoo.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
2026-07-29 21:02:23 -04:00
Nick MisasiandCursor 10804f80f5 Prepackage mattermost-plugin-agents v2.5.1 (#37727)
* Prepackage mattermost-plugin-agents v2.5.0.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Prepackage mattermost-plugin-agents v2.5.1.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-29 18:26:44 +00:00
9de0027868 [MM-69982] Fix intermittent remote cluster ping failures from stale keep-alive connection reuse (#37694)
* [MM-69982] Fix intermittent remote cluster ping failures from stale keep-alive reuse

The remote cluster HTTP transport set IdleConnTimeout to 90s while pings fire
every PingFreq (60s) and peers close idle keep-alive connections after their own
IdleTimeout (default 60s). Because 90s > 60s, the pooled ping connection outlived
the peer's reaping, so each 60s ping reused a connection the peer had already
closed, racing the FIN and failing intermittently with EOF / connection reset.

Set IdleConnTimeout to PingFreq/2 so the pool always discards the connection
before the next ping fires, making the reuse race structurally impossible while
keeping the invariant (IdleConnTimeout < PingFreq) explicit and tied to PingFreq.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

* chore: retrigger Server CI after check-style Docker Hub flake

Server CI check-style failed pulling buildenv (Docker Hub Client.Timeout /
missing buildenv-image artifact); unrelated to IdleConnTimeout changes.
Enterprise CI check-style already passed on the same commit.

Co-authored-by: mattermost-code <matty-code@mattermost.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: mattermost-code <matty-code@mattermost.com>
2026-07-28 23:17:19 -04:00
Alejandro García MontoroandMattermost Build 9230eb6f6a MM-69725: Propagate context through Extractor.Extract using new mattermost/pdf fork (#37579)
* Propagate context through Extractor.Extract

Adds ctx context.Context to the Extractor interface and all
implementations so callers can cancel in-flight extractions.

pdfExtractor passes the context down to GetPlainText(ctx), which
checks ctx.Done() at each page boundary. When extractWithTimeout
fires (deadline exceeded or parent cancel), it cancels the derived
context via defer cancel(), and the PDF goroutine stops at the next
page rather than running to completion, eliminating the heap
growth that caused OOM on complex documents.

For non-context-aware extractors (docconv, plain, archive) the
context is accepted but not yet acted on; they continue to run
to completion on a detached goroutine after the timeout, as before.

The call site in channels/app/file.go passes rctx.Context() so
request cancellation (e.g. HTTP disconnect) also propagates.

* Use new mattermost/pdf fork

* Update notice reflecting the new fork

* Fail archiveExtractor.Extract early on ctx errors

* Bump mattermost/pdf to cd8a834041c4

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
2026-07-28 17:29:36 +02:00
Nick MisasiandCursor Agent 6fefcc80a4 MM-67336: Request structured JSON output for AI message rewrites (#37581)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-07-28 10:13:16 -04:00
Ibrahim Serdar Acikgoz f6c1459ebf MM-69612: Add opt-in EnableAuditLogging setting for ABAC (#37322) 2026-07-28 13:12:09 +02:00
Ibrahim Serdar Acikgoz d33ad5a7e9 [MM-69810] Update golang.org/x/image dep (#37595) 2026-07-28 13:11:38 +02:00
Christopher Speller dc41b72fdd Prepackage mattermost-plugin-agents v2.5.0-rc2. (#37691) 2026-07-28 04:01:29 -07:00
Pablo Vélez 1dacc2d514 Mm 69830 abac membership messaging fixes (#37618)
* MM-69828 - Fix ABAC team Access-tab stuck-public cards, parent-policy mode-flip count

* Add fallback handling for parent-policy fetch failure in AccessTab component

* MM-69829 - Show Attribute Based indicator for policy-governed teams in admin Teams list

* MM-69829 - Clear stale navigation-block flag when opening a membership policy in the editor

* MM-69829 - Save cleanly when removing a team membership policy instead of prompting to re-apply

* MM-69829 - Count qualifying team members correctly in the apply-policy confirmation modal

* fix failing test

* do not show apply policy confirmation modal on policy unlink

* Refactor TableEditor state management and integrate deleteAccessControlPolicy action

* fix ci linter for playwright test

* adjust e2e to the new flow - no policy/rule , no blocking, clean state abac off

* MM-69830 - Return an OK body when deleting an access policy so rule removal saves cleanly

* MM-69830 - Soften the self-exclusion message on public teams to reflect advisory enforcement

* MM-69830 - Soften membership-policy notices to advisory copy on public teams

* MM-69830 - Uncheck auto-add when the last team membership rule is removed

* MM-69830 - Warn that removing all rules drops attribute enforcement instead of showing the save-count modal

* MM-69830 - Dispatch the deleted policy id so removing team rules doesn't crash the reducer

* MM-69830 - Skip the self-exclusion block on public teams instead of rewording it

* MM-69830 - Use advisory wording in the save-rules confirmation on public teams

* MM-69830 - Use advisory auto-add descriptions on public teams

* MM-69830 - Block switching a team to private when the admin would be self-excluded by the rules

* fix prettier issues and adjust comments

* MM-69830 - Assert advisory confirmation copy for team admins on public teams

* MM-69830 - Show a saving state on the membership save panel while a confirmed save runs

* MM-69830 - Save immediately when confirming the switch-to-private mode flip

* MM-69829 - Surface delete failures and page through all members in the ABAC team save flow

* MM-69830 - Align the remove-all-rules confirmation with performSave's parent-import check

* MM-69830 - Drop an unnecessary cast on the team policy result and memoize the self-exclusion modal handler
2026-07-25 09:39:56 +02:00
Pablo VélezandMattermost Build 1cc20031fe MM-69857 - Keep parent imports and team scope system-managed on the policy update endpoint (#37625)
Co-authored-by: Mattermost Build <build@mattermost.com>
2026-07-24 13:31:31 +02:00
Julien Tant bc3ad86a23 Update Playbooks plugin to v2.11.1 (incl. FIPS) (#37631) 2026-07-23 08:44:18 -07:00
Devin BinnieandMattermost Build f06971b657 Fix two Session Attribute issues (#37620)
Co-authored-by: Mattermost Build <build@mattermost.com>
2026-07-22 17:53:53 +00:00
Julien TantandClaude Sonnet 5 38b66d2262 [MM-69845] Add Global Attributes access gate to System Console (#37580)
* [MM-69845] Add Global Attributes access gate to System Console

Adds a hidden "Manage Attributes" shell page under the System
Attributes section, gated by a new FeatureFlags.GlobalAttributes
flag, Enterprise license tier, and sysadmin role. Also fixes
admin_sidebar.tsx never threading isCurrentUserSystemAdmin into its
isHidden(...) calls, which would have kept any sysadmin-gated
subsection permanently hidden from the menu regardless of role.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* [MM-69845] Match Manage Attributes gating to the Classification Markings pattern

Ticket was clarified: the sysadmin condition restricts edit access
(isDisabled), not visibility (isHidden) — the page stays reachable to
anyone who can already see the System Attributes section, matching
how Classification Markings gates its own page, rather than the
uniform three-way hide this PR originally built.

This removes the need for the admin_sidebar.tsx/index.ts plumbing fix
from the previous commit: the sidebar only ever evaluates isHidden,
never isDisabled, so no changes to shared sidebar code are needed.
Also adds a one-line placeholder to the shell per reviewer request.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* [MM-69845] Add e2e test for Global Attributes access gate

Covers the two QA-relevant states from the PR: feature flag off hides
the page regardless of license, and flag-on with an Enterprise+
license shows the empty shell with its placeholder text. Modeled on
classification_markings.spec.ts, the closest existing precedent for a
feature-flag + license-tier gated System Console page.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* [MM-69845] Address review feedback on e2e/unit test coverage

- Restore the GlobalAttributes flag to its pre-test value in
  afterAll instead of forcing it to false, so the e2e spec doesn't
  leave a config side effect on environments where it was already on.
- Remove the redundant systemConsolePage.goto() before navigating
  straight to the Manage Attributes path.
- Scope the flag-off absence assertion to the sidebar instead of the
  whole page, so an unrelated "Manage Attributes" string elsewhere
  can't produce a false pass.
- Add an unlicensed-server case to the isHidden predicate matrix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* [MM-69845] Scope remaining e2e assertions to testid, drop ambiguous .first()

Switch the sidebar locator from a CSS class to getByTestId('admin-sidebar'),
matching the data-testid already on that element. Split the flag-on test's
single .first() assertion into two testid-scoped assertions (sidebar entry,
page heading) instead of relying on DOM-order luck between the two elements
that both render "Manage Attributes".

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* [MM-69845] Fix prettier formatting in global_attributes.spec.ts

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* [MM-69845] Skip flag cleanup when env/SplitKey overrides GlobalAttributes

getConfig() returns the effective value, which reflects any env var
or SplitKey override rather than what's actually persisted. Writing
that effective value back in afterAll would silently corrupt the
persisted config on environments where an override is active. Add
isGlobalAttributesFlagOverridden() (checks getEnvironmentConfig() for
an env override and ServiceSettings.SplitKey for live flag sync) and
skip capturing/restoring the flag entirely when either is true.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* [MM-69845] Revert override-detection layer in e2e cleanup

No other spec in this suite checks env/SplitKey overrides before
restoring a feature flag (classification_markings.spec.ts and
global_classification_banner.spec.ts both toggle flags directly with
no such guard). Matching that established convention over the
theoretically-more-correct-but-unprecedented approach; the simple
capture-and-restore is enough.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-22 09:50:44 -07:00
Scott Bishel a8f55e5327 [MM-69885] Allow integrations to update mm_blocks_actions on their own posts (#37583)
* [MM-69885] Allow integrations to update mm_blocks_actions on their own posts

* prune mm_blocks_actions on post updates to match remaining buttons
2026-07-22 09:01:45 -06:00
Andre Vasconcelos 1bfa2e5113 Bumping prepackaged boards version to 9.3.1 (#37594) 2026-07-22 16:36:47 +03:00
25f3a75cb7 [MM-67163] Scheduled Recaps (#35495)
* docs: map existing codebase with focus on recaps

- STACK.md - Technologies and dependencies
- ARCHITECTURE.md - System design and patterns
- STRUCTURE.md - Directory layout
- CONVENTIONS.md - Code style and patterns
- TESTING.md - Test structure
- INTEGRATIONS.md - External services
- CONCERNS.md - Technical debt and issues

* docs: initialize project

Scheduled AI Recaps — enable recurring scheduled recaps instead of only manual one-off summaries

* chore: add project config

Mode: yolo
Depth: standard
Parallelization: enabled

* docs: complete domain research for scheduled recaps

Research files:
- STACK.md: Job server patterns, ScheduledPosts reference implementation
- FEATURES.md: Table stakes vs differentiators, anti-features to avoid
- ARCHITECTURE.md: Component boundaries, data flow, 5-phase build order
- PITFALLS.md: Timezone, cluster, schema critical pitfalls
- SUMMARY.md: Synthesized findings and roadmap implications

Key findings:
- Use ScheduledPosts polling pattern, not JobServer schedulers
- Store user intent + pre-computed NextRunAt for efficient polling
- Leverage existing recap worker, extend with time_period support
- Critical: Get timezone/schema right in Phase 1 to avoid migrations

* docs: define v1 requirements

35 requirements across 5 categories:
- Scheduling (7)
- Management (8)
- Backend Infrastructure (10)
- Frontend Wizard (7)
- Frontend Scheduled Tab (7)

Topic-based recaps deferred to v2

* docs: create roadmap (5 phases)

* docs(01): capture phase context

Phase 01: Database Foundation
- OpenCode discretion on implementation patterns
- Implicit requirements from downstream phases noted

* docs(01-database-foundation): research phase domain

Phase 1: Database Foundation
- Analyzed existing Recap and ScheduledPost patterns
- Documented schema design for recurring schedules
- Identified timezone/DST handling approach using IANA zones
- Catalogued store interface patterns
- Documented common pitfalls and anti-patterns

* docs(01): create phase plan for database foundation

Phase 01: Database Foundation
- 2 plans in 2 waves
- Wave 1: Model + migration (01-01-PLAN.md)
- Wave 2: Store layer + tests (01-02-PLAN.md)
- Ready for execution

* feat(01-01): create ScheduledRecap model with DST-aware NextRunAt computation

- Add ScheduledRecap struct with all required fields for schedule configuration
- Add day-of-week bitmask constants matching Go's time.Weekday (Sunday=0)
- Add channel mode constants (specific, all_unreads)
- Add time period constants (last_24h, last_week, since_last_read)
- Implement ComputeNextRunAt with timezone-aware scheduling using time.LoadLocation
- Implement IsValid for input validation
- Add PreSave/PreUpdate lifecycle methods
- Add Auditable method for audit logging

* feat(01-01): create database migration for ScheduledRecaps table

- Create ScheduledRecaps table with all required columns
- Add index for user queries (idx_scheduled_recaps_user_id)
- Add index for scheduler polling (idx_scheduled_recaps_next_run_at)
- Add composite index for efficient scheduler query (idx_scheduled_recaps_enabled_next_run)
- Add index for user + soft delete queries (idx_scheduled_recaps_user_delete)
- Add down migration to drop all indexes and table

* test(01-01): add unit tests for ScheduledRecap with DST edge cases

- Test day-of-week bitmask constants and operations
- Test ComputeNextRunAt for Monday-only, weekday, and every-day schedules
- Test timezone handling - different timezones produce different UTC millis
- Test DST spring forward edge case (March 2024) - Go normalizes non-existent times
- Test DST fall back edge case (November 2024) - Go uses first occurrence
- Test error cases: invalid timezone, invalid time format, zero days
- Test IsValid method for all validation rules
- Test PreSave and PreUpdate lifecycle methods
- Test Auditable method returns expected fields
- Fix ComputeNextRunAt to validate time format using regex before parsing

* docs(01-01): complete ScheduledRecap model and migration plan

Tasks completed: 3/3
- Task 1: Create ScheduledRecap model with constants and NextRunAt computation
- Task 2: Create database migration for ScheduledRecaps table
- Task 3: Add unit tests for ComputeNextRunAt with DST edge cases

SUMMARY: .planning/phases/01-database-foundation/01-01-SUMMARY.md

* feat(01-02): add ScheduledRecapStore interface to store.go

- Add ScheduledRecapStore interface with CRUD operations (Save, Get, Update, Delete)
- Add query operations (GetForUser, GetDueBefore)
- Add state update methods (UpdateNextRunAt, MarkExecuted, SetEnabled)
- Register ScheduledRecap() method in main Store interface

* feat(01-02): create SqlScheduledRecapStore implementation

- Implement CRUD operations (Save, Get, Update, Delete with soft delete)
- Implement GetForUser with pagination for user's scheduled recaps
- Implement GetDueBefore for scheduler polling query
- Implement efficient state updates (UpdateNextRunAt, MarkExecuted, SetEnabled)
- Handle JSON serialization/deserialization of ChannelIds array
- Follow existing patterns from recap_store.go

* feat(01-02): register ScheduledRecapStore in SqlStore

- Add scheduledRecap field to SqlStoreStores struct
- Initialize newSqlScheduledRecapStore in NewSqlStore
- Add ScheduledRecap() accessor method to SqlStore

* test(01-02): add comprehensive tests for ScheduledRecapStore

- Test CRUD operations (Save, Get, Update, Delete)
- Test GetForUser with pagination
- Test GetDueBefore scheduler query filtering
- Test state update methods (UpdateNextRunAt, MarkExecuted, SetEnabled)
- Test ChannelIds JSON serialization (array, empty, nil)
- All 13 test cases pass

* docs(01-02): complete ScheduledRecapStore plan

Tasks completed: 4/4
- Add ScheduledRecapStore interface to store.go
- Create SqlScheduledRecapStore implementation
- Register ScheduledRecapStore in SqlStore
- Create comprehensive store tests

SUMMARY: .planning/phases/01-database-foundation/01-02-SUMMARY.md

* fix(01): regenerate store mocks for ScheduledRecapStore

* docs(01): complete Database Foundation phase

Phase 1: Database Foundation
- 2 plans executed (model + store)
- 3 requirements complete (INFRA-01, INFRA-02, INFRA-10)
- Goal verified ✓

* docs(03): research phase scheduler integration domain

Phase 03: Scheduler Integration
- Standard stack identified (Mattermost job system)
- Architecture patterns documented (Scheduler + Worker pattern)
- Cluster-safe execution via leader-only scheduling
- Pitfalls catalogued (duplicate jobs, race conditions)
- Code examples from existing codebase patterns

* docs(02): create phase 2 API layer plans

Phase 02: API Layer
- 2 plans in 2 waves
- Plan 01 (Wave 1): App layer methods for CRUD + pause/resume
- Plan 02 (Wave 2): API handlers, routes, params, audit events
- Ready for execution

* feat(02-01): create App layer CRUD methods for ScheduledRecap

- Add CreateScheduledRecap with session-based userId, validation, NextRunAt computation
- Add GetScheduledRecap to retrieve by ID
- Add GetScheduledRecapsForUser with pagination
- Add UpdateScheduledRecap with NextRunAt recomputation when enabled
- Add DeleteScheduledRecap for soft delete
- Add PauseScheduledRecap to disable without deleting
- Add ResumeScheduledRecap with NextRunAt recomputation before enabling
- Regenerate store layer files for ScheduledRecapStore interface

* docs(02-01): complete App layer CRUD methods plan

Tasks completed: 3/3
- Regenerate store mocks (already complete from Phase 1)
- Create App layer file with CRUD methods
- Verify app layer interfaces (no interface file exists)

SUMMARY: .planning/phases/02-api-layer/02-01-SUMMARY.md

* feat(02-02): add audit event constants for scheduled recaps

- AuditEventCreateScheduledRecap for recap configuration creation
- AuditEventGetScheduledRecap for viewing single recap
- AuditEventGetScheduledRecaps for listing user recaps
- AuditEventUpdateScheduledRecap for configuration updates
- AuditEventDeleteScheduledRecap for recap deletion
- AuditEventPauseScheduledRecap for pausing execution
- AuditEventResumeScheduledRecap for resuming execution

* feat(02-02): add ScheduledRecapId to params and context

- Add ScheduledRecapId field to Params struct
- Parse scheduled_recap_id from URL path variables
- Add RequireScheduledRecapId validation method

* feat(02-02): add route registration for scheduled recaps

- Add ScheduledRecaps and ScheduledRecap routes to Routes struct
- Initialize route prefixes in Init function
- Add InitScheduledRecap call in initialization

* feat(02-02): create API handlers for scheduled recaps

- InitScheduledRecap registers all 7 API routes
- createScheduledRecap validates required fields and creates recap
- getScheduledRecap retrieves with authorization check
- getScheduledRecaps lists user's recaps with pagination
- updateScheduledRecap updates with ownership verification
- deleteScheduledRecap soft deletes with authorization
- pauseScheduledRecap disables execution with ownership check
- resumeScheduledRecap re-enables with NextRunAt recomputation
- All handlers include audit logging and feature flag check

* docs(02-02): complete API handlers plan

Tasks completed: 4/4
- Add audit event constants for scheduled recaps
- Add ScheduledRecapId to params and context
- Add route registration in api.go
- Create API handler file

SUMMARY: .planning/phases/02-api-layer/02-02-SUMMARY.md

* docs(02): complete API Layer phase

Phase 2: API Layer
- 2 plans executed in 2 waves
- 16/16 must-haves verified
- INFRA-05 through INFRA-09 complete
- Ready for Phase 3: Scheduler Integration

* docs(03): create phase plan for scheduler integration

Phase 03: Scheduler Integration
- 2 plans in 2 waves
- Wave 1: Job constant, scheduler, worker
- Wave 2: Job registration, App method
- Ready for execution

* fix(03): revise plan 03-02 Task 2 for worker context compatibility

- CreateRecapFromSchedule now creates recap directly via store
- Uses sr.UserId instead of rctx.Session().UserId (unavailable in worker)
- Creates JobTypeRecap job directly instead of delegating to CreateRecap
- Updated key_links to reflect store and job linkage

* docs(04): capture phase context

Phase 04: Scheduled Tab
- Implementation decisions documented
- Phase boundary established
- Figma references captured (123:62940, 123:19772)

* feat(03-01): add JobTypeScheduledRecap constant

- Add JobTypeScheduledRecap constant with value 'scheduled_recap'
- Add to AllJobTypes slice for job type validation

* feat(03-01): create ScheduledRecap scheduler

- Add Scheduler struct wrapping PeriodicScheduler
- 1-minute polling interval (SchedulerPollingInterval constant)
- Enabled when cfg.FeatureFlags.EnableAIRecaps is true
- ScheduleJob polls GetDueBefore for due recaps
- Creates job with CreateJobOnce for deduplication
- Job data: scheduled_recap_id, user_id, channel_ids, agent_id

* feat(03-01): create ScheduledRecap worker

- Define AppIface interface with CreateRecapFromSchedule method
- Use SimpleWorker pattern following recap/worker.go pattern
- Enabled when cfg.FeatureFlags.EnableAIRecaps is true
- Extract job data: scheduled_recap_id, user_id, channel_ids, agent_id
- Verify ScheduledRecap exists and is enabled before execution
- Call app.CreateRecapFromSchedule to create the actual recap
- Compute next run time using sr.ComputeNextRunAt
- Call MarkExecuted to update LastRunAt, NextRunAt, RunCount atomically
- Disable non-recurring schedules after execution

* docs(03-01): complete job system components plan

Tasks completed: 3/3
- Add JobTypeScheduledRecap constant
- Create scheduler implementation
- Create worker implementation

SUMMARY: .planning/phases/03-scheduler-integration/03-01-SUMMARY.md

* docs(04): create phase 4 plans - Scheduled Tab UI

Phase 04: Frontend - Scheduled Tab
- 4 plans in 4 waves
- Wave 1: TypeScript types + Client4 API methods
- Wave 2: Redux layer (action types, actions, reducer, selectors)
- Wave 3: ScheduledRecapItem component (card UI with toggle, menu)
- Wave 4: Scheduled tab integration with human verification

Covers requirements TAB-01 through TAB-07 and MGMT-01 through MGMT-08

* feat(03-02): add ScheduledRecap job registration in initJobs

- Register JobTypeScheduledRecap with worker and scheduler
- Import scheduled_recap package for job components
- Worker uses App interface for CreateRecapFromSchedule
- Scheduler polls for due recaps at 1-minute intervals

* feat(03-02): implement CreateRecapFromSchedule App method

- Create Recap from ScheduledRecap configuration
- Use sr.UserId instead of session (worker context has no session)
- Create recap record directly via store
- Create JobTypeRecap job to trigger processing
- Handle both specific channels and all_unreads mode

* docs(03-02): complete app integration plan

Tasks completed: 3/3
- Add job registration in initJobs
- Implement CreateRecapFromSchedule App method
- Verify full integration compiles

SUMMARY: .planning/phases/03-scheduler-integration/03-02-SUMMARY.md

* docs(phase-3): complete scheduler integration phase

* feat(04-01): add ScheduledRecap TypeScript types

- Add ScheduledRecap type matching Go model fields
- Add ScheduledRecapInput type for create/update operations
- Types exported via @mattermost/types/recaps

* feat(04-01): add Client4 scheduled recap route and imports

- Add getScheduledRecapsRoute() method returning /scheduled_recaps endpoint
- Import ScheduledRecap and ScheduledRecapInput types

* feat(04-01): add Client4 scheduled recap API methods

- createScheduledRecap: POST /scheduled_recaps
- getScheduledRecaps: GET /scheduled_recaps (paginated)
- getScheduledRecap: GET /scheduled_recaps/:id
- updateScheduledRecap: PUT /scheduled_recaps/:id
- deleteScheduledRecap: DELETE /scheduled_recaps/:id
- pauseScheduledRecap: POST /scheduled_recaps/:id/pause
- resumeScheduledRecap: POST /scheduled_recaps/:id/resume

* docs(04-01): complete TypeScript types and Client4 methods plan

Tasks completed: 3/3
- Add ScheduledRecap TypeScript type
- Add Client4 scheduled recap route helper
- Add Client4 scheduled recap API methods

SUMMARY: .planning/phases/04-scheduled-tab/04-01-SUMMARY.md

* feat(04-02): add scheduled recap action types

- GET_SCHEDULED_RECAPS_REQUEST/SUCCESS/FAILURE
- RECEIVED_SCHEDULED_RECAP and RECEIVED_SCHEDULED_RECAPS
- PAUSE_SCHEDULED_RECAP_REQUEST/SUCCESS/FAILURE
- RESUME_SCHEDULED_RECAP_REQUEST/SUCCESS/FAILURE
- DELETE_SCHEDULED_RECAP_REQUEST/SUCCESS/FAILURE

* feat(04-02): add scheduled recap Redux actions

- getScheduledRecaps: fetches paginated scheduled recaps
- pauseScheduledRecap: pauses a scheduled recap
- resumeScheduledRecap: resumes a paused scheduled recap
- deleteScheduledRecap: deletes a scheduled recap

* feat(04-02): add scheduled recaps to reducer

- Add scheduledRecaps to RecapsState type
- Handle RECEIVED_SCHEDULED_RECAP for single recap
- Handle RECEIVED_SCHEDULED_RECAPS for bulk updates
- Handle DELETE_SCHEDULED_RECAP_SUCCESS for removal

* feat(04-02): add scheduled recap selectors

- getScheduledRecapsState: base selector for raw state
- getAllScheduledRecaps: returns all scheduled recaps as array
- getActiveScheduledRecaps: filters enabled, non-deleted recaps
- getPausedScheduledRecaps: filters disabled, non-deleted recaps
- getScheduledRecapById: returns single recap by ID

* feat(04-02): update GlobalState type for scheduled recaps

- Import ScheduledRecap type from recaps
- Add scheduledRecaps field to recaps entity state

* docs(04-02): complete Redux store and actions plan

Tasks completed: 5/5
- Add scheduled recap action types
- Add scheduled recap Redux actions
- Add scheduled recaps to reducer
- Add scheduled recap selectors
- Update GlobalState type for scheduled recaps

SUMMARY: .planning/phases/04-scheduled-tab/04-02-SUMMARY.md

* feat(04-03): add i18n strings for scheduled recap UI

- Add scheduled tab label
- Add active/paused toggle states
- Add run stats strings (last run, run count, never run, next run)
- Add toast messages for pause/resume/delete
- Add kebab menu labels (edit, delete)
- Add delete confirmation modal strings
- Add empty state strings (title, description, cta)
- Add day formatting strings (weekdays, weekend, everyday, individual days)
- Add schedule format string

* feat(04-03): create useScheduleDisplay hook for schedule formatting

- Add bitmask constants matching Go model (Sun=1, Mon=2, etc.)
- formatDaysOfWeek: smart groupings (Every day, Weekdays, Weekends) or comma-separated
- formatTimeOfDay: locale-appropriate 12/24hr time from HH:MM
- formatSchedule: combines days and time with i18n format string
- formatNextRun: smart relative formatting (Today, Tomorrow, Day name, Date)
- formatLastRun: formatted date or 'Never run'
- formatRunCount: pluralized run count

* feat(04-03): create ScheduledRecapItem component

- Render card with title and schedule pattern subtitle
- Show next run time when schedule is active
- Toggle between Active/Paused states with pause/resume actions
- Run stats (last run, run count) appear on hover
- Kebab menu with Edit and Delete options
- Delete confirmation modal with FormattedMessage
- Use useScheduleDisplay hook for all formatting

* feat(04-03): add ScheduledRecapItem styles

- Card with border, radius, and hover state
- Flexbox layout with title/subtitle and actions
- Title with truncation (ellipsis) for long names
- Subtitle with metadata separator styling
- Run stats with opacity transition on hover
- Toggle button min-width for consistent sizing
- Kebab menu button hover state

* docs(04-03): complete ScheduledRecapItem component plan

Tasks completed: 4/4
- Add i18n strings for scheduled recap UI
- Create useScheduleDisplay hook for schedule formatting
- Create ScheduledRecapItem component
- Add ScheduledRecapItem styles

SUMMARY: .planning/phases/04-scheduled-tab/04-03-SUMMARY.md

* feat(04-04): create ScheduledRecapsEmptyState component

- Empty state displays when no scheduled recaps exist
- Shows illustration with icons, title, description
- CTA button to create first recap
- Supports disabled state when agents bridge is disabled

* feat(04-04): create ScheduledRecapsList component

- Renders empty state when no scheduled recaps exist
- Maps over scheduled recaps to render ScheduledRecapItem
- Passes edit and create handlers through to children

* feat(04-04): add Scheduled tab to main Recaps component

- Add Scheduled tab after Unread and Read tabs
- Fetch scheduled recaps on mount with getScheduledRecaps
- Display ScheduledRecapsList when on scheduled tab
- Wire up edit handler (opens create modal - Phase 5 adds pre-fill)
- Import scheduled_recap_item.scss for styling

* style(04-04): add SCSS styles for scheduled recaps

- Add .scheduled-recaps-list styles (flex column, centered, gap)
- Add .scheduled-recaps-empty-state styles (centered, illustration, text)
- Consistent with existing recap UI styling patterns

* docs(04-04): complete Scheduled tab integration plan

* docs(04): update STATE.md for phase 4 completion

* docs(04): complete Scheduled Tab phase

Phase 4: Scheduled Tab
- 4 plans executed across 4 waves
- 15/15 requirements verified
- Human verified UI works correctly

* docs(05): capture phase context

Phase 05: Enhanced Wizard
- Implementation decisions documented
- Phase boundary established

* docs(05): add research hints for component discovery

* docs(05): research phase domain for enhanced wizard

Phase 05: Enhanced Wizard - Frontend Implementation
- Standard stack identified (existing codebase components)
- Architecture patterns documented (multi-step modal, bitmask days)
- Pitfalls catalogued (timezone, validation, edit mode)
- Code examples from codebase referenced

* docs(05): create phase plan for enhanced wizard

Phase 05: Frontend - Enhanced Wizard
- 6 plans in 3 waves
- Wave 1: Redux actions, DayOfWeekSelector
- Wave 2: ScheduleConfiguration, Run once toggle
- Wave 3: Modal integration, Edit wiring
- Ready for execution

* feat(05-01): add action type constants for create/update scheduled recap

- CREATE_SCHEDULED_RECAP_REQUEST/SUCCESS/FAILURE
- UPDATE_SCHEDULED_RECAP_REQUEST/SUCCESS/FAILURE

* feat(05-02): create DayOfWeekSelector component

- Bitmask-based day selection matching server model
- Monday-first ordering for work schedule intuition
- XOR toggle for clean state management
- aria-pressed accessibility support

* feat(05-01): add createScheduledRecap async action

- Takes ScheduledRecapInput parameter
- Calls Client4.createScheduledRecap
- Dispatches RECEIVED_SCHEDULED_RECAP on success
- Follows existing pauseScheduledRecap pattern

* feat(05-02): add DayOfWeekSelector styles

- Flexbox layout with 8px gap between buttons
- 40x40px day buttons with hover states
- Selected state uses button-bg color
- Error and disabled state styling

* feat(05-01): add updateScheduledRecap async action

- Takes id and ScheduledRecapInput parameters
- Calls Client4.updateScheduledRecap
- Dispatches RECEIVED_SCHEDULED_RECAP on success
- Follows existing action patterns

* docs(05-02): complete DayOfWeekSelector plan

Tasks completed: 2/2
- DayOfWeekSelector component with bitmask state
- Styled button group with toggle/hover/error states

SUMMARY: .planning/phases/05-enhanced-wizard/05-02-SUMMARY.md

* docs(05-01): complete Redux actions for scheduled recaps plan

Tasks completed: 3/3
- Add action type constants for create/update scheduled recap
- Add createScheduledRecap async action
- Add updateScheduledRecap async action

SUMMARY: .planning/phases/05-enhanced-wizard/05-01-SUMMARY.md

* feat(05-04): add run once toggle to RecapConfiguration

- Add runOnce, setRunOnce, and isEditMode props to Props type
- Import Toggle component
- Add run once toggle section at bottom of Step 1
- Toggle hidden when isEditMode is true
- Include descriptive text below toggle

* feat(05-03): create ScheduleConfiguration component for Step 3

- Add day-of-week selection using DayOfWeekSelector
- Add time picker with 30-minute intervals and locale-aware formatting
- Add time period dropdown (Previous day, Last 3 days, Last 7 days)
- Add custom instructions textarea with 500 char limit
- Add next run preview with timezone support
- Use getCurrentTimezone selector for user timezone

* feat(05-04): add run once toggle styles

- Add .run-once-group with top separator border
- Style toggle and label with proper alignment
- Add description text with left margin for alignment
- Use consistent spacing and typography

* feat(05-03): add Step 3 schedule configuration styles

- Add step-three layout with vertical flex and gap
- Add form-group styling with label and error states
- Add next-run-preview styling with background and subtle text
- Add textarea overrides for custom instructions input

* docs(05-04): complete run once toggle plan

Tasks completed: 2/2
- Add run once toggle to RecapConfiguration
- Add run once toggle styles

SUMMARY: .planning/phases/05-enhanced-wizard/05-04-SUMMARY.md

* docs(05-03): complete ScheduleConfiguration plan

Tasks completed: 2/2
- Create ScheduleConfiguration component
- Add Step 3 styles to SCSS

SUMMARY: .planning/phases/05-enhanced-wizard/05-03-SUMMARY.md

* feat(05-05): add schedule state and edit mode props to modal

- Add editScheduledRecap prop for edit mode detection
- Add schedule state (daysOfWeek, timeOfDay, timePeriod, customInstructions)
- Add runOnce state and validation state (daysError, timeError)
- Add useEffect to pre-fill form in edit mode
- Import createScheduledRecap, updateScheduledRecap actions
- Import ScheduleConfiguration component and getCurrentTimezone selector

* feat(05-06): update handleEditScheduledRecap to pass scheduled recap to modal

- Find scheduled recap by ID from scheduledRecaps array
- Pass editScheduledRecap via dialogProps to CreateRecapModal
- Early return if scheduled recap not found

* feat(05-05): update step navigation for run once and schedule flows

- Update handleNext to clear validation errors on navigation
- Update handlePrevious to clear validation errors on navigation
- Update getTotalSteps for run once (2-3 steps) vs scheduled (always 3)
- Update getActualStep for proper step indicator mapping

* feat(05-05): update renderStep for schedule vs run once flows

- Pass runOnce, setRunOnce, and isEditMode props to RecapConfiguration
- Show ChannelSummary for run once mode at step 3
- Show ScheduleConfiguration for scheduled mode at step 3
- Pass all schedule state props to ScheduleConfiguration component

* feat(05-05): update handleSubmit for immediate and scheduled recaps

- Add schedule field validation for non-run-once mode
- Dispatch createRecap for run once mode (existing behavior)
- Dispatch updateScheduledRecap for edit mode
- Dispatch createScheduledRecap for new scheduled recaps
- Navigate to ?tab=scheduled after creating/editing scheduled recap
- Add proper error messages for schedule validation failures

* feat(05-05): update modal header and button text for edit mode

- Update canProceed to validate schedule fields in step 3
- Add getConfirmButtonText helper for context-aware button text
- Show 'Start recap' for run once, 'Save changes' for edit mode
- Show 'Create schedule' for new scheduled recaps
- Update headerText to show 'Edit your recap' in edit mode

* docs(05-05): complete wizard integration plan

Tasks completed: 5/5
- Add schedule state and edit mode props
- Update step navigation for run once and schedule flows
- Update renderStep for schedule vs run once flows
- Update handleSubmit for immediate and scheduled recaps
- Update modal header and button text for edit mode

SUMMARY: .planning/phases/05-enhanced-wizard/05-05-SUMMARY.md

* fix(05-06): JSON.stringify body in scheduled recap API calls

createScheduledRecap and updateScheduledRecap were passing objects
directly to doFetch body, causing '[object Object]' to be sent instead
of JSON. Fixed to match createRecap pattern.

* fix(05-06): align time period values with server model

Frontend was using 'last_3_days' and 'last_7_days' but server expects
'last_24h', 'last_week', and 'since_last_read'. Updated options to match.

* fix(05-06): remove duplicate border on custom instructions textarea

- GenericModal adds a border to all .form-control elements
- Input widget's Input_fieldset already provides a border container
- This caused a double-border visual glitch on the textarea
- Added border: none to the inner textarea to fix the issue

* fix(05-06): reserve space for next run preview to prevent modal height jump

- Always render next-run-preview container (previously conditionally rendered)
- Use visibility:hidden instead of not rendering when no preview available
- Add non-breaking space placeholder to maintain consistent element height
- Prevents jarring visual jump when user selects a day of the week

* fix(05-06): remove border/background from next-run-preview

- Remove padding, border-radius, and background-color from .next-run-preview
- Style as plain text with subtle color and smaller font size
- Keep margin for appropriate spacing from time selector

* fix(05-06): use abbreviated timezone in next recap preview

- Use Intl.DateTimeFormat with timeZoneName: 'short' to get timezone
  abbreviation (e.g., EST, PST, EDT) instead of full label
- Remove unused getCurrentTimezoneLabel selector import
- Preview now shows 'Monday at 9:00 AM (EST)' instead of
  'Monday at 9:00 AM ((UTC-05:00) Eastern Time (US & Canada))'

* fix(05-06): prevent modal height jump when next run preview appears

- Move next-run-preview inside time-selection-group as helper text
- Add min-height: 16px to reserve space when preview is hidden
- Use visibility: hidden instead of display: none for consistent height
- Reduce step-three gap from 20px to 16px for better spacing
- Add margin-bottom: 0 to form-group to override default spacing

* fix(05-06): add section titles and fix spacing in schedule configuration

- Add section titles per Figma design (Heading 100 style):
  - 'When would you like your summary sent?' as main header
  - 'On which days should your recap run?' for days section
  - 'At what time?' for time section
  - 'Select a time period for your recap to cover' for time period
  - 'Additional instructions for {agentName}' for custom instructions
- Pass agentName prop from parent to show selected agent name
- Fix spacing: reserve space for next-run preview with container
  to prevent time period section from jumping when preview appears
- Update SCSS with schedule-section groups and proper spacing

* fix(05-06): remove duplicate title and fix subtitle-dropdown spacing

- Remove 'When would you like your summary sent?' duplicate title
- Add scoped CSS rule for 12px total spacing between subtitle and dropdown

* fix(05-06): use standard Toggle without text labels for active/paused state

- Remove onText/offText props from Toggle component
- Add ariaLabel for accessibility (describes toggle state and action)
- Update SCSS to remove min-width constraint that was for text display
- Navigation to scheduled tab after creating scheduled recap already works correctly

* fix(05-06): toggle color and tab navigation after creating scheduled recap

- Use btn-toggle-primary class for scheduled recap toggle to display proper button-bg color
- Add useQuery hook to read tab query parameter from URL
- Sync activeTab state with URL tab parameter to enable navigation after modal close

* fix(05-06): sync tab state with URL bidirectionally

- Add handleTabChange callback that updates both state and URL
- Use history.replace() to update URL without polluting browser history
- Remove tab param from URL when switching to 'unread' (default tab)
- Simplify URL sync useEffect to always update from tabParam
- This enables proper navigation after creating scheduled recaps

* fix(05-06): fix navigation to scheduled tab after creating scheduled recap

- Replace useRouteMatch() with getCurrentRelativeTeamUrl selector
- Modal was using route match which returned wrong URL context (modal is rendered at root level)
- Use team selector to get correct team URL for navigation
- Update test to remove unnecessary useRouteMatch mock

* fix(05-06): sort scheduled recaps by newest first

- Update getAllScheduledRecaps selector to sort by create_at descending
- Follows same pattern as other recap selectors (getUnreadRecaps, getReadRecaps)
- Derived selectors (getActiveScheduledRecaps, getPausedScheduledRecaps) inherit sort order

* docs(05-06): complete edit wiring and UI polish plan

* docs(phase-05): complete Enhanced Wizard phase

Phase 5: Enhanced Wizard
- 6 plans executed across 3 waves
- 13 requirements verified
- Multi-step wizard for creating/editing scheduled recaps
- Run once and scheduled flows
- Full edit mode with pre-fill
- Extensive UI polish based on human feedback

All 39 requirements complete. Milestone ready for audit.

* docs(v1): milestone audit complete - all requirements satisfied

- 39/39 requirements verified
- 5/5 phases passed
- 100% cross-phase integration
- 5/5 E2E flows complete
- 2 minor tech debt items (non-blocking)

* chore: remove .planning from git tracking

- Add .planning to .gitignore
- Remove .planning files from git index (kept locally)
- Planning files are for local development only

* feat(06-01): add AIRecapSettings and RecapLimitSettings structs

- RecapLimitSettings with 7 limit fields (recaps/day, scheduled, channels, posts, tokens, posts/day, cooldown)
- AIRecapSettings with master toggle and per-limit enforcement toggles
- SetDefaults methods with sensible defaults (10 recaps/day, 5 scheduled, etc.)
- isValid/IsValid validation methods enforcing natural minimums

* feat(06-01): integrate AIRecapSettings into Config struct

- Add AIRecapSettings field to Config struct
- Call AIRecapSettings.SetDefaults() in Config.SetDefaults()
- Call AIRecapSettings.IsValid() in Config.IsValid()

* test(06-01): add tests for AIRecapSettings and RecapLimitSettings

- TestAIRecapSettingsSetDefaults: verifies all defaults match spec
- TestRecapLimitSettingsValidation: verifies validation rejects invalid values
- TestAIRecapSettingsPreservesExistingValues: verifies SetDefaults preserves existing
- TestAIRecapSettingsIsValid: verifies IsValid delegates to DefaultLimits

* feat(06-02): create EffectiveRecapLimits struct

- Add EffectiveRecapLimits struct with 7 resolved limit fields
- Add LimitSource type with system/group/user constants
- Add UnlimitedValue constant (-1) for disabled limits
- Add IsLimitEnabled helper function for enforcement code

* feat(06-02): create GetEffectiveLimits resolution function

- Add GetEffectiveLimits(userID) returning resolved limits for any user
- Resolve limits from AIRecapSettings.DefaultLimits config
- Apply per-limit enforcement toggles (disabled = -1 unlimited)
- Add helper functions getValueOrDefault and getBoolOrDefault
- Structure for Phase 8 group/user resolution with TODOs

* test(06-02): add tests for GetEffectiveLimits function

- TestGetEffectiveLimitsDefaults verifies system defaults returned
- TestGetEffectiveLimitsWithDisabledToggle verifies -1 returned for disabled limits
- TestGetEffectiveLimitsWithCustomDefaults verifies custom config honored
- TestGetEffectiveLimitsAllTogglesDisabled verifies all -1 when all disabled
- TestGetEffectiveLimitsUnlimitedConfigValue verifies -1 config value honored
- TestIsLimitEnabled verifies helper correctly identifies enabled limits

* feat(07-01): add RecapStatusSkipped constant and SkipReason field

- Add RecapStatusSkipped constant for recaps skipped due to limit violations
- Add SkipReasonDailyLimit and SkipReasonCooldown skip reason constants
- Add ScheduledRecapId field to Recap struct for tracking scheduled recaps
- Add SkipReason field to Recap struct for tracking why recap was skipped
- Update Auditable() method to include new fields
- Update recapColumns and recapToMap to include new fields

* feat(07-01): add store interface methods for limit enforcement

- Add CountForUserSince to RecapStore for daily limit enforcement
- Add GetLastCompletedManualRecap to RecapStore for cooldown checking
- Add CountForUser to ScheduledRecapStore for max scheduled recaps limit
- Update mock implementations for both stores

* feat(07-01): implement store methods in SQL stores

- Implement CountForUser in SqlScheduledRecapStore
  - Counts active (non-deleted, enabled) scheduled recaps for a user
- Implement CountForUserSince in SqlRecapStore
  - Counts recaps since timestamp, excluding skipped recaps
- Implement GetLastCompletedManualRecap in SqlRecapStore
  - Returns most recent completed manual recap (no ScheduledRecapId)
  - Returns nil, nil when no manual recap exists

* feat(07-03): add daily limit check to scheduled recap worker

- Add GetEffectiveLimits and GetUser to AppIface for limit checking
- Check MaxRecapsPerDay before executing scheduled recap
- Create skipped recap record when daily limit exceeded
- Use user's timezone for midnight calculation
- Update next run time even when skipping (scheduler moves on)

* feat(07-03): add cooldown check to manual recap creation

- Check CooldownMinutes before allowing manual recap creation
- Return HTTP 429 with retry-after info when cooldown active
- Only checks against completed manual recaps (per CONTEXT.md)
- Failed recaps don't consume cooldown (checks completed only)

* feat(07-03): add i18n messages for cooldown errors

- Add cooldown_active error with retry info template
- Add cooldown_check_failed error message
- Uses "Your organization's policy limits..." pattern

* feat(07-02): add limit checks to CreateScheduledRecap

- Add max scheduled recaps limit check using CountForUser store method
- Add max channels per recap limit check against ChannelIds length
- Return HTTP 400 with clear error messages when limits exceeded
- Uses GetEffectiveLimits for limit resolution (ENF-01, ENF-02, ENF-08)

* feat(07-02): add i18n error messages for scheduled recap limits

- Add max_scheduled_reached message with "Your organization's policy limits..." pattern
- Add max_channels_exceeded message with limit and requested count
- Add count_failed internal error message

* test(07-04): add unit tests for post/token truncation

- Test proportional post distribution across channels
- Test minimum 1 post per channel guarantee
- Test empty channel handling
- Test token estimation (4 chars/token heuristic)
- Test token limit truncation removes from largest channels

Verifies ENF-05, ENF-06 truncation implementation.

* test(07-05): add ENF-07 permission preservation tests

- Add tests verifying over-limit users can view/edit/delete existing recaps
- Tests confirm management operations do NOT check limits (grandfathering)
- Tests confirm creation IS still blocked when over limit
- Add migration 000151 for missing ScheduledRecapId/SkipReason columns

ENF-07: Limits only block creation, not management of existing resources

* feat(08-01): create UnlimitedNumberSetting component

- Number input with Unlimited checkbox for admin console settings
- When checked: disables input and sets value to -1 (unlimited)
- When unchecked: enables input and sets value to defaultValue
- Supports disabled state and setByEnv footer

* test(08-01): add unit tests for UnlimitedNumberSetting

- Tests rendering with numeric and unlimited values
- Tests checkbox toggle behavior (check/uncheck)
- Tests number input changes
- Tests disabled state and setByEnv footer
- Tests custom unlimited label and placeholder
- 11 test cases covering core functionality

* feat(08-02): add Recaps subsection to admin_definition.tsx

- Import UnlimitedNumberSetting component
- Add 'recaps' subsection under site configuration section
- Include master enable toggle for AI Recap Limits
- Add 3 grouped sections: Quota Limits, Content Limits, Time Limits
- Configure all 7 limit settings with proper config keys
- Add AIRecapSettings and RecapLimitSettings TypeScript types
- All settings disabled when master toggle is off

* feat(08-02): add i18n strings for Recaps admin section

- Add admin.sidebar.recaps for navigation
- Add admin.site.recaps for section title
- Add admin.recaps.enable.* for master toggle
- Add admin.recaps.sections.* for section descriptions
- Add admin.recaps.max*.* for all limit field labels/descriptions
- Add admin.recaps.cooldownMinutes.* for time limit settings
- Add admin.recaps.unlimited for checkbox label
- Total: 24 new i18n strings

* style(08-02): remove section comments to fix lint errors

Remove inline comments that triggered lines-around-comment lint rule

* feat(09-01): add RecapLimitStatus model and App layer logic

- Add RecapLimitStatus, DailyUsageStatus, CooldownStatus structs
- Implement App.GetRecapLimitStatus with daily usage count and cooldown calculation

* feat(09-01): add GET /api/v4/recaps/limit_status endpoint

- Register route and handler
- Return structured limit status
- Add error translation

* feat(09-01): add TypeScript types for limit status

- Export RecapLimitStatus and related types

* feat(09-02): add recap limit status redux integration

- Add Client4.getRecapLimitStatus method
- Add Redux action, reducer, and selector for limit status
- Update GlobalState and initial state to include limitStatus

* fix(09-02): update CreateRecapModal error handling and tests

- Check dispatch result.error instead of try/catch to handle server errors
- Display server error message (e.g. policy limits) inline
- Fix TypeScript errors: displayName property and missing props in tests

* feat(09-03): implement user-facing limit status UI

* Verify Phase 9: User-Facing UX

* Remove UAT artifact

* fix: UI/UX issues (badge, toggle, input, tooltip)

* fix: Increase RecapUsageBadge tooltip z-index

* blank lines

* [MM-67163] checkpoint: scheduled recaps feature working

All phases (01-09) complete and verified:
- Database foundation with DST-aware scheduling
- API layer for CRUD operations
- Job scheduler and worker
- Scheduled tab UI with list/create/edit/delete
- Enhanced wizard with schedule configuration
- Config settings and admin console section
- Limit enforcement (daily, cooldown, token, post)
- User-facing limit status badge

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* [MM-67163] simplify: reduce duplication and fix issues across scheduled recaps

- Extract requireScheduledRecapOwnership helper for 5 API handlers
- Consolidate ResumeScheduledRecap from 4 store calls to 2
- Remove redundant Get() in PauseScheduledRecap
- Extract advanceSchedule helper in worker to deduplicate skip/success paths
- Remove double PreSave() in store Save method
- Remove dead code fallback in GetEffectiveLimits
- Deduplicate ScheduledRecapInput construction in create modal
- Fix missing fetchRecapLimitStatus import (TS error)
- Extract day-of-week bitmask constants to @mattermost/types/recaps
- Fix hardcoded English strings in schedule_display formatNextRun

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Address CodeRabbit review feedback on scheduled recaps

Server fixes:
- Gate getRecapLimitStatus with requireRecapsEnabled guard
- Normalize error mapping (404 vs 500) in scheduled recap handlers
- Enforce MaxChannelsPerRecap limit in UpdateScheduledRecap
- Return explicit error for unsupported all_unreads mode in scheduled recaps
- Add compensation logic to clean up orphan recaps on job creation failure
- Fix proportional post truncation to strictly enforce maxPosts cap
- Add missing CountForUser retry wrapper in RetryLayerScheduledRecapStore
- Handle NULL ScheduledRecapId in manual recap cooldown lookup
- Exclude soft-deleted rows in scheduled recap Get query

Frontend fixes:
- Pass isCreationBlocked to ScheduledRecapsList empty state
- Fix same-day nextRunAt mislabeled as "Tomorrow" in schedule display
- Handle thunk error results in scheduled recap item actions
- Replace scheduled recaps map on full refresh instead of merging

Made-with: Cursor

* Handle thunk error results in scheduled recap toggle handler

Check dispatch result for errors in handleToggle to prevent
false-success UI flows when pause/resume operations fail.

Made-with: Cursor

* Fix CI failures: mock store, permissions, migrations, lint, and Playwright config

- Add ScheduledRecap() to storetest.Store mock and retrylayer test setup
- Register sysconsole_read_ai_recaps / sysconsole_write_ai_recaps permissions
- Renumber scheduled_recaps migration from 150→156 and recap_skip_fields from 151→157 to resolve version conflicts
- Fix ESLint errors in recap components (operator-linebreak, import order, labels, headers, etc.)
- Add AIRecapSettings to Playwright default_config.ts

Made-with: Cursor

* Address CodeRabbit Round 2 review feedback

- Add OpenAPI spec for GET /api/v4/recaps/limit_status with schema
  definitions for RecapLimitStatus, EffectiveRecapLimits,
  DailyUsageStatus, and CooldownStatus
- Set ScheduledRecapId when creating recaps from schedules to prevent
  cooldown logic from treating scheduled recaps as manual
- Handle past nextRunAt timestamps in schedule display: show "Yesterday"
  for -1 day and full date for older past dates

Made-with: Cursor

* Add missing variable declarations for AI recaps permissions

Made-with: Cursor

* Fix jsx-max-props-per-line lint errors in schedule_configuration.tsx

Made-with: Cursor

* Fix stylelint property order in recap SCSS files

Made-with: Cursor

* Update admin sidebar snapshots to include Recaps section

Made-with: Cursor

* Fix Go lint issues and add OpenAPI specs for scheduled_recap endpoints

- Use max/min builtins instead of if-statements (modernize/minmax)
- Use range-over-int syntax for for-loops (modernize/rangeint)
- Fix tautological Monday&Monday test (staticcheck/SA4000)
- Add OpenAPI specs for all 7 scheduled_recap API routes
- Add ScheduledRecap model definition to definitions.yaml

Made-with: Cursor

* Regenerate i18n en.json for recap-related strings

Made-with: Cursor

* Fix gofmt indentation in recap.go

Made-with: Cursor

* Fix scheduled recap CI regressions

Align the scheduled recap soft-delete store test with the current Get behavior and add the missing scheduled recap i18n strings so server and enterprise checks stay in sync.

Made-with: Cursor

* Address CodeRabbit review feedback on scheduled recaps

- Use session user ID instead of client-controlled recap.UserId for
  limit enforcement in UpdateScheduledRecap (security hardening)
- Add missing i18n entry for app.recap.fetch_posts.app_error
- All other review comments were already addressed in prior commits

Made-with: Cursor

* Fix schedule configuration import order

Reorder the moment import so the webapp lint job passes again on the scheduled recap PR.

Made-with: Cursor

* Allow selecting "all unreads" recap type when no current unreads exist

With scheduled recaps, users should be able to select "all unreads" even
without current unread channels since unreads will exist when the schedule
runs. The "run once" toggle is now disabled when all unreads is selected
with no current unreads, preserving the pre-scheduling behavior of
preventing an immediate recap with nothing to summarize.

Made-with: Cursor

* Fix indentation in recap_configuration.tsx to satisfy eslint

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Fix Recaps test mocks for scheduled recap state.

Keep the webapp test shard green by mirroring the new selectors and mount-time actions used by the Recaps page.

Made-with: Cursor

* Update server/channels/app/recap.go

* Update server/channels/app/recap.go

* Update server/channels/app/recap_limits.go

* Update server/channels/app/recap_limits.go

* Update server/channels/app/scheduled_recap.go

* Update server/channels/app/scheduled_recap.go

* Update webapp/channels/src/components/recaps/scheduled_recaps_empty_state.tsx

* Update server/channels/app/scheduled_recap.go

* Fix gofmt formatting in recap limits

Made-with: Cursor

* Fix recap limits for soft-deleted recaps

Keep deleted recaps in quota and cooldown checks so soft deletion cannot bypass AI usage enforcement.

Made-with: Cursor

* Fix translation

* Fix server check-style: concurrent indexes and lint cleanups

- Use CREATE/DROP INDEX CONCURRENTLY in 000168 scheduled recaps migrations
  (required by mattermost-govet concurrentIndex check).
- gofmt validation constants in scheduled_recap.go.
- Replace string += loops in recap tests with strings.Repeat for modernize linter.

Made-with: Cursor

* Fix 000168 migration: run CONCURRENTLY indexes outside transaction

PostgreSQL rejects CREATE/DROP INDEX CONCURRENTLY inside a transaction.
Morph requires -- morph:nontransactional for these migrations, matching
other index migrations in the repo.

Made-with: Cursor

* Stabilize scheduled Recaps for review

Bring the scheduled Recaps work back into a shippable state by tightening backend scheduling and limit semantics, cleaning up the UI flows, and adding focused Recaps E2E coverage.

Made-with: Cursor

* Fix scheduled recaps lint failures

Made-with: Cursor

* Fix scheduled recap Go lint

Made-with: Cursor

* Fix scheduled recaps Playwright check

Made-with: Cursor

* Sync scheduled recaps i18n catalog

Made-with: Cursor

* Fix server recaps CI checks

Made-with: Cursor

* Stabilize recap server CI setup

Made-with: Cursor

* Recaps: enforce token limit, remove dead truncation code, simplify scheduled worker

- Delete unused multi-channel truncation subsystem (FetchAndTruncatePostsForRecap, truncatePostsProportionally)
- Enforce MaxTokensPerRecap in the live recap path (previously a silent no-op)
- Drop redundant non-atomic daily-limit pre-check in the scheduled worker; rely on the atomic check in CreateRecapFromSchedule
- Disable non-recurring schedules on all terminal paths (including daily-limit skips) via finalizeSchedule
- Trim scheduled-recap job payload to scheduled_recap_id
- Exclude skipped recaps from GetRecapsForUser

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Recaps webapp: format schedule times in the schedule timezone, static day i18n, typed schedule fields

- Display next-run and schedule times using the scheduled recap's timezone (shared schedule_time_format helper) instead of browser-local time
- Replace dynamic day-of-week i18n message IDs with static descriptors so strings are extractable/translatable
- Add ScheduledRecapTimePeriod/ScheduledRecapChannelMode union types
- Use shared Button in the scheduled recaps empty state

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Recaps e2e: cover scheduled edit/delete/all-unreads/empty-state and token-limit enforcement

- Add UI coverage for editing, deleting, and the empty state of scheduled recaps
- Add all-unreads scheduled recap modal flow
- Add immediate-recap token-limit enforcement tests (single and per-channel), validating MaxTokensPerRecap truncation end-to-end
- Extend recaps page object and helpers

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Recaps: regenerate store layers/mocks and i18n after cleanup; fix lint

- Regenerate timerlayer/retrylayer/RecapStore mock to canonical order
- Drop orphaned app.recap.fetch_posts.app_error i18n key (removed with dead fetch helper)
- Add missing semicolon in UnlimitedNumberSetting props

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Recaps: regenerate default roles permissions and admin sidebar snapshot

- Regenerate Cypress default_roles_permissions fixture to include AI Recaps sysconsole permissions
- Update admin sidebar snapshot for the new Recaps section

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Recaps: stabilize flaky cooldown round-up test

The cooldown round-up subtest placed the prior recap 30s before a 2-minute
cooldown, leaving only ~30s of slack before the rounded remaining time would
flip from 2 to 1 minute. Under heavily loaded CI this could intermittently fail
the exact-minute assertion. Move the prior recap to 1s ago so the remaining time
sits near the top of the 2-minute band (~59s slack) while still exercising
ceiling rounding.

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Recaps: store ScheduledRecap.ChannelIds as jsonb

Postgres is the only supported database, so the prior TEXT+JSON-string
workaround for MySQL compatibility is unnecessary. Store ChannelIds in a
jsonb column and type the model field as model.StringArray, which removes the
bespoke marshal/unmarshal and intermediate scan struct in the store.

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Recaps: gate recap limit settings on the ai_recaps permission

Add access:"ai_recaps" to DefaultLimits and all RecapLimitSettings fields so a
delegated admin with sysconsole_write_ai_recaps can save the limit values,
instead of falling back to requiring manage_system.

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Recaps: drop bespoke Users row lock for limit enforcement

The recap limit savers were the only SELECT ... FOR UPDATE in the sqlstore.
Conform to the prevailing pattern (e.g. channel_bookmark_store.Save): enforce
MaxScheduledRecaps / MaxRecapsPerDay with a transactional count + insert and no
row lock, accepting the same best-effort behavior under concurrency as channels,
team members, and bookmarks.

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Recaps: document SaveOnceByTypeAndData dedup semantics

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Recaps: use SERIALIZABLE isolation for limit-check inserts

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Recaps: mark orphaned recap skipped when job enqueue fails

When CreateJob fails after the recap row is committed, flag the recap
skipped with reason job_creation_failed instead of leaving it pending.
Skipped recaps are excluded from the daily-limit count, so this frees
the quota slot for a recap that will never run, and keeps CreateRecap
consistent with CreateRecapFromSchedule.

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* ci: retrigger CI (flaky enterprise npm cache EEXIST)

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* ci: retrigger CI (flaky Vet API container init)

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Recaps: re-check channel read permission at recap execution time

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* fix scheduled recap job server test setup

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* ci: retry flaky webapp test

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* ci: retry documentation impact review

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* MM-67163: Address review feedback: remove unused userID param, return AppError from GetRecapLimitStatus

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* MM-67163: Bulk channel permission check for recap creation, bounded by channel limit

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* chore: rerun CI

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* chore: rerun CI after network failure

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-07-21 13:10:25 -04:00
Amy Blais 518b6b2c39 Updated minimum supported Edge and Chrome versions (#37589)
Automatic Merge
2026-07-21 16:29:17 +03:00
Nick MisasiandCursor f84dd25754 Prepackage mattermost-plugin-agents v2.5.0-rc1. (#37582)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-21 09:07:16 -04:00
9e51e325f5 Add --active flag to mmctl user list (#37560)
Mirror the existing --inactive filter so admins can list only active
users. Reject using --active and --inactive together, and update unit,
e2e, and docs coverage.

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Sven Hüster <svelle@users.noreply.github.com>
2026-07-20 15:13:14 +02:00
Andre Vasconcelos 28113e5a7a Bumping prepackaged mscalendar version to v1.7.0 (#37575) 2026-07-20 15:47:12 +03:00
2851af059d Add admin-locked profile fields for email users and pre-provisioned names on invites (#37458)
* Add TeamSettings.LockProfileFieldsForEmailUsers with server-side enforcement

Co-authored-by: Nick Misasi <nick13misasi@gmail.com>

* Add API tests for LockProfileFieldsForEmailUsers enforcement

Co-authored-by: Nick Misasi <nick13misasi@gmail.com>

* Hide admin-locked profile fields in user settings and add System Console dropdown

Co-authored-by: Nick Misasi <nick13misasi@gmail.com>

* Support pre-set username and name on team email invites

Co-authored-by: Nick Misasi <nick13misasi@gmail.com>

* Add tests for invite profiles; fix resend worker channel-list parsing

Co-authored-by: Nick Misasi <nick13misasi@gmail.com>

* Add pre-set profile inputs to member invite modal

Co-authored-by: Nick Misasi <nick13misasi@gmail.com>

* Prefill and lock pre-set username on signup page

Co-authored-by: Nick Misasi <nick13misasi@gmail.com>

* Add first/last name editing to System Console user detail and document new setting

Co-authored-by: Nick Misasi <nick13misasi@gmail.com>

* Fix lint issues in invite modal profile inputs

Co-authored-by: Nick Misasi <nick13misasi@gmail.com>

* Fix double outline on invite modal profile inputs inside GenericModal

Co-authored-by: Nick Misasi <nick13misasi@gmail.com>

* Refactor invite emails to InviteEmailData struct and harden invite profile validation

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Centralize profile-lock permission exemption in app layer and add config coverage

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Per-field name locking in profile settings, typed lock setting, and shared invite profile helpers

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Add Playwright E2E coverage for locked profile fields and pre-set invite profiles

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Sync playwright package-lock with merged workspace versions

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Restore upstream playwright package-lock (fix npm ci drift)

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Assert invite input cleared instead of chip text after adding email

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Fix invite modal scroll, username error layout, and clipped autocomplete

Keep the footer pinned while tall profile rows scroll, show username
validation full-width after blur, and portal select menus so they are not
clipped by the scroll container.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fixes for autocomplete items not aligning properly

* Make invite autocomplete menu portal opt-in and fix modal chrome

Confine document.body menu portaling to the invite modal via a menuPortal
prop, and restore click-away, slide-in animation, and header alignment for
the scrolling invite modal layout.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix eslint lines-around-comment on menuPortal props

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix stylelint property order in invitation modal SCSS.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix stylelint property order in invitation_modal.scss

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Adapt invite modal E2E to portaled autocomplete menus

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Simplify locked profile invite implementation

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Fix invite modal review and E2E feedback

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Retry flaky enterprise CI

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Retry Docker image export CI

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Minimize locked profile fields diff

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Fix locked profile E2E documentation

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Address minimized test review feedback

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Fix email test whitespace

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Retry OpenSearch download CI

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Retry flaky Cypress thread navigation

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Preserve legacy invite behavior without profiles

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Clarify invite profile validation

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

* Retry flaky enterprise E2E

Co-authored-by: nick.misasi <nick.misasi@mattermost.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Matthew Birtch <2040554+matthewbirtch@users.noreply.github.com>
2026-07-17 14:29:44 -04:00
Alejandro García Montoro d6d2c50189 Clear session cache on OAuth token refresh (#37459)
newSessionUpdateToken removed the old session row from the DB but
did not clear the in-memory session cache. Because GetSession
returns the cached entry without querying the DB, the rotated
(old) access token remained valid for up to SessionCacheInMinutes
(default 10 min) after rotation.

Add a ClearSessionCacheForUser call after the DB removal, matching
what RevokeAccessToken already does, so the old token is rejected
immediately.

Add a regression test that verifies the old token hits the cache
before refresh and is rejected right after rotation.
2026-07-17 15:03:47 +00:00
Pablo VélezandMattermost Build 3a820143a1 MM 69100 - Team ABAC Membership - members sync and end user surfaces (#37054)
* MM-69063 - Add team ABAC model and constants foundation

* Add team ABAC store EXISTS, channel Type retrofit, policy count split, and index migration

* Add team ABAC app layer: access gate, hydrators, assign/unassign, cleanup, and  GetTeamMembersToRemove store

* Enforce team membership ABAC on join and hide policy governed teams from  non-qualifying users in the directory

* Add team_ids to access policy assign/unassign, expose per-team policy GET,  and support abac_match_only for not_in_team user listing

* Add team ABAC client methods, websocket handler, per-team System Console policy UI, and hide policy-governed teams from non-qualifying users

* Make team ABAC mode-aware: advisory on public teams, strict on private, and surface governed private teams to qualifying users in directory listings

* Flag-gate team ABAC mutation/read APIs and fix policy-save error handling, member-removal limit, team-id  validation, export, and audit cleanup

* coderabbit feedback; Broadcast team policy enforcement updates on policy create/update and activation, not only on delete

* Update team access control policy schema to allow nullable policies and enhance test cases with channel counts

* Enhance access control policy tests to include team policy search alongside channel policy search

* Add team membership access control feature flag to docker-compose generation

* Implement team access control policy checks and refactor related components

* MM-69100 - add team membership ABAC sync worker with mode-aware removal and auto-add

* Add team ABAC removal/auto-add notifications, cascade audit, and team custom-rules save backend

* Add Team Settings team membership tab with custom-rules editor, auto-add toggle, save confirmation, and mode-flip  sync trigger

* Add team access control policy panels, job details team list, and e2e coverage for membership tab and discoverability cards

* ix save double-submit with loading state, team privacy via updateTeamPrivacy, mode-flip count accuracy, sync trigger logic, i18n keys, and e2e test cleanup

* Add team membership policy notices to invite and add-to-team flows, team access control system messages, and a team policy attributes endpoint

* Add team membership recommended tag for qualifying users and team requirements notice in the members modal

* temp

* adjust styling, add new e2e, fix team admin job permissions issues

* e2e clean items created

* fix directory and team integration e2e tests

* Surface team sync results in the Sync Job Details modal via a Teams tab linked from the chained channel job

* code clean up, adjust styling

* Add access control attributes endpoint and enhance E2E tests for team membership

* Enhance team access control by updating permission checks and syncing jobs

* Add API-level enforcement gate tests for ABAC team membership

* Fix formatting in team channel settings component by adding missing semicolon

* Fix formatting in Client4 class by adjusting type annotations for clarity

* revert unwanted changes in package-lock

* Refactor job permission test and simplify error handling in team details component

* Update test assertions and enhance auto-add functionality in team membership tab

* Update onboarding tests to use 'Public Team' card for team access settings

* Refactor AccessSettings and TeamPolicyEditor components to replace allowOpenInviteCheckbox with public and private team buttons, and update access control job dispatching logic.

* Remove redundant state updates for channel assignments in access control policy reducer

* Implement email suppression for team membership notifications and add related tests

* Authorize ABAC team self-join by attribute match instead of the join_private_teams role

* comments clean up

* Gate team privacy type normalization behind active ABAC, keep legacy allow_open_invite-only path  otherwise

* adjust job details styles

* Add team membership policy disconnect confirmation, fix linked-policy affected-member count, and polish job details modal

* Combine custom rules with system policy expressions for accurate confirm counts

* Implement child resource count stamping and deletion gating for access control policies

* Enhance team sync job triggering logic for membership changes without auto-add

* Refactor team privacy handling to align with open-directory model; update comments for clarity on allow_open_invite logic.

* Deduplicate team ABAC sync jobs per policy to prevent concurrent runs emitting duplicate DMs and audit records on HA clusters

* fix linter warnings

* Add confirmation for policy removal in TeamDetails tests

* Update delete policy message to include Teams in the warning

* Add linked teams warning to policy deletion and update translations

* Add Auto-add feature for policy selection and team management

- Enhanced PolicySelectionModal to include an Auto-add checkbox for each policy.
- Updated PolicyList to manage Auto-add state and reflect changes in the UI.
- Modified TeamAccessControl to handle Auto-add functionality and persist changes.
- Added tests to ensure Auto-add behavior works as expected in various scenarios.
- Updated translations for Auto-add related strings.

* Add team membership sync footer and enhance job fetching with policy ID

* Refactor team membership policy handling and sync footer implementation

* Add team sync channel cascade test for private channel membership

---------

Co-authored-by: Mattermost Build <build@mattermost.com>
2026-07-17 09:55:36 +03:00
Ben Cooke 3b64a2ac84 Property owners for property fields and values, new audit logs for cpa value changes and new pluginapis (#37299) 2026-07-16 18:12:27 -04:00
catalintomai 5f7f967a7d MM-69268 - Add ChannelTypeSpace backing-channel type for Docs (#37321) 2026-07-16 22:34:57 +02:00
Maria A NunezandCursor 57fe965ac7 Session Attributes: System Console management page and permission-policy picker integration (#37362)
* Add Session Attributes system console page shell

Register the gated System Console "Session Attributes" subsection backed by a
component page that loads the session_attributes property fields, plus a feature
discovery upsell. Removes the former config toggles from the UI. Includes display
helpers and unit tests.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Add Session Attributes listing table

Render the read-only session attributes table with Display Name, Name (with
server-sourced label), derived Type, platform availability icons, TTL, grace
period, and status columns, plus loading and empty states. Includes status
chip, platform icon, and table unit tests.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Add Session Attributes row actions and staged save

Add a per-row actions menu to tune TTL and grace period via presets and to
disable/enable each attribute, with a destructive disable confirmation modal.
Edits stage locally and persist on save via a per-field property patch thunk,
with an unsaved-changes guard. Includes the patch thunk and component tests.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Surface session attributes in permission policy editors only

Include session attributes in the permission policy attribute picker (both advanced and table editor modes) and group them under a dedicated section, while excluding them from membership and parent access-rule editors via a shared helper. Surface the server rejection message when a membership rule references session attributes.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix lint formatting in session attributes table test

Split single-line renderWithContext JSX in session_attributes_table.test.tsx
onto multiple lines to satisfy react/jsx-max-props-per-line, matching the
existing test formatting convention. Surfaced by the pre-commit ESLint gate.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Add Session Attributes E2E specs

Add Playwright end-to-end coverage for the Session Attributes console page: listing render, staged TTL/grace/enable edits with save persistence, disable confirmation modal, dirty navigation guard with cancel revert, and permission-versus-membership picker polarity. Includes the page object and sidebar wiring.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix playwright-lib build to resolve @/* tsconfig path alias

The lib rollup build (@rollup/plugin-typescript) compiled the @/* tsconfig path alias but never rewrote it, so with preserveModules the emitted dist files still imported the unresolved @/ specifier. Node could not load these, failing Playwright config loading for the entire suite (Cannot find module @/ui/components).

Add a minimal inline rollup resolveId plugin that maps @/* to the real src file so emitted dist uses correct relative imports. No new dependency required.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix session attribute group identity to use real group id

The webapp conflated the property group NAME (session_attributes) with the
group's real DB id. The server keys every field under a 26-char group UUID,
so the listing page selected fields under the name and always rendered
"No session attributes found." on a real server, and the ABAC editors
compared field.group_id against the name, which never matched live data.

Resolve the group properly instead:

- fetchPropertyFields now also dispatches RECEIVED_PROPERTY_GROUP (mirroring
  the managed-channel-categories flow) so the group name -> id mapping is
  available, and the listing page selects fields by the resolved group id
  via getPropertyGroupByName.
- Add isSessionAttributeField, keyed on the session object_type, and use it
  in the ABAC consumers (attribute picker, excludeSessionAttributes,
  permission policy picker, simulate-access modal). object_type is intrinsic
  to the autocomplete fields and avoids the name/UUID conflation entirely.
- Relax UserPropertyField.group_id to string since the server returns a UUID;
  the group name is only ever a client-side placeholder for an unsaved field.

Harden the unit tests to use realistic group UUIDs distinct from the group
name and correct object_type values, so fixtures can no longer pass with a
group_id the server never produces.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix SaveChangesPanel footer Cancel to revert in place

The admin SaveChangesPanel rendered its onCancel handler through a
BlockableButton, which intercepts clicks while navigation is blocked and
defers the action via deferNavigation instead of invoking it. On the
Session Attributes page this meant footer Cancel never reverted staged
edits directly: it re-opened the Discard Changes guard and only reverted
if a deferred navigation was later confirmed.

BlockableButton is only appropriate for the cancelLink (navigation) path.
For an in-place revert handler, wire onCancel to a plain button so it
calls the hook cancel() directly, reverting staged edits, clearing the
dirty state, releasing setNavigationBlocked, and never showing the
discard modal.

Repair the page-level jest test to exercise the real footer Cancel and
assert direct revert plus nav-guard release without confirming a deferred
navigation.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Merge enabled session attributes into permission policy editors

Inject enabled session attributes into the two permission-policy attribute
pickers (CEL and table editors) while keeping membership/parent-policy
surfaces restricted to long-lived user attributes via excludeSessionAttributes.
Adds a useEnabledSessionAttributeFields hook plus the shared
mergeSessionAttributes/celPrefixForField helpers so the editors can build CEL
across the user.attributes.* and user.session.* namespaces.

Addresses two review nits:
- Resolve the picked attribute by its unique field id (not bare name) so a
  session attribute sharing a name with a CPA attribute still maps to
  object_type 'session' and emits user.session.<name>; the picker also uses
  id-based DOM ids to avoid collisions.
- Make celPrefixForField the single source of CEL-prefix truth by consuming
  it in rowToCEL instead of inlining the namespace check.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix Session Attributes E2E spec: id-based picker selectors and per-test state isolation

The attribute picker menu items are keyed by field id (#attribute-<id>) rather
than name, so the permission/membership picker assertions now resolve session
attribute ids from the property-fields API and the Department user-attribute id
from the CPA fields API instead of targeting stale name-based ids.

Add an afterEach that restores every seeded session attribute to a baseline
captured at suite start, replacing the per-test finally restores. This keeps the
suite order-independent so the listing test "Disabled by default" assertions no
longer depend on whether another test has enabled a field first.

Co-authored-by: Cursor <cursoragent@cursor.com>

* UX Polish

* Linting

* Removed styled components

* Fix session attributes unit tests

Co-authored-by: maria.nunez <maria.nunez@mattermost.com>

* Fix invalid Sass '&&&' selector breaking production build

Co-authored-by: maria.nunez <maria.nunez@mattermost.com>

* Stabilize session attributes duration-preset e2e interaction

Co-authored-by: maria.nunez <maria.nunez@mattermost.com>

* Fix broken session attributes doc link in feature discovery

Co-authored-by: maria.nunez <maria.nunez@mattermost.com>

* Fix session attributes test type error from non-union group id

Co-authored-by: maria.nunez <maria.nunez@mattermost.com>

* Revert unnecessary playwright-lib rollup @/* alias resolver

@rollup/plugin-typescript already resolves the @/* tsconfig path alias;
building with and without the added resolveSrcAlias plugin produces
byte-identical dist JS, so the change was a no-op.

Co-authored-by: maria.nunez <maria.nunez@mattermost.com>

* Allow session-attribute row defaults and resolve table-editor lookups by namespace

findFirstAvailableAttributeFromList now treats session attributes as
selectable (mirroring AttributeSelectorMenu), so a session-only attribute
set no longer fails addRow. The operator menu reads row.attribute_type and
the value options match on name + object_type, so a user/session name
collision resolves the correct namespace.

Co-authored-by: maria.nunez <maria.nunez@mattermost.com>

* Type session-attribute platforms on the shared property attrs

Add platforms?: string[] to UserPropertyField.attrs so the attribute
picker reads it without an unsafe cast, and narrow the platform filter in
getSessionAttrs with a type guard.

Co-authored-by: maria.nunez <maria.nunez@mattermost.com>

* Session Attributes page: surface load errors, propagate read-only, and improve a11y

Handle a failed property-fields fetch with an explicit error state instead
of leaving the rejection uncaught and showing the empty state. Thread the
page's disabled flag through to the row dot menus so read-only admins cannot
stage edits. Convey each platform icon's active/inactive state in its
accessible name, and document the table border override.

Co-authored-by: maria.nunez <maria.nunez@mattermost.com>

* SaveChangesPanel: use shared Button for the in-place cancel control

Co-authored-by: maria.nunez <maria.nunez@mattermost.com>

* Cover fetchPropertyFields group name to uuid mapping

Co-authored-by: maria.nunez <maria.nunez@mattermost.com>

* E2E: scope session-attribute submenu trigger to its row and document single-field save

Co-authored-by: maria.nunez <maria.nunez@mattermost.com>

* Comment linting

* scss cleanup

* linting

* PR feedback

* PR Feedback. Removed IP + version icons

* PR feedback

* PR feedback

* Fix session attributes table test after IP/Version display types were removed

Co-authored-by: maria.nunez <maria.nunez@mattermost.com>

* Session Attributes admin console UX fixes

- Reduce table row height and shrink-wrap the panel to the table
- Reorder LHS sidebar so Session Attributes follows User Attributes
- Cap attribute selector popover at 400px max-height with scroll

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix native attribute selection by assigning synthetic field IDs

The ABAC editors resolve the selected attribute by field ID, but the
synthetic native user attribute fields were created without an ID. Every
built-in attribute therefore resolved to the first empty-ID field (Email),
so no other built-in could be selected. Assign each native field a stable,
unique, non-empty ID so id-based resolution works for all built-ins.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-16 08:05:03 -04:00
Ben SchumacherandClaude Sonnet 5 86888cbac6 [MM-69007] Speed up Support Packet tests by shortening the CPU profile sample (#36761)
* [MM-69007] Add CPU profile duration option to support packet API

Support packet generation always blocks on a 5s CPU profile sample.
Add cpu_profile_duration_seconds to GET /api/v4/system/support_packet
so callers can shorten or skip it; nil keeps the 5s default,
0 skips CPU profile generation, positive values override the duration.
Validated as an integer in [0, 300].

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Add test for zero CPU profile duration skipping cpu.prof

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Add API-level validation tests for cpu_profile_duration_seconds

Cover negative, above-max, non-integer, and clearly out-of-range values.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Document cpu_profile_duration_seconds query parameter

Adds the new optional query parameter to the support_packet endpoint
in the API reference spec so the generated docs match the handler
behavior added in this PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* MM-69007: Drop HTTP API surface, keep test-only CPU profile speedup

Rescope this PR to not expose CPU profile duration control via the
GET /api/v4/system/support_packet endpoint:
- Remove the cpu_profile_duration_seconds query parameter, its
  validation, and audit metadata from api4/system.go
- Remove the corresponding api4 validation test
- Remove the OpenAPI documentation for the query parameter

model.SupportPacketOptions.CPUProfileDurationSeconds and the
platform.GenerateSupportPacket/getCPUProfile plumbing remain, since
they're what let the platform/app support-packet tests pass a short
duration (1s, or 100ms for TestGetCPUProfile) instead of waiting on
the real 5s default. Clarify the field's doc comment to reflect that
it's for programmatic/test callers only, not the HTTP API.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* MM-69007: Cut CPU profile test duration from 1s to 100ms

Change model.SupportPacketOptions.CPUProfileDuration from *int
(whole seconds, so the minimum non-zero value was 1s) to
*time.Duration, so test callers can pass a sub-second duration.
Update all GenerateSupportPacket test call sites to use a shared
100ms duration instead of 1s, and extract it into a package-level
shortCPUProfileDuration var in each test file to avoid repeating the
literal.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* MM-69007: Remove unused SupportPacketOptions JSON plumbing

Neither SupportPacketOptionsFromReader nor the json tags on
IncludeLogs/PluginPackets are used anywhere in this repo -
api4/system.go builds SupportPacketOptions directly from request
form values, never from JSON. Drop the unused JSON tags, decode
helper, and now-unused encoding/json and io imports.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-07-16 15:48:57 +05:30
Julien Tant 1388ba477c Update Playbooks plugin to v2.11.0 (incl. FIPS) (#37528) 2026-07-15 23:10:25 -07:00
Ibrahim Serdar Acikgoz 8d10e91d38 MM-69798: Add AccessControlSettings setting to disable channel access indicators (#37519) 2026-07-16 07:14:23 +02:00
Bill Gardner b9179642f3 update Calls to v1.12.2 (#37502) 2026-07-15 20:12:33 -03:00
Doug Lauder 7778bb79d1 MM-69792: Recover shared channel sync after a brief remote outage (#37499)
* MM-69792: Recover shared channel sync after a brief remote outage

When a remote went offline for less than the 5-minute IsOnline()
threshold, sync recovery never ran. IsOnline() never flipped, so no
connection-state-change event fired and ForceSyncForRemote was never
triggered. Posts created during the outage stayed unsynced until the
next organic change in the channel.

The root cause was that send failures are reported asynchronously
through a callback that sendSyncMsgToRemote discarded. On a failed
send syncForRemote still returned nil, so processTask saw success:
the retry loop and the failure-notification hook were effectively
dead code for delivery failures.

Changes:

- sendSyncMsgToRemote now propagates the delivery error reported via
  the callback instead of swallowing it. A failed send is retried
  rather than silently dropped. This applies to every sync type
  (posts, users, memberships, reactions, statuses); the success path
  is unchanged.

- remotecluster tracks a per-remote "sync failed since last ping"
  flag (NotifySyncFailed). On the next successful ping, PingNow fires
  a connection-state-change event even when IsOnline() never flipped,
  driving a single ForceSyncForRemote on recovery.

- Sync retries are now spaced by SyncRetryDelay (15s) instead of
  retrying immediately, giving a short outage time to recover and
  guaranteeing a failed ping before retries exhaust.

Also adds a post/offline-recovery case to the sharedchannel-test
integration tool (stop and restart Server B mid-run) and disables
metrics on the two test servers so they do not collide on the
metrics port when the loaded config has metrics enabled.

* fix linter error

* Fix Rabbit nit

* fix CI

* Delete pr37499-fix-findings-1-and-4.md

* address review comments

* MM-69792: Keep a per-remote retry task when a shared channel sync fans out

A sync task with no specific remote (remoteID == "") fans out to every
remote sharing the channel. On failure each remote's retry was a copy of
the original task that still carried the original, remote-less task id.
Because addTask merges on task id, all of those per-remote retries
collapsed into a single entry, so only one failed remote was ever
retried and the rest were silently dropped. Delivery failures were
partly masked by the ping-driven ForceSyncForRemote recovery, but
non-delivery errors for the other remotes had no path back.

Give each per-remote retry a remote-specific id (task.id + remoteId) when
the originating task had no remote, so every failed remote keeps its own
retry task. Single-remote tasks already have a remote-specific id and are
left untouched, since recomputing would grow the id on each retry.

Also harden the tests: assert the retry is scheduled the full
SyncRetryDelay out (no tolerance), assert the result callback is not
invoked on delivery failures or unconfirmed responses (so the cursor
stays un-advanced), and add a regression test covering fan-out to
multiple failing remotes.

* fix one more pre-existing edge case.

* fix linter
2026-07-15 09:47:55 -04:00
Jesse Hallam 901b4256bd Fix assorted down migrations, add additional testing (#37464) 2026-07-15 07:40:12 -03:00
Harrison Healey d3ebd0ff65 MM-69751 Replace concurrent React user setting with feature flag and add to root.html (#37422)
* MM-69751 Replace concurrent React user setting with feature flag and add to root.html

* Address Coderabbit feedback
2026-07-14 14:25:15 -04:00
Jesse Hallam b959e88bf6 [MM-69721] Fix empty text/plain part in transactional emails (#37383) 2026-07-14 18:09:03 +02:00