this repo has no description
1{"id":"swim-294","title":"Implement test generators (test/generators.ml)","description":"Create QCheck generators for property-based testing.\n\n## Generators to implement\n\n### Basic types\n- `gen_node_id : node_id QCheck.Gen.t`\n- `gen_incarnation : incarnation QCheck.Gen.t`\n- `gen_member_state : member_state QCheck.Gen.t`\n\n### Node types\n- `gen_node_info : node_info QCheck.Gen.t`\n - Generate valid addresses\n - Random metadata strings\n\n### Protocol messages\n- `gen_ping : protocol_msg QCheck.Gen.t`\n- `gen_ping_req : protocol_msg QCheck.Gen.t`\n- `gen_ack : protocol_msg QCheck.Gen.t`\n- `gen_alive : protocol_msg QCheck.Gen.t`\n- `gen_suspect : protocol_msg QCheck.Gen.t`\n- `gen_dead : protocol_msg QCheck.Gen.t`\n- `gen_user_msg : protocol_msg QCheck.Gen.t`\n- `gen_protocol_msg : protocol_msg QCheck.Gen.t` (uniform choice)\n\n### Packets\n- `gen_packet : packet QCheck.Gen.t`\n - Valid cluster names\n - Primary + piggyback messages\n\n### Binary data\n- `gen_cstruct : Cstruct.t QCheck.Gen.t`\n - Various sizes\n\n### Arbitrary instances\n- `arb_*` wrappers with shrinkers where useful\n\n## Design constraints\n- Use QCheck.Gen combinators\n- Generate valid data by construction\n- Include edge cases (empty strings, max values)","acceptance_criteria":"- All message types have generators\n- Generators produce valid data\n- Good distribution of test cases","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-08T18:49:22.04090675+01:00","created_by":"gdiazlo","updated_at":"2026-01-08T18:49:22.04090675+01:00","labels":["qcheck","test"],"dependencies":[{"issue_id":"swim-294","depends_on_id":"swim-td8","type":"blocks","created_at":"2026-01-08T18:49:22.044472866+01:00","created_by":"gdiazlo"},{"issue_id":"swim-294","depends_on_id":"swim-wdc","type":"parent-child","created_at":"2026-01-08T18:49:26.910584411+01:00","created_by":"gdiazlo"}]} 2{"id":"swim-461","title":"Implement crypto tests (test/test_crypto.ml)","description":"Property-based and unit tests for crypto module.\n\n## Property tests\n\n### Roundtrip\n- `test_crypto_roundtrip` - encrypt then decrypt equals original\n- Test with various data sizes\n\n### Key validation\n- `test_invalid_key_length_rejected`\n- Test 31, 32, 33 byte keys\n\n## Unit tests\n\n### Encryption\n- Test output size = input size + overhead (28 bytes)\n- Test nonce is prepended\n- Test different plaintexts produce different ciphertexts\n\n### Decryption\n- Test successful decryption\n- Test tampered ciphertext fails\n- Test truncated data fails\n- Test wrong key fails\n\n### Key initialization\n- Test valid 32-byte key\n- Test invalid lengths rejected\n\n## Security tests\n- Verify nonces are unique (probabilistic)\n- Verify ciphertext differs from plaintext\n\n## Design constraints\n- Use QCheck for property tests\n- Test all error paths\n- Don't expose key material in errors","acceptance_criteria":"- All property tests pass\n- All unit tests pass\n- Security properties verified\n- Error handling tested","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-08T18:49:51.401236876+01:00","created_by":"gdiazlo","updated_at":"2026-01-08T18:49:51.401236876+01:00","labels":["crypto","security","test"],"dependencies":[{"issue_id":"swim-461","depends_on_id":"swim-hc9","type":"blocks","created_at":"2026-01-08T18:49:51.404483911+01:00","created_by":"gdiazlo"},{"issue_id":"swim-461","depends_on_id":"swim-294","type":"blocks","created_at":"2026-01-08T18:49:51.405793127+01:00","created_by":"gdiazlo"},{"issue_id":"swim-461","depends_on_id":"swim-wdc","type":"parent-child","created_at":"2026-01-08T18:49:56.45969199+01:00","created_by":"gdiazlo"}]} 3{"id":"swim-90e","title":"Implement transport.ml - Eio UDP/TCP networking","description":"Implement network transport layer using Eio.\n\n## UDP Transport\n\n### Functions\n- `create_udp_socket : Eio.Net.t -\u003e addr:string -\u003e port:int -\u003e Eio.Net.datagram_socket`\n- `send_udp : Eio.Net.datagram_socket -\u003e Eio.Net.Sockaddr.datagram -\u003e Cstruct.t -\u003e unit`\n- `recv_udp : Eio.Net.datagram_socket -\u003e Cstruct.t -\u003e (int * Eio.Net.Sockaddr.datagram)`\n\n## TCP Transport (for large payloads)\n\n### Functions\n- `create_tcp_listener : Eio.Net.t -\u003e addr:string -\u003e port:int -\u003e Eio.Net.listening_socket`\n- `connect_tcp : Eio.Net.t -\u003e addr:Eio.Net.Sockaddr.stream -\u003e timeout:float -\u003e clock:Eio.Time.clock -\u003e (Eio.Net.stream_socket, send_error) result`\n- `send_tcp : Eio.Net.stream_socket -\u003e Cstruct.t -\u003e (unit, send_error) result`\n- `recv_tcp : Eio.Net.stream_socket -\u003e Cstruct.t -\u003e (int, [`Connection_reset]) result`\n\n## Address parsing\n- `parse_addr : string -\u003e (Eio.Net.Sockaddr.datagram, [`Invalid_addr]) result`\n - Parse \"host:port\" format\n\n## Design constraints\n- Use Eio.Net for all I/O\n- No blocking except Eio primitives\n- Proper error handling via Result\n- Support for IPv4 and IPv6","acceptance_criteria":"- UDP send/recv works\n- TCP connect/send/recv works\n- Proper error handling\n- Address parsing robust","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-08T18:48:09.296035344+01:00","created_by":"gdiazlo","updated_at":"2026-01-08T19:39:34.082898832+01:00","closed_at":"2026-01-08T19:39:34.082898832+01:00","close_reason":"Implemented UDP and TCP transport with Eio.Net, plus address parsing (mli skipped due to complex Eio row types)","labels":["core","eio","transport"],"dependencies":[{"issue_id":"swim-90e","depends_on_id":"swim-oun","type":"blocks","created_at":"2026-01-08T18:48:09.299855321+01:00","created_by":"gdiazlo"},{"issue_id":"swim-90e","depends_on_id":"swim-wdc","type":"parent-child","created_at":"2026-01-08T18:48:15.52111057+01:00","created_by":"gdiazlo"}]} 4{"id":"swim-don","title":"Implement benchmarks (bench/)","description":"Performance benchmarks for critical paths.\n\n## bench/bench_codec.ml\n- `bench_encode_ping` - encoding a Ping message\n- `bench_encode_packet` - full packet with piggyback\n- `bench_decode_packet` - decoding a packet\n- `bench_encoded_size` - size calculation\n\n## bench/bench_crypto.ml\n- `bench_encrypt` - encryption throughput\n- `bench_decrypt` - decryption throughput\n- `bench_key_init` - key initialization\n\n## bench/bench_throughput.ml\n- `bench_broadcast_throughput` - messages/second\n- `bench_probe_cycle` - probe cycle latency\n- `bench_concurrent_probes` - parallel probe handling\n\n## bench/bench_allocations.ml\n- `bench_probe_cycle_allocations` - count allocations per probe\n- `bench_buffer_reuse_rate` - % of buffers reused\n- `bench_message_handling_allocations` - allocations per message\n\n## Performance targets to verify\n- \u003c 5 allocations per probe cycle\n- \u003e 95% buffer reuse rate\n- \u003c 3 seconds failure detection\n- \u003e 10,000 broadcast/sec\n- \u003c 1% CPU idle, \u003c 5% under load\n\n## Design constraints\n- Use core_bench or similar\n- Warm up before measuring\n- Multiple iterations for stability\n- Report with confidence intervals","acceptance_criteria":"- All benchmarks run\n- Performance targets documented\n- Regression detection possible\n- Results reproducible","status":"open","priority":3,"issue_type":"task","created_at":"2026-01-08T18:50:57.818433013+01:00","created_by":"gdiazlo","updated_at":"2026-01-08T18:50:57.818433013+01:00","labels":["bench","performance"],"dependencies":[{"issue_id":"swim-don","depends_on_id":"swim-zsi","type":"blocks","created_at":"2026-01-08T18:50:57.821397737+01:00","created_by":"gdiazlo"},{"issue_id":"swim-don","depends_on_id":"swim-wdc","type":"parent-child","created_at":"2026-01-08T18:51:03.066326187+01:00","created_by":"gdiazlo"}]} 5{"id":"swim-etm","title":"Implement pending_acks.ml - Ack tracking with promises","description":"Implement pending ack tracking for probe responses.\n\n## Pending_acks module\n```ocaml\ntype waiter = {\n promise : string option Eio.Promise.t;\n resolver : string option Eio.Promise.u;\n}\n\ntype t = {\n table : (int, waiter) Kcas_data.Hashtbl.t;\n}\n```\n\n### Functions\n- `create : unit -\u003e t`\n\n- `register : t -\u003e seq:int -\u003e waiter`\n - Create promise/resolver pair\n - Store in hashtable keyed by sequence number\n - Return waiter handle\n\n- `complete : t -\u003e seq:int -\u003e payload:string option -\u003e bool`\n - Find waiter by seq\n - Resolve promise with payload\n - Remove from table\n - Return true if found\n\n- `wait : waiter -\u003e timeout:float -\u003e clock:Eio.Time.clock -\u003e string option option`\n - Wait for promise with timeout\n - Return Some payload on success\n - Return None on timeout\n\n- `cancel : t -\u003e seq:int -\u003e unit`\n - Remove waiter from table\n - Called on timeout to cleanup\n\n## Design constraints\n- Use Eio.Promise for async waiting\n- Use Eio.Time.with_timeout for timeouts\n- Lock-free via Kcas_data.Hashtbl\n- Cleanup on timeout to prevent leaks","acceptance_criteria":"- Acks properly matched to probes\n- Timeouts work correctly\n- No memory leaks on timeout\n- Concurrent completion safe","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-08T18:47:51.390307674+01:00","created_by":"gdiazlo","updated_at":"2026-01-08T19:35:56.984403853+01:00","closed_at":"2026-01-08T19:35:56.984403853+01:00","close_reason":"Implemented pending_acks with Eio.Promise for async waiting and Kcas_data.Hashtbl for lock-free storage","labels":["core","kcas","protocol"],"dependencies":[{"issue_id":"swim-etm","depends_on_id":"swim-oun","type":"blocks","created_at":"2026-01-08T18:47:51.394677184+01:00","created_by":"gdiazlo"},{"issue_id":"swim-etm","depends_on_id":"swim-wdc","type":"parent-child","created_at":"2026-01-08T18:47:57.657173744+01:00","created_by":"gdiazlo"}]} 6{"id":"swim-fac","title":"Implement protocol_pure.ml - Pure SWIM state transitions","description":"Implement pure (no effects) SWIM protocol logic for state transitions.\n\n## Core abstraction\n```ocaml\ntype 'a transition = {\n new_state : 'a;\n broadcasts : protocol_msg list;\n events : node_event list;\n}\n```\n\n## State transition functions\n- `handle_alive : member_state -\u003e alive_msg -\u003e now:float -\u003e member_state transition`\n- `handle_suspect : member_state -\u003e suspect_msg -\u003e now:float -\u003e member_state transition`\n- `handle_dead : member_state -\u003e dead_msg -\u003e now:float -\u003e member_state transition`\n- `handle_ack : probe_state -\u003e ack_msg -\u003e probe_state transition`\n\n## Timeout calculations\n- `suspicion_timeout : config -\u003e node_count:int -\u003e float`\n - Based on suspicion_mult and log(node_count)\n - Capped by suspicion_max_timeout\n\n## Probe target selection\n- `next_probe_target : probe_index:int -\u003e members:node list -\u003e (node * int) option`\n - Round-robin with wraparound\n - Skip self\n\n## Message invalidation (for queue pruning)\n- `invalidates : protocol_msg -\u003e protocol_msg -\u003e bool`\n - Alive invalidates Suspect for same node with \u003e= incarnation\n - Dead invalidates everything for same node\n - Suspect invalidates older Suspect\n\n## State merging\n- `merge_member_state : local:member_state -\u003e remote:member_state -\u003e member_state`\n - CRDT-style merge based on incarnation\n - Dead is final (tombstone)\n - Higher incarnation wins\n\n## Retransmit calculation\n- `retransmit_limit : config -\u003e node_count:int -\u003e int`\n - Based on retransmit_mult * ceil(log(node_count + 1))\n\n## Design constraints\n- PURE functions only - no I/O, no time, no randomness\n- All inputs explicit\n- Exhaustive pattern matching\n- Fully testable with property-based tests","acceptance_criteria":"- All functions are pure (no effects)\n- Property-based tests for SWIM invariants\n- Incarnation ordering correct\n- Suspicion timeout formula matches SWIM paper","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-08T18:46:48.400928801+01:00","created_by":"gdiazlo","updated_at":"2026-01-08T19:29:29.816719466+01:00","closed_at":"2026-01-08T19:29:29.816719466+01:00","close_reason":"Implemented all pure SWIM state transitions: handle_alive, handle_suspect, handle_dead, suspicion_timeout, retransmit_limit, next_probe_target, invalidates, merge_member_state, select_indirect_targets","labels":["core","protocol","pure"],"dependencies":[{"issue_id":"swim-fac","depends_on_id":"swim-td8","type":"blocks","created_at":"2026-01-08T18:46:48.40501031+01:00","created_by":"gdiazlo"},{"issue_id":"swim-fac","depends_on_id":"swim-wdc","type":"parent-child","created_at":"2026-01-08T18:46:52.770706917+01:00","created_by":"gdiazlo"}]} 7{"id":"swim-hc9","title":"Implement crypto.ml - AES-256-GCM encryption","description":"Implement encryption layer using mirage-crypto for AES-256-GCM.\n\n## Constants\n- `nonce_size = 12`\n- `tag_size = 16`\n- `overhead = nonce_size + tag_size` (28 bytes)\n\n## Functions\n\n### Key initialization\n- `init_key : string -\u003e (key, [`Invalid_key_length]) result`\n- Must be exactly 32 bytes for AES-256\n\n### Encryption\n- `encrypt : key -\u003e Cstruct.t -\u003e Cstruct.t`\n- Generate random nonce via mirage-crypto-rng\n- Prepend nonce to ciphertext\n- Result: nonce (12) + ciphertext + tag (16)\n\n### Decryption\n- `decrypt : key -\u003e Cstruct.t -\u003e (Cstruct.t, [`Too_short | `Decryption_failed]) result`\n- Extract nonce from first 12 bytes\n- Verify and decrypt remaining data\n- Return plaintext or error\n\n## Design constraints\n- Use mirage-crypto.Cipher_block.AES.GCM\n- Use mirage-crypto-rng for nonce generation\n- Return Result types, no exceptions\n- Consider in-place decryption where possible","acceptance_criteria":"- Property-based roundtrip tests pass\n- Invalid data properly rejected\n- Key validation works\n- Nonces are unique (use RNG)","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-08T18:46:09.946405585+01:00","created_by":"gdiazlo","updated_at":"2026-01-08T19:24:49.736202746+01:00","closed_at":"2026-01-08T19:24:49.736202746+01:00","close_reason":"Implemented crypto.ml with AES-256-GCM using mirage-crypto. Uses Eio.Flow for secure random nonce generation.","labels":["core","crypto","security"],"dependencies":[{"issue_id":"swim-hc9","depends_on_id":"swim-oun","type":"blocks","created_at":"2026-01-08T18:46:09.950083952+01:00","created_by":"gdiazlo"},{"issue_id":"swim-hc9","depends_on_id":"swim-wdc","type":"parent-child","created_at":"2026-01-08T18:46:14.608204384+01:00","created_by":"gdiazlo"}]} 8{"id":"swim-iwg","title":"Implement dissemination.ml - Broadcast queue with invalidation","description":"Implement the broadcast queue for SWIM protocol message dissemination.\n\n## Broadcast_queue module\n```ocaml\ntype item = {\n msg : protocol_msg;\n transmits : int Kcas.Loc.t;\n created : Mtime.span;\n}\n\ntype t = {\n queue : item Kcas_data.Queue.t;\n depth : int Kcas.Loc.t;\n}\n```\n\n### Functions\n- `create : unit -\u003e t`\n\n- `enqueue : t -\u003e protocol_msg -\u003e transmits:int -\u003e created:Mtime.span -\u003e unit`\n - Add message with initial transmit count\n - Increment depth\n\n- `drain : t -\u003e max_bytes:int -\u003e encode_size:(protocol_msg -\u003e int) -\u003e protocol_msg list`\n - Pop messages up to max_bytes\n - Decrement transmit count\n - Re-enqueue if transmits \u003e 0\n - Return list of messages to piggyback\n\n- `depth : t -\u003e int`\n\n- `invalidate : t -\u003e invalidates:(protocol_msg -\u003e protocol_msg -\u003e bool) -\u003e protocol_msg -\u003e unit`\n - Remove messages invalidated by newer message\n - Uses Protocol_pure.invalidates\n\n## Design constraints\n- Lock-free via Kcas_data.Queue\n- Transmit counting for reliable dissemination\n- Size-aware draining for UDP packet limits\n- Message invalidation to prune stale updates","acceptance_criteria":"- Messages properly disseminated\n- Transmit counts respected\n- Invalidation works correctly\n- No message loss during concurrent access","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-08T18:47:32.926237507+01:00","created_by":"gdiazlo","updated_at":"2026-01-08T19:34:04.973053383+01:00","closed_at":"2026-01-08T19:34:04.973053383+01:00","close_reason":"Implemented broadcast queue with enqueue, drain (size-aware), and invalidate functions using Kcas_data.Queue","labels":["core","dissemination","kcas"],"dependencies":[{"issue_id":"swim-iwg","depends_on_id":"swim-td8","type":"blocks","created_at":"2026-01-08T18:47:32.933998652+01:00","created_by":"gdiazlo"},{"issue_id":"swim-iwg","depends_on_id":"swim-fac","type":"blocks","created_at":"2026-01-08T18:47:32.93580631+01:00","created_by":"gdiazlo"},{"issue_id":"swim-iwg","depends_on_id":"swim-wdc","type":"parent-child","created_at":"2026-01-08T18:47:40.222942145+01:00","created_by":"gdiazlo"}]} 9{"id":"swim-l32","title":"Implement codec tests (test/test_codec.ml)","description":"Property-based and unit tests for codec module.\n\n## Property tests\n\n### Roundtrip\n- `test_codec_roundtrip` - encode then decode equals original\n- `test_encoder_decoder_roundtrip` - for primitive types\n\n### Size calculation\n- `test_encoded_size_accurate` - encoded_size matches actual encoding\n\n### Error handling\n- `test_invalid_magic_rejected`\n- `test_unsupported_version_rejected`\n- `test_truncated_message_rejected`\n- `test_invalid_tag_rejected`\n\n## Unit tests\n\n### Encoder\n- Test write_byte, write_int16_be, etc.\n- Test write_string with various lengths\n- Test buffer overflow detection\n\n### Decoder\n- Test read operations\n- Test remaining/is_empty\n- Test boundary conditions\n\n### Message encoding\n- Test each message type individually\n- Test packet with piggyback messages\n- Test empty piggyback list\n\n## Design constraints\n- Use QCheck for property tests\n- Use Alcotest or similar for unit tests\n- Cover all message types\n- Test error paths","acceptance_criteria":"- All property tests pass\n- All unit tests pass\n- Edge cases covered\n- Error handling tested","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-08T18:49:38.017959466+01:00","created_by":"gdiazlo","updated_at":"2026-01-08T18:49:38.017959466+01:00","labels":["codec","test"],"dependencies":[{"issue_id":"swim-l32","depends_on_id":"swim-l5y","type":"blocks","created_at":"2026-01-08T18:49:38.021527282+01:00","created_by":"gdiazlo"},{"issue_id":"swim-l32","depends_on_id":"swim-294","type":"blocks","created_at":"2026-01-08T18:49:38.02331756+01:00","created_by":"gdiazlo"},{"issue_id":"swim-l32","depends_on_id":"swim-wdc","type":"parent-child","created_at":"2026-01-08T18:49:42.065502393+01:00","created_by":"gdiazlo"}]} 10{"id":"swim-l5y","title":"Implement codec.ml - Zero-copy binary encoding/decoding","description":"Implement binary encoding/decoding with zero-copy semantics using Cstruct.\n\n## Components\n\n### Encoder module\n- `type t` with buf and mutable pos\n- `create : buf:Cstruct.t -\u003e t`\n- `write_byte`, `write_int16_be`, `write_int32_be`, `write_int64_be`\n- `write_string` (length-prefixed)\n- `write_bytes`\n- `to_cstruct` - returns view, no copy\n- `reset`, `remaining`\n\n### Decoder module\n- `type t` with buf and mutable pos\n- `create : Cstruct.t -\u003e t`\n- `read_byte`, `read_int16_be`, `read_int32_be`, `read_int64_be`\n- `read_string` - returns string (must copy for safety)\n- `read_bytes` - returns Cstruct view\n- `remaining`, `is_empty`\n\n### Codec module\n- Magic bytes: \"SWIM\"\n- Version: 1\n- Message tags: 0x01-0x07 for each message type\n- `encode_packet : packet -\u003e buf:Cstruct.t -\u003e (int, [`Buffer_too_small]) result`\n- `decode_packet : Cstruct.t -\u003e packet decode_result`\n- `encoded_size : protocol_msg -\u003e int` for queue draining\n\n### Helper encoders\n- `encode_node`, `encode_node_id`\n- `encode_option`\n- `decode_msg`\n\n## Design constraints\n- No allocations in hot path except unavoidable string creation\n- Return Result types, no exceptions\n- Use Cstruct sub-views where possible","acceptance_criteria":"- Property-based roundtrip tests pass\n- No unnecessary allocations\n- All message types encode/decode correctly\n- Error handling for truncated/invalid data","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-08T18:45:54.407900731+01:00","created_by":"gdiazlo","updated_at":"2026-01-08T19:23:12.726852552+01:00","closed_at":"2026-01-08T19:23:12.726852552+01:00","close_reason":"Implemented codec.ml with Encoder/Decoder modules, zero-copy encoding/decoding for all protocol messages, IP address parsing, and encoded_size calculation","labels":["codec","core","zero-copy"],"dependencies":[{"issue_id":"swim-l5y","depends_on_id":"swim-td8","type":"blocks","created_at":"2026-01-08T18:45:54.412742463+01:00","created_by":"gdiazlo"},{"issue_id":"swim-l5y","depends_on_id":"swim-wdc","type":"parent-child","created_at":"2026-01-08T18:45:59.779010836+01:00","created_by":"gdiazlo"}]} 11{"id":"swim-oll","title":"Implement membership.ml - Kcas-based member table","description":"Implement lock-free membership state management using kcas and kcas_data.\n\n## Member module\n```ocaml\ntype t = {\n node : node_info; (* Immutable *)\n state : member_state Kcas.Loc.t;\n incarnation : incarnation Kcas.Loc.t;\n state_change_time : Mtime.span Kcas.Loc.t;\n last_ack_time : Mtime.span Kcas.Loc.t;\n}\n```\n\n### Functions\n- `create : node_info -\u003e t`\n- `node : t -\u003e node_info` (pure accessor)\n- `get_state`, `get_incarnation`, `get_last_ack` (kcas reads)\n- `set_alive`, `set_suspect`, `set_dead` with `~xt:Kcas.Xt.t`\n- `record_ack : t -\u003e now:Mtime.span -\u003e xt:Kcas.Xt.t -\u003e unit`\n- `snapshot : t -\u003e xt:Kcas.Xt.t -\u003e member_snapshot`\n\n## Membership module\n```ocaml\ntype t = {\n table : (string, Member.t) Kcas_data.Hashtbl.t;\n count : int Kcas.Loc.t;\n}\n```\n\n### Functions\n- `create : unit -\u003e t`\n- `add : t -\u003e Member.t -\u003e unit`\n- `remove : t -\u003e node_id -\u003e unit` (returns bool for success)\n- `find : t -\u003e node_id -\u003e Member.t option`\n- `mem : t -\u003e node_id -\u003e bool`\n- `to_list : t -\u003e Member.t list` (snapshot)\n- `count : t -\u003e int`\n- `update_member : t -\u003e node_id -\u003e (Member.t -\u003e xt:Kcas.Xt.t -\u003e unit) -\u003e bool`\n\n## Design constraints\n- All state via kcas locations\n- Use Kcas_data.Hashtbl for lock-free hashtable\n- Transactional updates via Kcas.Xt.commit\n- No I/O inside transactions\n- Short transactions only","acceptance_criteria":"- Lock-free operations work correctly\n- Concurrent access safe\n- Atomic state transitions\n- Snapshot consistency","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-08T18:47:11.022624275+01:00","created_by":"gdiazlo","updated_at":"2026-01-08T19:33:07.449792483+01:00","closed_at":"2026-01-08T19:33:07.449792483+01:00","close_reason":"Implemented Member module with kcas locations and Membership table with Kcas_data.Hashtbl","labels":["core","kcas","membership"],"dependencies":[{"issue_id":"swim-oll","depends_on_id":"swim-td8","type":"blocks","created_at":"2026-01-08T18:47:11.047048045+01:00","created_by":"gdiazlo"},{"issue_id":"swim-oll","depends_on_id":"swim-wdc","type":"parent-child","created_at":"2026-01-08T18:47:20.00544253+01:00","created_by":"gdiazlo"}]} 12{"id":"swim-oun","title":"Project setup: dune-project, opam, dependencies","description":"Set up the project structure and dependencies for the SWIM library.\n\n## Tasks\n1. Update dune-project with proper metadata and dependencies\n2. Configure swim.opam with all required dependencies:\n - eio (\u003e= 1.0)\n - kcas (\u003e= 0.7)\n - kcas_data (\u003e= 0.7)\n - mirage-crypto\n - mirage-crypto-rng\n - cstruct\n - qcheck (for testing)\n3. Create lib/dune with proper library configuration\n4. Create test/dune for test configuration\n5. Create bench/dune for benchmarks (optional initially)\n6. Verify project builds with `dune build`","acceptance_criteria":"- dune build succeeds\n- opam install . --deps-only works\n- All dependencies available","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-08T18:45:16.711747605+01:00","created_by":"gdiazlo","updated_at":"2026-01-08T19:13:17.972217465+01:00","closed_at":"2026-01-08T19:13:17.972217465+01:00","close_reason":"Project setup complete: dune-project, lib/dune, test/dune configured. Build and tests pass.","labels":["infrastructure","setup"],"dependencies":[{"issue_id":"swim-oun","depends_on_id":"swim-wdc","type":"parent-child","created_at":"2026-01-08T18:45:20.330948173+01:00","created_by":"gdiazlo"}]} 13{"id":"swim-szx","title":"Implement kcas data structure tests (test/test_kcas.ml)","description":"Concurrent correctness tests for kcas-based data structures.\n\n## Buffer_pool tests\n- `test_buffer_pool_no_leaks` - all acquired buffers released\n- `test_buffer_pool_concurrent` - multiple fibers acquiring/releasing\n- `test_with_buffer_exception_safe` - buffer released on exception\n\n## Membership tests\n- `test_membership_concurrent_add_remove` - no lost updates\n- `test_membership_snapshot_consistency` - to_list is consistent\n- `test_membership_count_accurate` - count matches actual\n\n## Broadcast_queue tests\n- `test_broadcast_queue_fifo` - messages dequeued in order\n- `test_broadcast_queue_transmit_counting` - transmits decremented correctly\n- `test_broadcast_queue_invalidation` - old messages pruned\n- `test_broadcast_queue_concurrent` - concurrent enqueue/drain safe\n\n## Pending_acks tests\n- `test_pending_acks_complete` - ack resolves waiter\n- `test_pending_acks_timeout` - timeout returns None\n- `test_pending_acks_cancel` - cancel removes waiter\n- `test_pending_acks_concurrent` - multiple pending acks\n\n## Transactional tests\n- `test_atomic_member_update` - multi-location update is atomic\n- `test_transaction_retry` - conflicting transactions retry\n\n## Design constraints\n- Use Eio for concurrency\n- Test with multiple domains if possible\n- Verify linearizability properties","acceptance_criteria":"- All concurrent tests pass\n- No race conditions\n- Atomicity verified\n- Stress tests pass","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-08T18:50:25.944980162+01:00","created_by":"gdiazlo","updated_at":"2026-01-08T18:50:25.944980162+01:00","labels":["concurrency","kcas","test"],"dependencies":[{"issue_id":"swim-szx","depends_on_id":"swim-xoo","type":"blocks","created_at":"2026-01-08T18:50:25.94903667+01:00","created_by":"gdiazlo"},{"issue_id":"swim-szx","depends_on_id":"swim-oll","type":"blocks","created_at":"2026-01-08T18:50:25.950569487+01:00","created_by":"gdiazlo"},{"issue_id":"swim-szx","depends_on_id":"swim-iwg","type":"blocks","created_at":"2026-01-08T18:50:25.951465481+01:00","created_by":"gdiazlo"},{"issue_id":"swim-szx","depends_on_id":"swim-etm","type":"blocks","created_at":"2026-01-08T18:50:25.952262505+01:00","created_by":"gdiazlo"},{"issue_id":"swim-szx","depends_on_id":"swim-wdc","type":"parent-child","created_at":"2026-01-08T18:50:30.713954321+01:00","created_by":"gdiazlo"}]} 14{"id":"swim-t28","title":"Implement protocol.ml - Main protocol loop and handlers","description":"Implement the effectful protocol runner that applies pure transitions.\n\n## Main Cluster Type\n```ocaml\ntype t = {\n config : config;\n env : env;\n self : node;\n members : Membership.t;\n incarnation : int Kcas.Loc.t;\n sequence : int Kcas.Loc.t;\n broadcast_queue : Broadcast_queue.t;\n pending_acks : Pending_acks.t;\n probe_index : int Kcas.Loc.t;\n send_pool : Buffer_pool.t;\n recv_pool : Buffer_pool.t;\n udp_sock : Eio.Net.datagram_socket;\n tcp_listener : Eio.Net.listening_socket;\n event_stream : node_event Eio.Stream.t;\n handlers : (node -\u003e string -\u003e string -\u003e unit) list Kcas.Loc.t;\n cipher_key : Mirage_crypto.Cipher_block.AES.GCM.key;\n stats : stats Kcas.Loc.t;\n shutdown : bool Kcas.Loc.t;\n}\n```\n\n## Protocol Loop\n- `run_protocol : t -\u003e unit`\n - Main loop: probe cycle, timing, check shutdown\n - Use Protocol_pure for state transitions\n\n- `probe_cycle : t -\u003e Member.t -\u003e unit`\n - Get sequence number\n - Drain piggyback messages\n - Send ping\n - Wait for ack with timeout\n - On timeout: indirect probe\n\n- `indirect_probe : t -\u003e Member.t -\u003e seq:int -\u003e now:Mtime.span -\u003e unit`\n - Select k random members\n - Send ping_req through them\n - Wait for any ack\n\n## Receive Loop\n- `run_udp_receiver : t -\u003e unit`\n - Acquire buffer from pool\n - Receive packet\n - Fork fiber for processing\n - Release buffer after processing\n\n- `handle_udp_packet : t -\u003e buf:Cstruct.t -\u003e addr:Eio.Net.Sockaddr.datagram -\u003e unit`\n - Decrypt\n - Decode\n - Dispatch to handler\n\n## Message Handlers\n- `handle_packet : t -\u003e addr:Eio.Net.Sockaddr.datagram -\u003e packet -\u003e unit`\n- `handle_ping : t -\u003e Ping.t -\u003e unit`\n- `handle_ping_req : t -\u003e Ping_req.t -\u003e unit`\n- `handle_ack : t -\u003e Ack.t -\u003e unit`\n- `handle_alive : t -\u003e Alive.t -\u003e unit`\n- `handle_suspect : t -\u003e Suspect.t -\u003e unit`\n- `handle_dead : t -\u003e Dead.t -\u003e unit`\n- `handle_user_msg : t -\u003e User_msg.t -\u003e unit`\n\n## Design constraints\n- Thin effectful wrapper over Protocol_pure\n- Use kcas for all state\n- Buffer pool for zero-copy I/O\n- Fork fibers for concurrent handling","acceptance_criteria":"- Protocol loop runs correctly\n- Probe cycles at configured interval\n- All message types handled\n- Stats updated accurately","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-08T18:48:36.304687885+01:00","created_by":"gdiazlo","updated_at":"2026-01-08T19:53:04.782054511+01:00","closed_at":"2026-01-08T19:53:04.782054511+01:00","close_reason":"Implemented main protocol loop with all message handlers, probe cycles, indirect probing, UDP receiver, and cluster state management","labels":["core","eio","protocol"],"dependencies":[{"issue_id":"swim-t28","depends_on_id":"swim-td8","type":"blocks","created_at":"2026-01-08T18:48:36.308642743+01:00","created_by":"gdiazlo"},{"issue_id":"swim-t28","depends_on_id":"swim-l5y","type":"blocks","created_at":"2026-01-08T18:48:36.310137809+01:00","created_by":"gdiazlo"},{"issue_id":"swim-t28","depends_on_id":"swim-hc9","type":"blocks","created_at":"2026-01-08T18:48:36.310988083+01:00","created_by":"gdiazlo"},{"issue_id":"swim-t28","depends_on_id":"swim-xoo","type":"blocks","created_at":"2026-01-08T18:48:36.311690387+01:00","created_by":"gdiazlo"},{"issue_id":"swim-t28","depends_on_id":"swim-fac","type":"blocks","created_at":"2026-01-08T18:48:36.3123488+01:00","created_by":"gdiazlo"},{"issue_id":"swim-t28","depends_on_id":"swim-oll","type":"blocks","created_at":"2026-01-08T18:48:36.313012122+01:00","created_by":"gdiazlo"},{"issue_id":"swim-t28","depends_on_id":"swim-iwg","type":"blocks","created_at":"2026-01-08T18:48:36.313695305+01:00","created_by":"gdiazlo"},{"issue_id":"swim-t28","depends_on_id":"swim-etm","type":"blocks","created_at":"2026-01-08T18:48:36.314462189+01:00","created_by":"gdiazlo"},{"issue_id":"swim-t28","depends_on_id":"swim-90e","type":"blocks","created_at":"2026-01-08T18:48:36.315296073+01:00","created_by":"gdiazlo"},{"issue_id":"swim-t28","depends_on_id":"swim-wdc","type":"parent-child","created_at":"2026-01-08T18:48:48.416247923+01:00","created_by":"gdiazlo"}]} 15{"id":"swim-td8","title":"Implement types.ml - Immutable message and node types","description":"Create the core immutable types for the SWIM protocol.\n\n## Types to implement\n\n### Node identification\n- `node_id = Node_id of string [@@unboxed]`\n- `incarnation = Incarnation of int [@@unboxed]`\n\n### Node information\n- `node_info` record with id, addr (Eio.Net.Sockaddr.datagram), meta\n\n### Member state\n- `member_state = Alive | Suspect | Dead`\n- `member_snapshot` record for pure operations\n\n### Protocol messages (pattern-matchable variants)\n- `Ping of { seq; sender }`\n- `Ping_req of { seq; target; sender }`\n- `Ack of { seq; responder; payload }`\n- `Alive of { node; incarnation }`\n- `Suspect of { node; incarnation; suspector }`\n- `Dead of { node; incarnation; declarator }`\n- `User_msg of { topic; payload; origin }`\n\n### Packet structure\n- `packet = { cluster; primary; piggyback }`\n\n### Error types\n- `decode_error` variants\n- `send_error` variants\n\n### Configuration\n- `config` record with all SWIM parameters\n- `default_config` value\n\n### Environment\n- `env` record with Eio dependencies (net, clock, mono_clock, random, sw)\n\n## Design constraints\n- All types immutable\n- Use [@@unboxed] where appropriate for performance\n- Pattern-matchable variants for protocol messages","acceptance_criteria":"- All types defined with proper signatures in types.mli\n- Types compile with dune build\n- No mutable fields except where kcas will manage them","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-08T18:45:34.790084068+01:00","created_by":"gdiazlo","updated_at":"2026-01-08T19:16:46.941262108+01:00","closed_at":"2026-01-08T19:16:46.941262108+01:00","close_reason":"Implemented types.ml and types.mli with all core types: node_id, incarnation, node_info, member_state, protocol_msg, packet, decode_error, send_error, node_event, config, env, stats","labels":["core","types"],"dependencies":[{"issue_id":"swim-td8","depends_on_id":"swim-oun","type":"blocks","created_at":"2026-01-08T18:45:34.794012265+01:00","created_by":"gdiazlo"},{"issue_id":"swim-td8","depends_on_id":"swim-wdc","type":"parent-child","created_at":"2026-01-08T18:45:39.489609655+01:00","created_by":"gdiazlo"}]} 16{"id":"swim-w4y","title":"Implement protocol_pure tests (test/test_pure.ml)","description":"Property-based tests for pure SWIM logic.\n\n## State transition properties\n\n### Incarnation ordering\n- `test_alive_dominates_suspect` - Alive with \u003e= incarnation beats Suspect\n- `test_higher_incarnation_wins` - Higher incarnation always dominates\n- `test_dead_is_final` - Dead state cannot be overridden\n\n### Message invalidation\n- `test_invalidation_transitive` - if A invalidates B and B invalidates C, A invalidates C\n- `test_alive_invalidates_suspect` - for same node with \u003e= incarnation\n- `test_dead_invalidates_all` - Dead invalidates Alive and Suspect for same node\n\n### Merge properties\n- `test_merge_commutative` - merge(a, b) = merge(b, a)\n- `test_merge_idempotent` - merge(a, a) = a\n- `test_merge_respects_incarnation` - higher incarnation wins\n\n### Timeout calculation\n- `test_suspicion_timeout_increases_with_nodes` - more nodes = longer timeout\n- `test_suspicion_timeout_bounded` - never exceeds max\n\n### Probe target selection\n- `test_probe_wraps_around` - index wraps at list end\n- `test_probe_skips_self` - self is never selected\n\n## Unit tests\n- Test specific transition scenarios\n- Test edge cases (empty member list, incarnation 0, etc.)\n\n## Design constraints\n- All tests on pure functions\n- No I/O or effects in tests\n- Comprehensive property coverage","acceptance_criteria":"- All SWIM invariants tested\n- Properties match SWIM paper\n- Edge cases covered\n- All tests pass","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-08T18:50:08.398465616+01:00","created_by":"gdiazlo","updated_at":"2026-01-08T18:50:08.398465616+01:00","labels":["protocol","pure","test"],"dependencies":[{"issue_id":"swim-w4y","depends_on_id":"swim-fac","type":"blocks","created_at":"2026-01-08T18:50:08.402396924+01:00","created_by":"gdiazlo"},{"issue_id":"swim-w4y","depends_on_id":"swim-294","type":"blocks","created_at":"2026-01-08T18:50:08.40380169+01:00","created_by":"gdiazlo"},{"issue_id":"swim-w4y","depends_on_id":"swim-wdc","type":"parent-child","created_at":"2026-01-08T18:50:13.114782761+01:00","created_by":"gdiazlo"}]} 17{"id":"swim-wdc","title":"SWIM Protocol Library Implementation","description":"Production-ready SWIM (Scalable Weakly-consistent Infection-style Process Group Membership) protocol library in OCaml 5 for cluster membership, failure detection, and lightweight pub/sub messaging.\n\n## Core Design Principles\n- Pure functions by default, separate pure logic from effectful operations\n- Immutable data structures, mutations only through kcas\n- Zero-copy buffer management with buffer pools\n- Lock-free coordination via kcas/kcas_data\n- AES-256-GCM encryption\n\n## Dependencies (allowed)\n- eio (\u003e= 1.0), kcas (\u003e= 0.7), kcas_data (\u003e= 0.7)\n- mirage-crypto, mirage-crypto-rng, cstruct\n\n## Target Scale\n- Up to 100 nodes\n- Sub-second failure detection\n- Optimized for datacenter/cloud environments","acceptance_criteria":"- All 11 core modules implemented\n- Property-based tests for pure functions\n- Integration tests passing\n- Build and opam package working\n- Performance targets met (\u003c 5 allocations/probe, \u003e 95% buffer reuse)","status":"open","priority":1,"issue_type":"epic","created_at":"2026-01-08T18:45:08.49485159+01:00","created_by":"gdiazlo","updated_at":"2026-01-08T18:45:08.49485159+01:00","labels":["epic","ocaml5","swim"]} 18{"id":"swim-wwr","title":"Implement integration tests (test/test_integration.ml)","description":"End-to-end integration tests for the SWIM library.\n\n## Two-node tests\n- `test_two_node_join` - node2 joins node1, both see each other\n- `test_two_node_leave` - graceful leave propagates\n- `test_two_node_broadcast` - broadcast message received\n- `test_two_node_direct_send` - direct TCP message delivered\n\n## Three-node tests\n- `test_gossip_propagation` - message reaches all nodes\n- `test_indirect_probe` - indirect probe detects alive node\n- `test_failure_detection` - dead node detected and removed\n\n## Failure scenarios\n- `test_network_partition` - nodes handle partition\n- `test_node_crash` - crashed node detected as dead\n- `test_rejoin_after_crash` - node can rejoin after restart\n\n## Metadata tests\n- `test_metadata_propagation` - metadata updates reach all nodes\n- `test_metadata_update` - updated metadata replaces old\n\n## Event stream tests\n- `test_join_event_fired` - Join event on new member\n- `test_leave_event_fired` - Leave event on departure\n- `test_suspect_event_fired` - Suspect event on probe timeout\n\n## Performance tests\n- `test_convergence_time` - cluster converges within expected time\n- `test_message_throughput` - broadcast rate meets target\n\n## Design constraints\n- Use Eio_main.run for all tests\n- Proper cleanup with shutdown\n- Realistic timing (but accelerated)\n- Isolated network per test","acceptance_criteria":"- All integration tests pass\n- Failure scenarios handled\n- Performance targets met\n- Clean teardown","status":"open","priority":2,"issue_type":"task","created_at":"2026-01-08T18:50:43.333077327+01:00","created_by":"gdiazlo","updated_at":"2026-01-08T18:50:43.333077327+01:00","labels":["integration","test"],"dependencies":[{"issue_id":"swim-wwr","depends_on_id":"swim-zsi","type":"blocks","created_at":"2026-01-08T18:50:43.337480017+01:00","created_by":"gdiazlo"},{"issue_id":"swim-wwr","depends_on_id":"swim-wdc","type":"parent-child","created_at":"2026-01-08T18:50:46.783801496+01:00","created_by":"gdiazlo"}]} 19{"id":"swim-xoo","title":"Implement buffer_pool.ml - Buffer management with kcas_data","description":"Implement buffer pool for zero-copy network I/O.\n\n## Buffer_pool module\n\n### Type\n```ocaml\ntype t = {\n buffers : Cstruct.t Kcas_data.Queue.t;\n size : int;\n total : int;\n semaphore : Eio.Semaphore.t;\n}\n```\n\n### Functions\n- `create : size:int -\u003e count:int -\u003e t`\n - Pre-allocate `count` buffers of `size` bytes\n - Use Kcas_data.Queue for lock-free storage\n - Eio.Semaphore for blocking acquire\n\n- `acquire : t -\u003e Cstruct.t`\n - Block on semaphore if no buffers\n - Pop from queue\n - Reset buffer (memset 0) before returning\n\n- `try_acquire : t -\u003e Cstruct.t option`\n - Non-blocking acquire\n - Return None if no buffers available\n\n- `release : t -\u003e Cstruct.t -\u003e unit`\n - Push buffer back to queue\n - Release semaphore\n\n- `with_buffer : t -\u003e (Cstruct.t -\u003e 'a) -\u003e 'a`\n - RAII-style acquire/release\n - Use Fun.protect for exception safety\n\n- `available : t -\u003e int` - current available count\n- `total : t -\u003e int` - total pool size\n\n## Design constraints\n- Lock-free queue via kcas_data\n- Semaphore for blocking (only blocking allowed per spec)\n- Clear buffer ownership semantics\n- No memory leaks on exceptions","acceptance_criteria":"- Buffers properly recycled\n- No leaks under concurrent use\n- with_buffer is exception-safe\n- Stats accurate","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-08T18:46:28.146790073+01:00","created_by":"gdiazlo","updated_at":"2026-01-08T19:27:29.348943322+01:00","closed_at":"2026-01-08T19:27:29.348943322+01:00","close_reason":"Implemented buffer_pool.ml with Kcas_data.Queue and Eio.Semaphore","labels":["buffer","core","zero-copy"],"dependencies":[{"issue_id":"swim-xoo","depends_on_id":"swim-oun","type":"blocks","created_at":"2026-01-08T18:46:28.151030562+01:00","created_by":"gdiazlo"},{"issue_id":"swim-xoo","depends_on_id":"swim-wdc","type":"parent-child","created_at":"2026-01-08T18:46:32.927877844+01:00","created_by":"gdiazlo"}]} 20{"id":"swim-zsi","title":"Implement swim.ml/swim.mli - Public API assembly","description":"Implement the public API as specified in swim.mli.\n\n## Cluster module (public interface)\n\n### Lifecycle\n- `create : env -\u003e config -\u003e (t, [\u003e `Invalid_key | `Bind_failed of string]) result`\n - Initialize crypto\n - Create buffer pools\n - Bind sockets\n - Start protocol and receiver fibers\n\n- `join : t -\u003e seed_nodes:string list -\u003e (unit, [\u003e `No_seeds_reachable]) result`\n - Parse seed addresses\n - Send join requests\n - Wait for acks\n\n- `leave : t -\u003e ?timeout:float -\u003e unit -\u003e unit`\n - Broadcast leave\n - Wait for propagation\n - Graceful shutdown\n\n- `shutdown : t -\u003e unit`\n - Set shutdown flag\n - Close sockets\n - Release resources\n\n### Membership queries (pure)\n- `local_node : t -\u003e node`\n- `nodes : t -\u003e node list`\n- `node_count : t -\u003e int`\n- `is_alive : t -\u003e node_id -\u003e bool`\n- `find_node : t -\u003e node_id -\u003e node option`\n\n### Events\n- `events : t -\u003e node_event Eio.Stream.t`\n\n### Metadata\n- `set_meta : t -\u003e string -\u003e (unit, [\u003e `Too_large]) result`\n\n### Msg submodule\n- `broadcast : t -\u003e topic:string -\u003e payload:string -\u003e (unit, [\u003e `Too_large]) result`\n- `send : t -\u003e node -\u003e payload:string -\u003e (unit, [\u003e `Unreachable | `Timeout]) result`\n- `on_message : t -\u003e handler -\u003e unit`\n- `on_topic : t -\u003e string -\u003e (node -\u003e string -\u003e unit) -\u003e unit`\n\n### Health submodule\n- `stats : t -\u003e stats`\n- `is_healthy : t -\u003e bool`\n\n## Design constraints\n- All operations fiber-safe\n- No blocking except Eio primitives\n- Result types for fallible operations\n- Clean module signature in .mli","acceptance_criteria":"- Public API matches spec\n- All operations work correctly\n- Clean .mli signature\n- Documentation comments","status":"closed","priority":1,"issue_type":"task","created_at":"2026-01-08T18:49:05.567892446+01:00","created_by":"gdiazlo","updated_at":"2026-01-08T19:54:05.541094079+01:00","closed_at":"2026-01-08T19:54:05.541094079+01:00","close_reason":"Implemented Cluster module as public API with create, start, shutdown, join, broadcast, member queries, and event streaming","labels":["api","core"],"dependencies":[{"issue_id":"swim-zsi","depends_on_id":"swim-t28","type":"blocks","created_at":"2026-01-08T18:49:05.571629003+01:00","created_by":"gdiazlo"},{"issue_id":"swim-zsi","depends_on_id":"swim-wdc","type":"parent-child","created_at":"2026-01-08T18:49:09.915596516+01:00","created_by":"gdiazlo"}]}