milestone 18: collapse duplicated infrastructure into shared listener core, crud list helper, resource shells, transport race, name and line helpers, ui modules
CI / test (push) Successful in 1m22s
CI / test-aarch64 (push) Successful in 5m6s
CI / frontend (push) Successful in 45s
CI / cross (push) Successful in 7m53s
CI / docker (push) Failing after 1h10m57s

This commit is contained in:
2026-08-07 18:20:30 +02:00
parent c50c6d285a
commit 6f67940995
82 changed files with 3167 additions and 3114 deletions
+11 -2
View File
@@ -1258,18 +1258,27 @@ predicate in S5 needs the true count.
SQLite and is invalidated by the next `step`; a repository that returns a borrowed slice is a
use-after-free waiting for the second row.
- Every `list` builds into a `std.ArrayList(T)` with an `errdefer` that frees **both** every element
already appended and every string of the partially-built element:
already appended and every string of the partially-built element. The two list-level `errdefer`s
run in reverse declaration order, so the free pass is declared **after** `out.deinit` to run
**before** it — the other order reads `out.items` once the backing array is already released:
```zig
var out: std.ArrayList(model.Group) = .empty;
errdefer freeGroups(gpa, out.items);
errdefer out.deinit(gpa);
errdefer freeGroups(gpa, out.items);
while (try stmt.step()) {
const name = try stmt.columnTextAlloc(gpa, 0);
errdefer gpa.free(name);
try out.append(gpa, .{ .name = name, .safe_search = stmt.columnBool(1) });
}
```
> **Correction (milestone 18, ruling 2).** This sample originally declared the two `errdefer`s in
> the opposite order, which frees the backing array before the pass that walks it — a
> use-after-free. Every repository written from it silently corrected the order; the sample above
> is the corrected one. Milestone 18 moved the whole choreography into
> `src/storage/repositories/crud.zig` as `listRows`/`freeRows`, so a new repository delegates to
> that helper instead of copying this shape.
- `freeX` frees every heap string in every element and is idempotent against an empty slice.
- The callers of `list` may pass an arena; `freeX` must still be correct against a general-purpose
allocator, because the tests use `std.testing.allocator`.