Removes the `service.enablebotusers` config flag, the matching
`bot_users_enabled` field on /info, and the now-unused
`ErrBotUsersDisabled` error. Bot user routes and the frontend
settings tab are now always available.
https://claude.ai/code/session_01VhAR6xnoCdG1fpX52bzaCC
User search previously filtered bots only when they happened to match the
search string. That produced two bad behaviours:
1. Bots owned by other users could surface on an exact-username match,
leaking them into assignee pickers and similar UI.
2. A user could not reliably find their own bots by typing a partial
name, so bots became awkward to assign to tasks.
Change ListUsers to treat bot ownership explicitly: the existing match
branch excludes rows owned by someone else, and a second branch always
returns bots owned by the calling user. The own-bots branch also
respects any AdditionalCond passed in so project-scoped listings don't
start leaking bots from outside the project.
Bot users now render with a cool-toned (blue/cyan/violet/teal/indigo)
marble variant so they're visually distinguishable from human users.
Marble's rendering logic is parameterized with a palette; the route
forces the bot palette whenever the resolved user is a bot, overriding
whatever avatar provider they'd otherwise inherit.
The JWT skipper bypassed validation entirely for /token/test when the
bearer was an API token, leaving "user" unset in the context. CheckToken
then type-asserted it to *jwt.Token and panicked.
Validate the API token in the skipper but skip the route permission
check (since /token/test is not exposed in the API token route registry,
no token can hold explicit permission for it). Drop the now-redundant
JWT assertion in CheckToken — auth has already passed by the time the
handler runs.
The Modal mounts the <dialog> via v-if and calls showModal() in a
follow-up flush, so v-focus runs while the dialog is still closed and
its focus() call is dropped. The existing rAF retry was gated on
quick-add mode, leaving Ctrl+K in the regular app with no focused
input. Run the retry whenever the quick actions become active and keep
the command pre-selection scoped to quick-add mode.
Stylelint 17.9.0 resolves customSyntax modules relative to the
stylelint package, so the transitive postcss-html pulled in via
stylelint-config-recommended-vue is no longer reachable and lint
fails with "Could not find postcss-html".
During E2E testing with Playwright (via `mage test:e2e`), the frontend is built using Vite with `--mode development`. However, Vite hardcodes `process.env.NODE_ENV` to `production` during the build step, which causes `import.meta.env.DEV` to statically evaluate to `false`.
Because the `v-cy` custom testing directive relied on the `DEV` flag, it silently evaluated to false and failed to render the `data-cy` attributes into the DOM during the test build. This caused test failures because Playwright could not locate the elements.
Changing the check to explicitly evaluate `import.meta.env.MODE === 'development'` successfully bypasses the Vite build behavior, ensuring that `data-cy` testing attributes are consistently rendered during E2E tests.
For more context on Vite's build behavior regarding `DEV` and development mode, see:
https://github.com/vitejs/vite/discussions/14083
Mouse event emulation from taps on touch devices caused the glance
tooltip to appear unexpectedly with no reliable way to dismiss it.
Gate the tooltip behind a `(hover: hover) and (pointer: fine)` media
query so it only activates on devices with a real pointer.
Point agents to the skills up front so they are loaded before code is
written in the relevant areas. Patterns like permission placement and
migration error handling have been documented for 5+ months but still
recur in review; a path-aware skill prompt is a stronger trigger than
guidance buried further down the file.
Checklist skill invoked before editing files under pkg/migration/. Covers
cross-DB type safety across MySQL/PostgreSQL/SQLite, DDL error handling
(no silent discards), time-column conventions, path sanitization for
user-supplied input, and model/frontend sync requirements.
Checklist skill invoked before editing models in pkg/models/. Covers Can*
method placement (on the model, never in route handlers), the four
permission methods, required positive+negative test coverage, and the
anti-patterns most frequently flagged in review.
The api translation scanner only looked at literal arguments to i18n.T /
i18n.TP, so keys passed via a variable (e.g. the time.since_* keys stored
in a struct slice in pkg/utils/humanize_duration.go and looked up via
chunk.key) were invisible and had to be hard-coded in an allowlist of
dynamic prefixes.
Mirror the frontend scanner: collect every dotted string literal in the
Go source as a "usage hint" and treat any literal that matches a known
translation key as used. This automatically picks up the time.since_*
case and removes the need for the apiDynamicKeyPrefixes allowlist.
Previously the PR introduced a separate `check:frontendTranslations` mage
task and a second CI job. Merge both into the existing `check:translations`
task and a single CI job. Also rename internal references from "backend" to
"api" to match the project convention (Vikunja's Go server is the api, not
the backend).
Add a frontend-check-translations job that runs the new
check:frontendTranslations mage task. Like the existing
api-check-translations job, failures hard-fail CI. This makes
reviewers catch dead keys and missing $t() wiring up front instead of
having to flag them manually in pull request review.
Remove 47 keys from frontend/src/i18n/lang/en.json that are not
referenced by any $t / t / i18n.t / i18n.global.t / tc / <i18n-t>
call site, nor by any stored-as-literal dynamic lookup pattern.
The keys fall into a few broad groups: leftover attribute labels on
filter and label models, dropped editor toolbar entries, unused
password/password-confirmation copy, and a handful of stale admin and
migration strings. The sibling translation files will be reconciled on
the next Crowdin sync.
Remove five keys from pkg/i18n/lang/en.json that are no longer
referenced by any i18n.T / i18n.TP call. These surfaced once the
translation check started reporting dead keys. The sibling translation
files will be reconciled on the next Crowdin sync.
Removed keys:
- notifications.task.comment.mentioned_message
- notifications.task.mentioned.message
- notifications.common.actions.assigned_you
- notifications.common.actions.assigned_themselves
- notifications.common.actions.assigned_user