Commit 68cf0fb
authored
fix(locking): a parked run keeps its locks, and the lock nodes exist in the editor (#3454)
* fix(locking): a parked run keeps its locks, and the lock nodes exist in the editor
Three defects in the run-scoped locking that #3444 shipped, all three found by
walking a live instance and none by the suite.
A RUN LOST EVERY LOCK THE MOMENT IT PARKED. The dispatch predicate really is
`isTerminal()` on the persisted row, and the row really did say `completed` —
transiently, in the middle of a pass that went on to store `suspended`.
`FlowEngine::fireOnStream()` computes `enabledAfter` by asking
`FlowStreamWalk::workRemains()` for the transitions enabled on the marking it
has just advanced, but that method answers from the walk's in-memory stream
picture, which `commitFiring()` only re-reads AFTER the commit. So it compared
the NEW enabled transitions against the OLD places and answered "no work
remains" at every ordinary mid-flow firing. `applyDerivedStatus()` then took
its "nothing enabled, nothing parked, nothing terminal" arm and wrote
`completed`, `FlowRunMapper::update()` announced terminality, and the lock
listener released the run's locks — every firing, of every run, not just ones
that lock. The park path had the mirror bug: `workRemains()` was evaluated
before `park()` marked the stream parked, so a parking run was derived
`queued` with no wake time until `finalize()` corrected it.
`workRemains()` now takes what the caller's commit is about to change:
`produced`, the places the firing takes, and `settling`, a stream whose place
stops counting because it is parking. Over-counting is the safe direction here
— it yields `queued`, which the next pass corrects — and under-counting is
what produced a false terminal.
A RUN-KIND LOCK REFUSED THE RUN THAT HELD IT. `SaveObject` asked the guard
"which user", never "which run", so a flow that locked a case was turned away
by its own lock at its next write. The run identity now reaches the guard
through the ambient `FlowRunContext`, for the same reason attribution does:
the write is routinely several calls deep inside code that has never heard of
flows. Absent, it reads as a person, which is the fail-closed answer.
The D-6 sweep is re-done over the GUARD's callers, not just `lockObject()`'s:
`SaveObject`, `RevertHandler` and `ObjectsController::update` now pass the
caller's run; the three post-save auto-unlock tests in the controller
deliberately do not, and say so — they decide a RELEASE, and a run's lock must
outlive every write the run makes. `LockObjectNode` and `UnlockObjectNode`
already passed theirs.
The same predicate had the opposite hole: a user lock did not refuse a run
executing as its holder, so a run passing over a person's locked object took
the extend branch, rewrote the payload as its own run lock, and destroyed the
person's lock when it ended. A run and the person it runs as are different
holders in both directions.
BOTH NODES WERE INVISIBLE IN THE EDITOR. `core/img/actions/lock.svg` and
`unlock.svg` do not exist in NC 33 or 34, `imagePath()` throws for an image
the server does not ship, and `palette()` caught that with everything else —
so the catalogue held 25 nodes rather than 27 and neither node could be added
to a flow at all. The icons are now app-owned, and the silent skip is loud: an
icon is resolved on its own, an unresolvable one is an ERROR naming the node,
and the node is served with the app icon instead of being deleted from the
catalogue. A node that survives with the wrong picture beats a node that does
not exist.
Tests, each proven red first, driving the real engine rather than a fake — the
existing coverage mocked `FlowRunMapper::update()` and restated
`workRemains()` in a fake, so both agreed with the bug:
- a suspended run announces nothing and keeps its locks, and each of
`FlowRun::TERMINAL` releases them (iterated from the constant)
- the holding run writes to its own locked object; another run and a person
are refused
- a person's lock survives a run passing over the object, payload byte-identical
- every registered node's icon resolves, as a sweep over all 27 rather than a
check of these two, plus the palette behaviour that stops the next one vanishing
* fix(locking): trace the two icon methods to the palette requirement
gate-16 counts a changed method with no `@spec` as an untraceable change, and
these two are exactly the methods the palette requirement is about.
* test(locking): say why each terminal leg rebuilds the harness1 parent d85a0b8 commit 68cf0fb
18 files changed
Lines changed: 1540 additions & 23 deletions
File tree
- img
- lib
- Controller
- Db
- Service
- Flow
- Nodes
- Object
- openspec/changes/run-scoped-object-locking/specs/run-scoped-object-locking
- tests
- Fixtures
- Unit
- Db
- Service
- Flow
- Object
Loading
Loading
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2856 | 2856 | | |
2857 | 2857 | | |
2858 | 2858 | | |
2859 | | - | |
| 2859 | + | |
2860 | 2860 | | |
2861 | 2861 | | |
2862 | 2862 | | |
| |||
2923 | 2923 | | |
2924 | 2924 | | |
2925 | 2925 | | |
| 2926 | + | |
| 2927 | + | |
| 2928 | + | |
| 2929 | + | |
| 2930 | + | |
| 2931 | + | |
| 2932 | + | |
| 2933 | + | |
2926 | 2934 | | |
2927 | 2935 | | |
2928 | 2936 | | |
| |||
3152 | 3160 | | |
3153 | 3161 | | |
3154 | 3162 | | |
| 3163 | + | |
| 3164 | + | |
| 3165 | + | |
| 3166 | + | |
| 3167 | + | |
| 3168 | + | |
| 3169 | + | |
| 3170 | + | |
3155 | 3171 | | |
3156 | 3172 | | |
3157 | 3173 | | |
| |||
3334 | 3350 | | |
3335 | 3351 | | |
3336 | 3352 | | |
| 3353 | + | |
| 3354 | + | |
| 3355 | + | |
| 3356 | + | |
| 3357 | + | |
| 3358 | + | |
| 3359 | + | |
| 3360 | + | |
3337 | 3361 | | |
3338 | 3362 | | |
3339 | 3363 | | |
| |||
4844 | 4868 | | |
4845 | 4869 | | |
4846 | 4870 | | |
| 4871 | + | |
| 4872 | + | |
| 4873 | + | |
| 4874 | + | |
| 4875 | + | |
| 4876 | + | |
| 4877 | + | |
| 4878 | + | |
| 4879 | + | |
| 4880 | + | |
| 4881 | + | |
| 4882 | + | |
| 4883 | + | |
| 4884 | + | |
| 4885 | + | |
| 4886 | + | |
| 4887 | + | |
| 4888 | + | |
| 4889 | + | |
| 4890 | + | |
| 4891 | + | |
| 4892 | + | |
| 4893 | + | |
| 4894 | + | |
| 4895 | + | |
| 4896 | + | |
| 4897 | + | |
| 4898 | + | |
| 4899 | + | |
| 4900 | + | |
| 4901 | + | |
4847 | 4902 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1163 | 1163 | | |
1164 | 1164 | | |
1165 | 1165 | | |
| 1166 | + | |
| 1167 | + | |
| 1168 | + | |
| 1169 | + | |
| 1170 | + | |
| 1171 | + | |
1166 | 1172 | | |
1167 | 1173 | | |
1168 | 1174 | | |
| |||
1189 | 1195 | | |
1190 | 1196 | | |
1191 | 1197 | | |
| 1198 | + | |
| 1199 | + | |
| 1200 | + | |
| 1201 | + | |
| 1202 | + | |
| 1203 | + | |
| 1204 | + | |
| 1205 | + | |
| 1206 | + | |
1192 | 1207 | | |
1193 | 1208 | | |
1194 | 1209 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
938 | 938 | | |
939 | 939 | | |
940 | 940 | | |
941 | | - | |
| 941 | + | |
| 942 | + | |
| 943 | + | |
| 944 | + | |
| 945 | + | |
| 946 | + | |
| 947 | + | |
| 948 | + | |
| 949 | + | |
942 | 950 | | |
943 | 951 | | |
944 | 952 | | |
| |||
1123 | 1131 | | |
1124 | 1132 | | |
1125 | 1133 | | |
1126 | | - | |
| 1134 | + | |
| 1135 | + | |
| 1136 | + | |
| 1137 | + | |
| 1138 | + | |
| 1139 | + | |
| 1140 | + | |
| 1141 | + | |
| 1142 | + | |
| 1143 | + | |
1127 | 1144 | | |
1128 | 1145 | | |
1129 | 1146 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
| 35 | + | |
35 | 36 | | |
36 | 37 | | |
37 | 38 | | |
| |||
108 | 109 | | |
109 | 110 | | |
110 | 111 | | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
111 | 118 | | |
112 | 119 | | |
113 | 120 | | |
114 | 121 | | |
| 122 | + | |
115 | 123 | | |
116 | 124 | | |
117 | 125 | | |
| |||
204 | 212 | | |
205 | 213 | | |
206 | 214 | | |
207 | | - | |
| 215 | + | |
208 | 216 | | |
209 | 217 | | |
210 | 218 | | |
| |||
250 | 258 | | |
251 | 259 | | |
252 | 260 | | |
253 | | - | |
254 | | - | |
255 | | - | |
256 | | - | |
257 | | - | |
258 | | - | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
| 269 | + | |
| 270 | + | |
| 271 | + | |
| 272 | + | |
| 273 | + | |
| 274 | + | |
| 275 | + | |
259 | 276 | | |
260 | 277 | | |
261 | 278 | | |
262 | 279 | | |
263 | 280 | | |
264 | 281 | | |
265 | 282 | | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
| 289 | + | |
| 290 | + | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
266 | 330 | | |
267 | 331 | | |
268 | 332 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
160 | 160 | | |
161 | 161 | | |
162 | 162 | | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
163 | 197 | | |
164 | 198 | | |
165 | 199 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
253 | 253 | | |
254 | 254 | | |
255 | 255 | | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
| 262 | + | |
| 263 | + | |
| 264 | + | |
| 265 | + | |
| 266 | + | |
| 267 | + | |
| 268 | + | |
256 | 269 | | |
| 270 | + | |
| 271 | + | |
257 | 272 | | |
258 | 273 | | |
259 | 274 | | |
260 | 275 | | |
261 | 276 | | |
262 | | - | |
263 | | - | |
264 | | - | |
265 | | - | |
266 | | - | |
267 | | - | |
268 | | - | |
| 277 | + | |
| 278 | + | |
269 | 279 | | |
270 | 280 | | |
271 | 281 | | |
272 | | - | |
| 282 | + | |
273 | 283 | | |
274 | 284 | | |
275 | 285 | | |
| |||
278 | 288 | | |
279 | 289 | | |
280 | 290 | | |
| 291 | + | |
| 292 | + | |
| 293 | + | |
| 294 | + | |
| 295 | + | |
| 296 | + | |
| 297 | + | |
| 298 | + | |
| 299 | + | |
| 300 | + | |
| 301 | + | |
| 302 | + | |
| 303 | + | |
| 304 | + | |
| 305 | + | |
| 306 | + | |
| 307 | + | |
| 308 | + | |
| 309 | + | |
| 310 | + | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
281 | 322 | | |
282 | 323 | | |
283 | 324 | | |
| |||
0 commit comments