studio-engine 0.29.0 → 0.29.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b93ed19a9ea116acfe2d15bfcdd7cabc9abcf6ae2de9a63bfdcfc6d412d60e73
4
- data.tar.gz: 9a98b2c22d817cddc642df050e1f9ecc34dfd87d9d87e4730b735774fec579b1
3
+ metadata.gz: db4fb7663508ced21fce80ca29ec063c300c2826e0dadc511c266219303e449d
4
+ data.tar.gz: b7e7c5cb133164b6d2a0521d4f08291540a78b4fb01a0be3b840f5413e35d8bb
5
5
  SHA512:
6
- metadata.gz: 6d6cc744ebba89724d9811785d79a954d43b9cd9827d8fc5f86780cd465312668215b31a989cbb1a57c973ad74507600d7b4ae9179c912780c5beffa073c2f76
7
- data.tar.gz: 39cace3ee05070698a8f06cf1c4463e03cf8bd8fdc97d2ad6b1b5d158a87fc23425e8618f09335034e5dfb04a70a5529b00c9a9743cedd001d6e9f6fe57204a4
6
+ metadata.gz: 328965e3979861ddc61b3c312a665b42b4e17d4d35a45851c3c76e9c538904e8096ba20725aa81fd45ba3e0509e8cf1fa764bbac70483a26afb5d8273bb43341
7
+ data.tar.gz: c3323dfb24072f240935a1c3300dcf694d9c5ba22097fe2c1dc27ed04d0ee68cfb18df95844fe182e71622ccc6ba151dcb8ef3c5cc3488b5ac9959535e16c0c3
data/CHANGELOG.md CHANGED
@@ -2,6 +2,24 @@
2
2
 
3
3
  The format is [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). This project follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html) — `MAJOR.MINOR.PATCH`. Consumer Rails apps install the released RubyGems package with `gem "studio-engine", "~> 0.6"`; bumping the gem version and updating consumer lockfiles is a release.
4
4
 
5
+ ## 0.29.1 — 2026-07-29
6
+
7
+ **`Studio::Board::Rankable#reposition!` is now atomic — a mid-loop failure can no
8
+ longer strand a partial restamp.** The per-id `update_all` restamp loop ran with no
9
+ transaction, so if any one write raised (a constraint violation, a dropped
10
+ connection) the cards already stamped kept their new rank while the rest kept the
11
+ old — the read model left half-reordered. The loop body now runs inside a single
12
+ `transaction`, so a failure rolls back every rank write in the pass. Purely
13
+ additive: the success path is byte-identical, and `skip_locked` / `rank_attr` /
14
+ `gap` / `direction` semantics are unchanged, so all four boards (tasks / news /
15
+ content / depth-chart) reorder exactly as before.
16
+
17
+ ### Fixed
18
+
19
+ - **`Studio::Board::Rankable#reposition!` wraps its restamp loop in a transaction**
20
+ so a mid-loop `update_all` failure rolls back the whole column instead of leaving
21
+ a partial restamp. No behavior change on the clean path.
22
+
5
23
  ## 0.29.0 — 2026-07-29
6
24
 
7
25
  **The four depth-chart gaps folded into the `studio/board` primitive (Phase D), so
@@ -97,15 +97,23 @@ module Studio
97
97
  # DG4 — `skip_locked: true` leaves a PINNED entry untouched: its rank column is
98
98
  # not written, so a locked starter keeps its depth while the others reflow
99
99
  # around it. The lock flag column is `lock_attr` (default :locked).
100
+ #
101
+ # ATOMIC: the whole restamp runs in ONE transaction, so a mid-loop failure
102
+ # (a raised update_all — e.g. a constraint violation or a dropped connection)
103
+ # rolls back EVERY rank write in the pass. The read model never survives a
104
+ # partial restamp where some cards moved and others kept their old rank. The
105
+ # success path is unchanged — a clean pass commits exactly as before.
100
106
  def reposition!(ids, gap: 100, direction: :desc, id_attr: primary_key,
101
107
  rank_attr: board_rank_attr, skip_locked: false, lock_attr: :locked)
102
108
  ids = Array(ids)
103
109
  length = ids.length
104
- ids.each_with_index do |id, index|
105
- rank = direction.to_sym == :asc ? (index + 1) * gap : (length - index) * gap
106
- rel = where(id_attr => id)
107
- rel = rel.where.not(lock_attr => true) if skip_locked
108
- rel.update_all(rank_attr => rank)
110
+ transaction do
111
+ ids.each_with_index do |id, index|
112
+ rank = direction.to_sym == :asc ? (index + 1) * gap : (length - index) * gap
113
+ rel = where(id_attr => id)
114
+ rel = rel.where.not(lock_attr => true) if skip_locked
115
+ rel.update_all(rank_attr => rank)
116
+ end
109
117
  end
110
118
  ids
111
119
  end
@@ -1,3 +1,3 @@
1
1
  module Studio
2
- VERSION = "0.29.0"
2
+ VERSION = "0.29.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: studio-engine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.29.0
4
+ version: 0.29.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alex McRitchie
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-07-29 00:00:00.000000000 Z
11
+ date: 2026-07-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails