Skip to content

Commit 10ed787

Browse files
hyperpolymathclaude
andcommitted
feat(zig-api/abi): rename src/abi/ → src/ZigApi/ABI/, add Layout + Proofs
Idris2 requires source paths to match module names. Rename the flat src/abi/ layout to the proper Idris2 hierarchy src/ZigApi/ABI/ so the package compiles with `module ZigApi.ABI.*` declarations. Added the two remaining modules: Layout.idr — machine-checked ABI layout claims (total, zero believe_me): - Tag-count bounds: Result×11, ServiceId×11, ConnectorState×6, Method×7, ServerState×4, ExecResult×6, HealthStatus×2 all fit in u8 - Pool/sentinel bounds: poolCapacity(64) ≤ sentinel(255) — valid slot indexes are unambiguously distinct from the failure sentinel - Buffer bounds: responseBufferSize(4096) fits in uint32_t Proofs.idr — cross-cutting correctness proofs: - Slot validity: So (255 < 64) is uninhabited → sentinel cannot be a ValidSlot; any ValidSlot index rewriting to 255 reaches Void - Roundtrip completeness: all *FromTag(*Tag x) = Just x re-exported - Cross-enum tag coincidence: Result.Ok and ServiceId.AmbientOps share tag 0 but decode safely (discrimination by call site) - SafePath soundness: empty allowlist denies; head match accepts; Left↔IsSafePath; Right↔¬IsSafePath - Health HTTP contract: Serving→200, NotServing→503, codes distinct Also adds zig-api.ipkg listing all seven modules so `idris2 --build` resolves the package correctly. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 11a1342 commit 10ed787

8 files changed

Lines changed: 424 additions & 0 deletions

File tree

zig-api/src/ZigApi/ABI/Layout.idr

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
-- SPDX-License-Identifier: PMPL-1.0-or-later
2+
-- Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
3+
--
4+
-- Layout.idr — ABI memory-layout claims for unified-zig-api
5+
--
6+
-- This module makes the C ABI layout constraints explicit and machine-checked.
7+
-- Every claim here corresponds to a compile-time guarantee in the Zig source
8+
-- (build.zig @compileError checks and std.debug.assert on sizes/capacities).
9+
--
10+
-- Three categories of claims:
11+
--
12+
-- 1. Tag-count bounds — the total number of constructors for each enum
13+
-- fits in u8 (< 256). Stated as `Nat.lte tagCount 255 = True`,
14+
-- which Idris2 reduces at the type level so `Refl` closes the goal.
15+
--
16+
-- 2. Pool and sentinel bounds — pool capacity is 64, the failure sentinel
17+
-- returned by uapi_connector_create is 255, 64 ≤ 255 so no valid slot
18+
-- index can equal the sentinel.
19+
--
20+
-- 3. Buffer bounds — response buffer size (4096 bytes) fits in uint32_t.
21+
--
22+
-- Note: Bits8 / Bits16 / Bits32 / Bits64 widths are guaranteed by the Idris2
23+
-- type system itself (they are machine-word types). No separate claim needed.
24+
25+
module ZigApi.ABI.Layout
26+
27+
import Data.Bits
28+
import ZigApi.ABI.Types
29+
import ZigApi.ABI.Http
30+
import ZigApi.ABI.Process
31+
import ZigApi.ABI.Connector
32+
33+
%default total
34+
35+
-- ============================================================================
36+
-- 1. Tag-count bounds (number of enum constructors fits in u8)
37+
-- ============================================================================
38+
--
39+
-- Proof strategy: each enum has a finite, concrete number of constructors.
40+
-- We name that count and prove `Nat.lte count 255 = True` by Refl.
41+
-- Idris2 evaluates `Nat.lte` at the type level so Refl closes the goal
42+
-- without any helper lemmas.
43+
44+
-- ---- Result (11 constructors, max tag = 10) ----
45+
46+
||| The Result enum has 11 constructors with tags 0..10.
47+
public export
48+
resultTagCount : Nat
49+
resultTagCount = 11
50+
51+
||| 11 distinct tags fit in a u8 (11 ≤ 255).
52+
public export
53+
resultTagCountFitsU8 : Nat.lte resultTagCount 255 = True
54+
resultTagCountFitsU8 = Refl
55+
56+
-- ---- ServiceId (11 constructors, max tag = 10) ----
57+
58+
||| The ServiceId enum has 11 constructors with tags 0..10.
59+
public export
60+
serviceIdTagCount : Nat
61+
serviceIdTagCount = 11
62+
63+
||| 11 distinct service IDs fit in a u8.
64+
public export
65+
serviceIdTagCountFitsU8 : Nat.lte serviceIdTagCount 255 = True
66+
serviceIdTagCountFitsU8 = Refl
67+
68+
-- ---- ConnectorState (6 constructors, max tag = 5) ----
69+
70+
||| The ConnectorState enum has 6 constructors with tags 0..5.
71+
public export
72+
connectorStateTagCount : Nat
73+
connectorStateTagCount = 6
74+
75+
public export
76+
connectorStateTagCountFitsU8 : Nat.lte connectorStateTagCount 255 = True
77+
connectorStateTagCountFitsU8 = Refl
78+
79+
-- ---- HTTP Method (7 constructors, max tag = 6) ----
80+
81+
||| The HTTP Method enum has 7 constructors with tags 0..6.
82+
public export
83+
methodTagCount : Nat
84+
methodTagCount = 7
85+
86+
public export
87+
methodTagCountFitsU8 : Nat.lte methodTagCount 255 = True
88+
methodTagCountFitsU8 = Refl
89+
90+
-- ---- ServerState (4 constructors, max tag = 3) ----
91+
92+
||| The ServerState enum has 4 constructors with tags 0..3.
93+
public export
94+
serverStateTagCount : Nat
95+
serverStateTagCount = 4
96+
97+
public export
98+
serverStateTagCountFitsU8 : Nat.lte serverStateTagCount 255 = True
99+
serverStateTagCountFitsU8 = Refl
100+
101+
-- ---- ExecResult (6 constructors, max tag = 5) ----
102+
103+
||| The ExecResult enum has 6 constructors with tags 0..5.
104+
public export
105+
execResultTagCount : Nat
106+
execResultTagCount = 6
107+
108+
public export
109+
execResultTagCountFitsU8 : Nat.lte execResultTagCount 255 = True
110+
execResultTagCountFitsU8 = Refl
111+
112+
-- ---- HealthStatus (2 constructors, max tag = 1) ----
113+
114+
||| The HealthStatus enum has 2 constructors with tags 0..1.
115+
public export
116+
healthStatusTagCount : Nat
117+
healthStatusTagCount = 2
118+
119+
public export
120+
healthStatusTagCountFitsU8 : Nat.lte healthStatusTagCount 255 = True
121+
healthStatusTagCountFitsU8 = Refl
122+
123+
-- ============================================================================
124+
-- 2. Pool and sentinel bounds
125+
-- ============================================================================
126+
127+
||| The connector pool holds exactly this many slots.
128+
||| Matches POOL_CAPACITY in connector.zig.
129+
public export
130+
poolCapacity : Nat
131+
poolCapacity = 64
132+
133+
||| 64 valid slot indices fit in a u8 (64 ≤ 255).
134+
public export
135+
poolCapacityFitsU8 : Nat.lte poolCapacity 255 = True
136+
poolCapacityFitsU8 = Refl
137+
138+
||| The failure sentinel returned by uapi_connector_create when no slot is
139+
||| available. Must be strictly greater than any valid slot index.
140+
public export
141+
connectorFailureSentinel : Nat
142+
connectorFailureSentinel = 255
143+
144+
||| 64 ≤ 255: every valid slot index is strictly below the sentinel.
145+
||| This means a caller can distinguish success from failure unambiguously.
146+
public export
147+
sentinelAbovePool : Nat.lte poolCapacity connectorFailureSentinel = True
148+
sentinelAbovePool = Refl
149+
150+
||| The sentinel 255 is not a valid pool index (pool capacity is 64).
151+
||| Equiv: 64 < 255 + 1, so poolCapacity ≤ connectorFailureSentinel.
152+
public export
153+
sentinelNotASlot : Nat.lte (S poolCapacity) (S connectorFailureSentinel) = True
154+
sentinelNotASlot = Refl
155+
156+
-- ============================================================================
157+
-- 3. Buffer bounds
158+
-- ============================================================================
159+
160+
||| Minimum response buffer size for uapi_connector_call.
161+
||| Matches RESPONSE_BUF_SIZE in connector.zig.
162+
public export
163+
responseBufferSize : Nat
164+
responseBufferSize = 4096
165+
166+
||| uint32_t maximum (2^32 − 1).
167+
public export
168+
uint32Max : Nat
169+
uint32Max = 4294967295
170+
171+
||| 4096 bytes fits in a uint32_t out_len parameter (4096 ≤ 2^32 − 1).
172+
public export
173+
responseBufferFitsU32 : Nat.lte responseBufferSize uint32Max = True
174+
responseBufferFitsU32 = Refl
175+
176+
||| The pool does not exceed the u8 range: poolCapacity < 256.
177+
||| (Stronger than FitsU8: the slot index type IS a Bits8.)
178+
public export
179+
poolFitsU8Type : Nat.lte poolCapacity 256 = True
180+
poolFitsU8Type = Refl

0 commit comments

Comments
 (0)