Skip to content

Commit 6218c42

Browse files
committed
Implement selective API hardening
1 parent 8e771c5 commit 6218c42

5 files changed

Lines changed: 354 additions & 12 deletions

File tree

CHANGELOG.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
# Changelog
22

3+
## 2026-02-26
4+
5+
- Added debug-time pinned-struct move guards for callback binding invariants (`App`/`Service`).
6+
- Added typed lifecycle diagnostics for detected move violations:
7+
- `lifecycle.pinned_struct_moved.app`
8+
- `lifecycle.pinned_struct_moved.service`
9+
- Added deterministic invariant regression tests for stable path, moved path detection, diagnostic emission, and no-false-positive flow.
10+
- Added move/pinning safety guidance in `README.md`, `docs/migration.md`, and `MIGRATION.md`.
11+
- Preserved runtime architecture (no allocation-based pinning rewrite).
12+
313
## 2026-02-24
414

515
- Replaced active C-backed build/runtime path with Zig-only modules.

MIGRATION.md

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,33 @@ Browser fallback controls:
5252

5353
```zig
5454
var app = try webui.App.init(allocator, .{
55-
.transport_mode = .native_webview,
56-
.browser_fallback_on_native_failure = true,
57-
.auto_open_browser = true,
55+
.launch_policy = .{
56+
.preferred_transport = .native_webview,
57+
.fallback_transport = .browser,
58+
.browser_open_mode = .on_browser_transport,
59+
},
5860
});
5961
```
62+
63+
## Pinned Struct Move Safety
64+
65+
The runtime currently enforces move safety by contract (not by forced heap pinning):
66+
67+
- Do not move/copy an initialized `App` after it owns windows.
68+
- Do not move/copy an initialized `Service` after `Service.init`.
69+
70+
Unsafe examples:
71+
- Extra by-value copies of initialized structs.
72+
- Relocating containers that move initialized `Service`/`App` entries.
73+
74+
Safe examples:
75+
- Initialize in final storage.
76+
- Pass pointers (`*Service`, `*App`) across helpers.
77+
78+
Debug guard behavior:
79+
- With diagnostics enabled via `onDiagnostic(...)`, `Debug`/`ReleaseSafe` emit typed diagnostics and fail fast:
80+
- `lifecycle.pinned_struct_moved.app`
81+
- `lifecycle.pinned_struct_moved.service`
82+
- `ReleaseFast`/`ReleaseSmall` compile these checks out.
83+
84+
This is intentionally documented and guarded behavior, not an allocation-based pinning redesign.

README.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,33 @@ pub fn main() !void {
176176
}
177177
```
178178

179+
## Pinned Struct Move Safety
180+
181+
`App` and `Service` are move-sensitive once windows are created.
182+
183+
Rules:
184+
- Once an `App` owns windows, do not move/copy that initialized `App` by value.
185+
- Once a `Service` is initialized, do not move/copy that initialized `Service` by value.
186+
187+
Unsafe patterns:
188+
- Returning/stashing an initialized `Service`/`App` through extra by-value copies.
189+
- Storing initialized `Service` values in relocating containers.
190+
- Assigning `var moved = service;` (or equivalent copies) after init.
191+
192+
Safe patterns:
193+
- Initialize `App`/`Service` in its final storage location.
194+
- Pass pointers (`*App`, `*Service`) through helpers.
195+
- Keep initialized structs out of relocatable by-value containers.
196+
197+
Debug behavior:
198+
- In `Debug`/`ReleaseSafe`, when diagnostics are enabled via `onDiagnostic(...)`, the runtime checks callback-binding invariants and emits typed diagnostics:
199+
- `lifecycle.pinned_struct_moved.app`
200+
- `lifecycle.pinned_struct_moved.service`
201+
- After emitting the diagnostic, it fails fast to avoid latent crashes.
202+
- In `ReleaseFast`/`ReleaseSmall`, this guard is compiled out.
203+
204+
This project intentionally documents and guards move-safety instead of forcing allocation-based pinning.
205+
179206
## Typed RPC + Bridge Generation
180207

181208
Everything starts with a comptime method set:

docs/migration.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,24 @@ Typed control APIs:
122122

123123
- Existing warning log strings may still appear, but typed diagnostics are the authoritative integration surface.
124124
- For Linux packaging, validate helper/runtime presence through `listRuntimeRequirements`.
125+
126+
## Pinned Struct Move Safety
127+
128+
This release keeps the current architecture (no forced allocation pinning), so move-safety is explicit:
129+
130+
- Do not move/copy an initialized `App` after it owns windows.
131+
- Do not move/copy an initialized `Service` after `Service.init`.
132+
133+
Avoid:
134+
- By-value hops of initialized values between helper return values/temporaries.
135+
- Relocating containers that move initialized `Service`/`App` values.
136+
137+
Prefer:
138+
- Initialize in final storage.
139+
- Pass pointers (`*Service`, `*App`) across function boundaries.
140+
141+
Debug guard behavior:
142+
- With diagnostics enabled via `onDiagnostic(...)`, `Debug` and `ReleaseSafe` emit typed diagnostics then fail fast:
143+
- `lifecycle.pinned_struct_moved.app`
144+
- `lifecycle.pinned_struct_moved.service`
145+
- `ReleaseFast` and `ReleaseSmall` compile these checks out.

0 commit comments

Comments
 (0)