24078 Commits

Author SHA1 Message Date
Álvaro Tejero Cantero
04c60331b6
📎 Assert materialization against the ordinary storage path 2026-10-06 10:36:54 +02:00
Álvaro Tejero Cantero
bdcb979211
✨ Let any branch become an ordinary file
::materialize-file-branch persists a branch's derived state as the
file's own data payload, drops the op log, releases the pinned base
snapshot, closes any pull request over it and clears the branch marker,
so every read and write path takes the ordinary route from then on.
That is the preview's exit door: the flag can go off and the engine can
be replaced without stranding anybody with an unreadable file.

It keys on the branch file rather than the branch metadata, so calling
it twice is a reported no-op rather than an error.
2026-10-06 10:36:54 +02:00
Álvaro Tejero Cantero
b6232edfd5
⚡ Keep the summary cache for a working day, not ten minutes
The fetch the cache exists for is a file open, and expireAfterAccess of
ten minutes threw the entry away between two sessions on the same file:
a listing measured at 5 ms was 26 s again fifteen minutes later. An
entry is a uuid and five numbers, so the window costs nothing.
2026-10-06 10:36:54 +02:00
Álvaro Tejero Cantero
48f2f4c55f
🐛 Restore the app.config require the cache test needs 2026-10-06 10:36:54 +02:00
Álvaro Tejero Cantero
cf50457e89
🐛 Restore the uuid require the cache test needs 2026-10-06 10:36:54 +02:00
Álvaro Tejero Cantero
93cec8be7c
📎 Assert a repeat listing performs no comparison work 2026-10-06 10:36:54 +02:00
Álvaro Tejero Cantero
f39905455e
📚 Describe the listing summary cache 2026-10-06 10:36:54 +02:00
Álvaro Tejero Cantero
a844752c47
✨ Cache the branch diff summary by revision tuple
The listing's per-branch [ahead behind conflicts] summary is cached in
memory keyed by (base-snapshot-id, source-revn, branch-revn), which
names exactly the inputs the computation reads. A stale key cannot
exist, so the cache needs no invalidation logic: a repeat listing with
no intervening save performs no comparison work, and a cache miss is
the old behaviour. Failed computations are not stored, so a broken
branch keeps degrading to zeros with a warning.
2026-10-06 10:36:54 +02:00
Álvaro Tejero Cantero
dbab1d5620
📚 Reword remap-refs for the derived-data model 2026-10-06 10:36:54 +02:00
Álvaro Tejero Cantero
9b3436e7a2
📚 Describe the op log storage model
The technical guide documented the full-copy branch model this change
replaces. Update the conceptual model, data model, creation steps,
ahead/behind computation, update-from-main (squash), and the limitations
section to describe the derived-state model.
2026-10-06 10:36:54 +02:00
Álvaro Tejero Cantero
b043fcc76a
✨ Store branch state as an op log, not a file copy
Swap the storage and materialization layer of file branching to remove
the per-branch data duplication, keeping the RPC contracts and the
frontend unchanged.

- file_branch_change (0154): one row per branch save, the blob-encoded
  change vector, ordered by revn; branch files store no data payload.
- Read path: app.binfile.common/get-file* derives the branch file data
  by replaying the op log over the pinned merge-base snapshot, carrying
  the base's version and migration set so need-migration? stays quiet.
- Save path: update-file routes branch files to persist-branch-file!,
  which appends the vector to the op log and updates the file row
  without any file_data write.
- Creation: create-file-branch no longer duplicates main's data; it
  inserts an empty branch file row, grants the creator, and copies the
  library rels.
- update-branch-from-main squashes the op log after integrating main's
  changes: the repositioned base already carries main's side, so the
  log is replaced with the net branch-only changes.
- Kept (keep-branch) branches retain the base pin: their state is
  derived, so releasing the pin would leave them unreadable after GC.

Tests: backend-tests.rpc-file-branch-oplog-test asserts the storage and
derivation invariants; the existing branch, file, binfile, snapshot,
thumbnail, and pull request suites pass unchanged against the swapped
engine (84 tests, 1487 assertions, 0 failures).
2026-10-06 10:36:54 +02:00
Álvaro Tejero Cantero
bc22388776
🐛 Decode snapshot metadata before resolving storage-backed data 2026-10-06 10:36:54 +02:00
Álvaro Tejero Cantero
e8957a24e4
🐛 Register the 0157 file-branch-change migration 2026-10-06 10:36:54 +02:00
Álvaro Tejero Cantero
2a5a235063
✨ Add the design preview of file branching and pull requests
This commit carries the design-branching preview by Juan de la Cruz:
elhombretecla/penpot:branching at 0ac265a3f5, which holds 45 commits
and 3 develop merges and was last synced with develop on 2026-09-16.
The branch is squashed together with a merge of origin/develop at
7c039231f7, so the tree is the merged result.

The merge resolved conflicts in six files:

- backend/src/app/migrations.clj: develop's 0155 and 0156 steps run
  first, then the branching steps; the names differ.
- backend/src/app/rpc/commands/viewer.clj: both requires stay.
- common/src/app/common/flags.cljc: both flags stay.
- frontend/src/app/main/ui/dashboard/file_menu.cljs: "Create branch"
  becomes a menu-item* with the same id, guard and handler.
- frontend/src/app/main/ui/dashboard/grid.cljs: the portal goes with
  develop's menu rewrite; the branches popover stays in the trigger.
- frontend/translations/en.po: both sides' entries stay, with
  develop's source references.

Co-authored-by: Juan de la Cruz <delacruzgarciajuan@gmail.com>
AI-assisted-by: mixed models
2026-10-06 10:36:54 +02:00
Belén Albeza
8d8dff8de4
🐛 Fix dropping caret styles when pasting html text (#12108) 2026-10-06 10:29:53 +02:00
Andrey Antukh
d3ca9a0b83
✨ Add unified jobs substrate replacing legacy one-shot task system (#11542)
* 🎉 Move backend jobs to the unified job table substrate

Replace the generic 'task' table with a unified 'job' table as the
durable substrate for all backend jobs, and migrate every producer
and consumer to it. The legacy 'task' table stops receiving writes
but stays dormant (still cleaned by tasks-gc); upload-session
chunking and the other post-branch upstream changes are preserved.

New 0154 migration creates the 'job' table: dispatch and lifecycle
columns (name, queue, priority, scheduled_at, retries, status,
timestamps, props) plus optional user-facing ledger columns
(profile_id, target, progress, error, result, resource_id,
expires_at). No modified_at trigger: the application updates it on
every write for lease and orphan detection. Partial indexes cover
the dispatcher claim query, orphan scanning, and per-profile
listing.

New app.jobs public API: submit!/invoke!, heartbeats and progress
reporting, a precompiled job-def registry (name, schema, handler,
decoder, validator), an ephemeral request/response mode, and a
management API for external workers. The dispatcher claims
new/retry rows (FOR UPDATE SKIP LOCKED) with a plain JSON redis
payload and marks lease-expired running rows as orphaned; the
runner executes per-queue with the same retry conventions.

New jobs GC (daily): deletes expired rows and retains internal
terminal rows per :jobs-retention, touching referenced storage
objects so storage-gc-touched reclaims them. Job resources live in
a dedicated job-resource bucket with a reclaim branch in
storage-gc-touched.

All production call-sites (sendmail, delete-object, demo-purge,
file-gc, offload, webhooks, quotes, nitrate bulk delete,
snapshots, telemetry) and the ten cron tasks now submit through
app.jobs; cron is a pure scheduler and the legacy worker registry,
task-table inserts, and per-namespace handlers are removed.

Includes migrations, dispatcher, runner, jobs, request/response,
jobs GC, management API, and cron test suites.

AI-assisted-by: glm-5.3-flash
AI-assisted-by: muse-spark-1.3-contributor
AI-assisted-by: kimi-k3
AI-assisted-by: mimo-v2.5

* ✨ Address review comments on jobs substrate

Resolves the four open review threads on the jobs PR. Orphaned jobs
are now marked aborted, a system-side terminal state with no retry:
the dispatcher sweep stores the end event with outcome aborted in a
single bulk insert in the same transaction, records the terminal
outcome next to the orphaned counter, and reports an error log;
orphans keep alerting through the log and the counter, with no
user notification.

The aborted state is terminal everywhere: the status CHECK, the
sweep, the lifecycle writers, the jobs GC retention, the backlog
gauges and the user status mapping, with tests covering each
transition. The claim comment, the 2.20 doc version and the legacy
task table notes are corrected, and objects-gc heartbeats per chunk.

AI-assisted-by: muse-spark-1.3-contributor
2026-10-06 07:33:12 +02:00
David Barragán Merino
0eb1e8ba95 🐛 Request pull-requests permission in auto-label token
Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-10-05 14:32:04 +02:00
David Barragán Merino
b2ea46a71a 🔧 Use client-id instead of app-id in auto-label workflow
actions/create-github-app-token v3 deprecates the app-id input in
favour of client-id. Switch to the new TRIAGE_APP_CLIENT_ID secret.

Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-10-05 13:11:08 +02:00
dependabot[bot]
66c5e6fdfd ⬆️ Bump the github-actions group across 1 directory with 8 updates
Bumps the github-actions group with 8 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [actions/create-github-app-token](https://github.com/actions/create-github-app-token) | `1.12.0` | `3.2.0` |
| [actions/github-script](https://github.com/actions/github-script) | `7.1.0` | `9.0.0` |
| [actions/checkout](https://github.com/actions/checkout) | `6.1.0` | `7.0.1` |
| [pnpm/setup](https://github.com/pnpm/setup) | `2.0.2` | `3.0.0` |
| [aws-actions/configure-aws-credentials](https://github.com/aws-actions/configure-aws-credentials) | `4.3.1` | `6.3.0` |
| [actions/cache/restore](https://github.com/actions/cache) | `5.1.0` | `6.1.0` |
| [actions/cache/save](https://github.com/actions/cache) | `5.1.0` | `6.1.0` |
| [actions/download-artifact](https://github.com/actions/download-artifact) | `7.0.0` | `8.0.1` |



Updates `actions/create-github-app-token` from 1.12.0 to 3.2.0
- [Release notes](https://github.com/actions/create-github-app-token/releases)
- [Changelog](https://github.com/actions/create-github-app-token/blob/main/CHANGELOG.md)
- [Commits](d72941d797...bcd2ba4921)

Updates `actions/github-script` from 7.1.0 to 9.0.0
- [Release notes](https://github.com/actions/github-script/releases)
- [Commits](f28e40c7f3...3a2844b7e9)

Updates `actions/checkout` from 6.1.0 to 7.0.1
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](d23441a48e...3d3c42e5aa)

Updates `pnpm/setup` from 2.0.2 to 3.0.0
- [Release notes](https://github.com/pnpm/setup/releases)
- [Commits](84cb39b217...fbda4c85fc)

Updates `aws-actions/configure-aws-credentials` from 4.3.1 to 6.3.0
- [Release notes](https://github.com/aws-actions/configure-aws-credentials/releases)
- [Changelog](https://github.com/aws-actions/configure-aws-credentials/blob/main/CHANGELOG.md)
- [Commits](7474bc4690...e1253824e5)

Updates `actions/cache/restore` from 5.1.0 to 6.1.0
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](caa2961268...55cc834586)

Updates `actions/cache/save` from 5.1.0 to 6.1.0
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](caa2961268...55cc834586)

Updates `actions/download-artifact` from 7.0.0 to 8.0.1
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](37930b1c2a...3e5f45b2cf)

---
updated-dependencies:
- dependency-name: actions/create-github-app-token
  dependency-version: 3.2.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/github-script
  dependency-version: 9.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/checkout
  dependency-version: 7.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: pnpm/setup
  dependency-version: 3.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: aws-actions/configure-aws-credentials
  dependency-version: 6.3.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/cache/restore
  dependency-version: 6.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/cache/save
  dependency-version: 6.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
- dependency-name: actions/download-artifact
  dependency-version: 8.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-10-05 12:35:56 +02:00
Andrey Antukh
341e5d1027 Merge remote-tracking branch 'origin/staging' into develop 2026-10-05 11:59:05 +02:00
Andrey Antukh
b6a108c456 📚 Restrict new ! suffix to app.db and transients
Default is now no `!` for new functions. Existing `!`
stays as legacy until its gradual refactor.

Allowed cases live in clojure/idioms: mutating public
app.db API and functions working over transients. Any
further case must be registered there first.

AI-assisted-by: muse-spark-1.3-contributor
2026-10-05 09:54:46 +00:00
Elena Torró
aed4e12c63
⚡ Avoid decoding all pages when building file thumbnail data (#12054) 2026-10-05 11:34:10 +02:00
Eva Marco
b06a43404b
🎉 Add detach to typography and shadow tokens (#11969)
* 🎉 Add detach to typography tokens

* 🎉 Add detach to shadow tokens

* 🎉 Add tests
2026-10-05 11:32:29 +02:00
Dr. Dominik Jain
4c507e56e3
📚 Update MCP API type information (#12078) 2026-10-05 11:22:32 +02:00
Eva Marco
c04a3d346d
🔧 Register the missing test namespaces in the common JS runner (#12074)
Kaocha finds every test namespace on the JVM, but the JS runner only
runs the ones listed in runner.cljc. Sixteen namespaces were missing
from both lists, and files.shapes-builder-test was required but not
listed, so 125 tests never ran in JS. Add them to both lists, sorted.

The only one that failed in JS was make-tokens-status-from-tokens-lib:
(.sets base-lib) is a method call in ClojureScript. On the JVM it read
the field, but (:sets ...) on the result gave nil, so the legacy lib
was built without sets or themes. Use (.-sets base-lib) and
(.-themes base-lib), as the first part of the same test does.

AI-assisted-by: claude-opus-5-5
2026-10-05 08:25:15 +02:00
Belén Albeza
7c039231f7
🎉 Implement paste of basic HTML formatting (v3) (#12050)
* ✨ Add rich HTML paste to the v3 text editor

Pasting HTML into the v3 text editor keeps bold, italic, decoration and
text-transform, and drops fonts, colors and links. Gated behind
text-editor-wasm/v1-html-paste.

Refs #10465

AI-assisted-by: claude-opus-5-5

* ✨ Paste HTML on the canvas as a styled text shape

With v3 HTML paste on, pasting HTML on the canvas creates a text shape
with the same emphasis. Clipboard options are now read at paste time.

Refs #10465

AI-assisted-by: claude-opus-5-5
2026-10-02 15:07:12 +02:00
Dr. Dominik Jain
8efbd9b5d3
🎉 Allow MCP to work with multiple files in parallel (#11927)
* 🎉 Add multi-file session routing to the MCP server

Session routing:
- Track each user's Penpot connections by session ID.
- Dispatch explicit sessions directly; otherwise discover the sole session.
- Return a retryable error when discovery fails or is incomplete.

Plugin and tools:
- Initialize connections with a fresh session ID and file metadata.
- Share the optional sessionId schema and place it last in tool inputs.

Validation:
- 84 tests, server type checking, and MCP formatting checks pass.
- Disable test logging to avoid the logging worker shutdown hang.

AI-assisted-by: gpt-6

* 🎉 Support independent MCP connections

- Require explicit connection intent for each integrated workspace.
- Derive short session IDs from the Penpot app instance and file.
- Show and copy session IDs in the MCP menu and standalone plugin UI.
- Stop stale callbacks and reconnect attempts after disconnect.

Validate with frontend and plugin tests, type checking, live multi-tab
checks, and standalone UI checks with a simulated connection. Server
tests pass with --test-force-exit; the normal runner can hang on shutdown.

AI-assisted-by: gpt-6

* 📚 Document multi-file MCP sessions

- Explain independent connections, session selection, and copying IDs.
- Describe session ID lifetimes for integrated and standalone plugins.
- Correct browser focus, connection, and recovery guidance.

Validate with the documentation site build and diff checks.

AI-assisted-by: gpt-6

* ✨ Change behavior on session ID duplication

* ✨ Open MCP toolbar menu on hover and match menu style

---------

Co-authored-by: alonso.torres <alonso.torres@kaleidos.net>
Co-authored-by: elhombretecla <delacruzgarciajuan@gmail.com>
2026-10-02 14:22:52 +02:00
David Barragán Merino
61bc585386 👷 Accept remaining zizmor exceptions
wrangler stays an ad hoc install in the plugin deploys and the devenv
image keeps its floating tag: both are deliberate, so mark them as
accepted instead of pending.

AI-assisted-by: Claude
Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-10-02 12:49:04 +02:00
Elena Torró
d531c0050b
⚡ Skip builder local apply on plain translations (#12036)
Translations that touch no group-like shape or ancestor do not need
the builder objects updated, since only parent resizing reads them.

AI-assisted-by: claude-opus-5-5
2026-10-02 12:39:18 +02:00
David Barragán Merino
e65d10017d 👷 Use a slug of the ref for Docker tags
A git ref accepts characters that a Docker tag does not, such as "/" in
feature/foo, which made the promote and retag steps fail. Tag images with
a slug of the ref instead (anything outside [A-Za-z0-9._-] becomes "-",
at most 100 chars), as payments already does. develop, staging and
release tags are unchanged.

AI-assisted-by: Claude
Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-10-02 12:27:34 +02:00
David Barragán Merino
63ee26a502 👷 Require ci-maintainers review for .github
Add a CODEOWNERS entry for .github/, so changes to workflows, CI
configuration and repository automation need a review from the
ci-maintainers team.

AI-assisted-by: Claude
Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-10-02 12:27:34 +02:00
David Barragán Merino
7e73a42123 👷 Label Dependabot PRs and skip them in auto-label
Dependabot PRs get the dependencies, team infra and no issue required
labels, and auto-label no longer adds them to the triage board.

AI-assisted-by: Claude
Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-10-02 12:27:34 +02:00
Elena Torró
da55d6723c
⚡ Reduce per-frame CLJS work while dragging (#12027)
Memoize snap points and frame titles on the base objects and compute
the drag target frames once per gesture.
2026-10-02 12:24:04 +02:00
Elena Torró
822dbd6517
⚡ Speed up sidebar menus for large selections (#12028)
Reuse extracted colors for unchanged shapes and cache the text attrs
read from each text content.
2026-10-02 12:05:25 +02:00
Elena Torró
c148ba5267
⚡ Rebuild drag tiles per moved subtree (#12030)
WASM receives only the transforms that differ from the parent (bool
operands and masks are kept), rebuilds tiles once per moved subtree
and invalidates only the previous frame coverage.
2026-10-02 11:39:24 +02:00
Elena Torró
13a9da3bc3
⚡ Reduce commit cost when dropping large selections (#12032)
Collect children ids into one transient vector and track WASM shape
changes in a JS Map instead of a volatile persistent map.

AI-assisted-by: claude-opus-5-5
2026-10-02 11:22:19 +02:00
Dr. Dominik Jain
3a0ed93360
📚 Improve MCP README (#12022)
* Make the scope of the usage instructions clear (local usage)
* Provide overview of the server's different modes of operation
* Improve npm release instructions
* Remove deprecated multi-user mode documentation
2026-10-02 11:08:53 +02:00
Andrey Antukh
ea3d18c0a5
✨ Audit-log docs, error-reporting flag and frontend initiator (#11997)
* ✨ Gate error reporters behind :error-reporting flag

Error reporters used to start on every boot with no way to
switch them off. They now only start when the new
:error-reporting flag is on (off by default, enable with
PENPOT_FLAGS=enable-error-reporting). The database reporter
needs the flag alone; mattermost needs the flag plus its
webhook url. Shutdown with the flag off is safe, and the
database loop now honors its runtime switch.

AI-assisted-by: muse-spark-1.3-contributor

* 📚 Add audit-log reference memory and update dev tooling

Adds the top-level audit-log reference memory with all event
flows and turns backend/audit-log into a redirect stub.
Also includes the staged dev-tooling updates (playwright
deps, mdts args, gitignore).

AI-assisted-by: muse-spark-1.3-contributor

* ✨ Assign server-side initiator to frontend audit events

Frontend audit events now carry a server-assigned initiator
derived from the x-client header (penpot-frontend goes to
app, penpot-admin-console goes to admin-console, legacy
penpot-nitrate stays on admin-console, all else goes to
app). Client-sent values are always overwritten, and the
key survives on telemetry shadow rows.

AI-assisted-by: muse-spark-1.3-contributor

* 🐛 Keep initiator on frontend telemetry context

The strips-pii-keys test still asserted the old contract
(initiator stripped). The initiator only names the sending
app, so it is not personal data and must survive the
telemetry filter like on the backend channel.

AI-assisted-by: muse-spark-1.3-contributor

* 📚 Document initiator on PostHog and browser origins

Complete the audit-log memory for the latest changes: the
initiator property forwarded to PostHog on both channels
and the eventOrigin rule for admin-console browser events.

AI-assisted-by: muse-spark-1.3-contributor

* 🐛 Fix unscaped argument in script

---------

Co-authored-by: alonso.torres <alonso.torres@kaleidos.net>
2026-10-02 11:08:13 +02:00
Elena Torró
1554847d40
⚡ Compute the drop ignore tree in a single pass (#12033)
Walk only from the top-most transformed shapes, carry the component
root down the walk and memoize each shape transform so the update
step reuses it.

AI-assisted-by: claude-opus-5-5
2026-10-02 10:51:21 +02:00
Elena Torró
09ffcb1fbc
⚡ Reduce render-wasm GPU memory and move cost on large pages (#11980) 2026-10-02 10:24:05 +02:00
María Valderrama
c8ef223f67
🐛 Fix organization/team switcher ui (#12019) 2026-10-02 09:58:38 +02:00
David Barragán Merino
571a8805e6 👷 Document remaining zizmor exceptions
Mark each remaining ignore in zizmor.yml as a pending clean-up or an
accepted risk, with its reason.

Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-10-02 09:50:58 +02:00
David Barragán Merino
8c1a813b14 👷 Drop dependency cache from plugin doc deploys
Deploy workflows must not restore a cache that other workflows can
write to. The pnpm store cache is removed from the api-doc and
styles-doc deploys; installs are slower but deploys are infrequent.

Also install with --frozen-lockfile in the three plugin deploys, as CI
already does by default, so a deploy never resolves dependencies that
differ from the lockfile.

Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-10-02 09:50:58 +02:00
David Barragán Merino
46d64da931 👷 Scope the triage GitHub App token
Request a token for this repository only, instead of every repository
in the installation, and only with the permissions the job uses: issues
(labels) and organization projects (the board). Also document why the
workflow needs pull_request_target and why it is safe.

Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-10-02 09:50:58 +02:00
David Barragán Merino
214cbbf678 👷 Drop pull_request_target from commit checker
The job only reads commit messages, which works with the pull_request
trigger for PRs from forks too. Running it on both triggers checked every
PR twice, once with a privileged token it does not need.

Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-10-02 09:50:58 +02:00
David Barragán Merino
7f95439bc2 👷 Declare least-privilege workflow permissions
Add an explicit permissions block to every workflow that relied on the
repository default. Entry points that only call reusable workflows get
permissions: {} and grant each call what the called workflow needs:
contents: read for the bundle and docker builds, contents: write for the
release, nothing for the admin-console dispatch (it uses its own token).
Workflows that check out code get contents: read; the commit checker
also gets pull-requests: read to list the PR commits.

Signed-off-by: David Barragán Merino <david.barragan@kaleidos.net>
2026-10-02 09:50:58 +02:00
Andrey Antukh
84c794c5b8 Merge remote-tracking branch 'origin/staging' into develop 2026-10-01 19:25:59 +02:00
Andrey Antukh
3f610ad793
✨ Isolate backend-test database per devenv instance (#12048)
* ✨ Isolate backend-test database per devenv instance

Each wsN now gets its own backend-test database
(penpot_test_wsN) and Valkey DB (6+N) on the shared
infra, so parallel test runs no longer wipe each
other. The main database stays shared on purpose.

manage.sh creates the test database on bring-up and
passes PENPOT_TEST_* into the main container. Test
helpers already read those vars, so no runtime code
changes. scripts/psql and db-schema gain --ws.

AI-assisted-by: muse-spark-1.3-contributor-free

* 🐛 Fix per-instance test database creation on bring-up

psql -c does not reliably mix SQL with psql-only
commands, so the CREATE DATABASE ... WHERE NOT
EXISTS ... \gexec one-liner never ran. Use two
plain SQL round-trips instead (check pg_database,
then CREATE DATABASE) and print what happens, so a
future failure shows up in the bring-up output.

AI-assisted-by: muse-spark-1.3-contributor-free
2026-10-01 19:22:01 +02:00
Belén Albeza
60679bbbcd
🐛 Revert RTL auto-width text growing from its right edge (#12051)
Revert #11775 (commit 142f3d9de8). We are putting this fix on hold
until we settle the UX for RTL auto-width text.

Relates to #11523
2026-10-01 18:29:07 +02:00
Pablo Alba
03e24d18ce
✨ Dual-send audit log archive to Nitrate and Nexus (#12037)
Run the audit archive cron when :nexus or :admin-console is on. Ship
allowlisted events to Admin Console first (with row ids for
idempotency), then the full chunk to Nexus when :nexus is set. Mark
archived_at for the whole chunk on success, including nitrate-only
mode. Rename the gate flag from :audit-log-archive to :nexus.

AI-assisted-by: Composer

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-01 17:17:11 +02:00