opencode-skills-collection 4.0.36 → 4.0.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/README.md +6 -3
  2. package/bundled-skills/.antigravity-install-manifest.json +7 -1
  3. package/bundled-skills/agent-harness-fault-injection/SKILL.md +250 -0
  4. package/bundled-skills/audit-agent-run-evidence/SKILL.md +165 -0
  5. package/bundled-skills/boost-asio-pro/SKILL.md +172 -0
  6. package/bundled-skills/boost-asio-pro/references/build.md +88 -0
  7. package/bundled-skills/boost-asio-pro/references/classic-boost.md +33 -0
  8. package/bundled-skills/boost-asio-pro/references/coroutines.md +415 -0
  9. package/bundled-skills/boost-asio-pro/references/pre-cpp20.md +164 -0
  10. package/bundled-skills/boost-asio-pro/references/ssl.md +38 -0
  11. package/bundled-skills/docs/integrations/jetski-cortex.md +3 -3
  12. package/bundled-skills/docs/integrations/jetski-gemini-loader/README.md +1 -1
  13. package/bundled-skills/docs/maintainers/repo-growth-seo.md +1 -1
  14. package/bundled-skills/docs/maintainers/skills-update-guide.md +1 -1
  15. package/bundled-skills/docs/users/aas-core.md +9 -1
  16. package/bundled-skills/docs/users/bundles.md +1 -1
  17. package/bundled-skills/docs/users/claude-code-skills.md +1 -1
  18. package/bundled-skills/docs/users/gemini-cli-skills.md +1 -1
  19. package/bundled-skills/docs/users/kiro-integration.md +1 -1
  20. package/bundled-skills/docs/users/usage.md +3 -3
  21. package/bundled-skills/docs/users/visual-guide.md +4 -4
  22. package/bundled-skills/multi-source-search/SKILL.md +139 -0
  23. package/bundled-skills/multi-source-search/references/report-schema.md +47 -0
  24. package/bundled-skills/multi-source-search/scripts/validate_report.py +221 -0
  25. package/bundled-skills/review-multi-agent-orchestration/SKILL.md +201 -0
  26. package/bundled-skills/ui-slop-score/SKILL.md +80 -0
  27. package/bundled-skills/youtube-summarizer/SKILL.md +21 -7
  28. package/bundled-skills/youtube-summarizer/scripts/extract-transcript.py +45 -12
  29. package/package.json +3 -2
  30. package/skills_index.json +175 -0
@@ -0,0 +1,88 @@
1
+ # Build Configuration
2
+
3
+
4
+ ### Boost.Asio (header-only since Boost 1.74+)
5
+
6
+ ```cmake
7
+ find_package(Boost REQUIRED)
8
+ find_package(OpenSSL REQUIRED) # if using SSL
9
+ find_package(Threads REQUIRED)
10
+
11
+ target_link_libraries(myapp PRIVATE
12
+ Boost::headers # header-only Asio
13
+ OpenSSL::SSL OpenSSL::Crypto # if using SSL
14
+ Threads::Threads
15
+ )
16
+
17
+ target_compile_features(myapp PRIVATE cxx_std_20)
18
+
19
+ # REQUIRED for GCC coroutine support — build will fail without this
20
+ target_compile_options(myapp PRIVATE
21
+ $<$<CXX_COMPILER_ID:GNU>:-fcoroutines>
22
+ )
23
+
24
+ # Optional: truly header-only (no Boost.System link needed)
25
+ target_compile_definitions(myapp PRIVATE BOOST_ERROR_CODE_HEADER_ONLY)
26
+ ```
27
+
28
+ ### Standalone Asio (always header-only)
29
+
30
+ ```cmake
31
+ # Standalone Asio has no CMake config — use pkg-config or manual path
32
+ find_package(OpenSSL REQUIRED)
33
+ find_package(Threads REQUIRED)
34
+
35
+ # If installed via brew:
36
+ find_path(ASIO_INCLUDE_DIR asio.hpp HINTS /opt/homebrew/include)
37
+
38
+ target_include_directories(myapp PRIVATE ${ASIO_INCLUDE_DIR})
39
+ target_link_libraries(myapp PRIVATE OpenSSL::SSL OpenSSL::Crypto Threads::Threads)
40
+ target_compile_features(myapp PRIVATE cxx_std_20)
41
+ target_compile_definitions(myapp PRIVATE ASIO_STANDALONE)
42
+
43
+ target_compile_options(myapp PRIVATE
44
+ $<$<CXX_COMPILER_ID:GNU>:-fcoroutines>
45
+ )
46
+ ```
47
+
48
+ ### Dual-mode CMake (supports both)
49
+
50
+ ```cmake
51
+ option(USE_STANDALONE_ASIO "Use standalone Asio instead of Boost.Asio" OFF)
52
+
53
+ find_package(OpenSSL REQUIRED)
54
+ find_package(Threads REQUIRED)
55
+
56
+ if(USE_STANDALONE_ASIO)
57
+ find_path(ASIO_INCLUDE_DIR asio.hpp HINTS /opt/homebrew/include)
58
+ target_include_directories(myapp PRIVATE ${ASIO_INCLUDE_DIR})
59
+ target_compile_definitions(myapp PRIVATE USE_STANDALONE_ASIO ASIO_STANDALONE)
60
+ else()
61
+ find_package(Boost REQUIRED)
62
+ target_link_libraries(myapp PRIVATE Boost::headers)
63
+ target_compile_definitions(myapp PRIVATE BOOST_ERROR_CODE_HEADER_ONLY)
64
+ endif()
65
+
66
+ target_link_libraries(myapp PRIVATE OpenSSL::SSL OpenSSL::Crypto Threads::Threads)
67
+ target_compile_features(myapp PRIVATE cxx_std_20)
68
+ target_compile_options(myapp PRIVATE $<$<CXX_COMPILER_ID:GNU>:-fcoroutines>)
69
+ ```
70
+
71
+ ## Header-Only Usage
72
+
73
+ **Boost.Asio:** Asio is header-only by default. The only thing that pulls in a Boost library to link is `boost::system::error_code`'s out-of-line symbols, so for a truly link-free build define **`BOOST_ERROR_CODE_HEADER_ONLY`**. `BOOST_ASIO_HEADER_ONLY` is rarely needed and only relevant if separate compilation was previously enabled; you do **not** normally need both.
74
+
75
+ **Define `BOOST_ERROR_CODE_HEADER_ONLY` in exactly ONE place — prefer CMake** (`target_compile_definitions`, as shown above). Defining it in CMake *and* with a source `#define` triggers `-Wmacro-redefined`. So in source, just include — no `#define`:
76
+ ```cpp
77
+ #include <boost/asio.hpp>
78
+ #include <boost/asio/ssl.hpp>
79
+ #include <boost/asio/experimental/awaitable_operators.hpp>
80
+ ```
81
+
82
+ **Standalone Asio:**
83
+ ```cpp
84
+ #include <asio.hpp>
85
+ #include <asio/ssl.hpp>
86
+ #include <asio/experimental/awaitable_operators.hpp>
87
+ // No macros needed — always header-only
88
+ ```
@@ -0,0 +1,33 @@
1
+ # Classic Boost (pre-1.66, the `io_service` era — verified to 1.62)
2
+
3
+
4
+ To support Boost older than 1.66 (no `io_context`, no `make_strand`, no `any_io_executor`), drop to the classic API — verified building **back to Boost 1.62** (Debian 9) while still compiling on current Boost via a tiny shim:
5
+
6
+ | Modern (1.66+) | Classic (pre-1.66) |
7
+ |----------------|--------------------|
8
+ | `io_context` | `io_service` |
9
+ | `make_strand(ex)` / `strand<any_io_executor>` | `io_service::strand strand(io)` |
10
+ | `bind_executor(strand, h)` | `strand.wrap(h)` |
11
+ | `timer.expires_after(d)` | `timer.expires_from_now(d)` |
12
+ | move-return `async_accept()` | `async_accept(socket_, handler)` |
13
+ | header-only `error_code` | link **Boost.System** (`find_package(Boost COMPONENTS system)`) |
14
+
15
+ Only the `io_service`/`io_context` name and the `expires_after`/`expires_from_now` call actually differ across 1.62…1.90 — isolate both behind `#if BOOST_VERSION >= 106600`:
16
+ ```cpp
17
+ #include <boost/version.hpp>
18
+ #include <boost/asio/steady_timer.hpp> // not pulled in by <boost/asio.hpp> on old Boost
19
+ #if BOOST_VERSION >= 106600
20
+ using io_service_t = boost::asio::io_context;
21
+ #else
22
+ using io_service_t = boost::asio::io_service;
23
+ #endif
24
+ template <class T, class Rep, class Period>
25
+ void timer_expires_in(T& t, std::chrono::duration<Rep,Period> d) {
26
+ #if BOOST_VERSION >= 106600
27
+ t.expires_after(d);
28
+ #else
29
+ t.expires_from_now(d);
30
+ #endif
31
+ }
32
+ ```
33
+ CMake for this range: `cmake_minimum_required(VERSION 3.5)` (Debian 9 ships cmake 3.7), link `Boost::system` only if the component is found (modern Boost is header-only and has no such component), and use the classic out-of-source build (`mkdir build && cd build && cmake ..`) since `-S`/`-B` need cmake ≥ 3.13.
@@ -0,0 +1,415 @@
1
+ # C++20 Coroutine Style (Boost ≥ 1.77)
2
+
3
+ The preferred style when the toolchain allows it. Read `SKILL.md` first — the rules there (write queue, buffer lifetime, version floors) apply here and are not repeated.
4
+
5
+ ## Core Architecture
6
+
7
+ Boost.Asio uses the **Proactor pattern**: async operations run in the background, completion handlers are invoked with results.
8
+
9
+ ```
10
+ Program → I/O Object → Execution Context → OS → (completion) → Handler
11
+ ```
12
+
13
+ **Execution contexts:** `io_context` (single/multi-thread event loop), `thread_pool`, `system_context`
14
+
15
+ **I/O objects:** `tcp::socket`, `tcp::acceptor`, `udp::socket`, `steady_timer`, `ssl::stream<>`
16
+
17
+ **Completion tokens:** Control how async results are delivered — `use_awaitable`, `deferred` (default), `detached`, callbacks, futures.
18
+
19
+ ## C++20 Coroutines (Preferred Style)
20
+
21
+ ```cpp
22
+ #include <boost/asio.hpp>
23
+ #include <boost/asio/co_spawn.hpp>
24
+ #include <boost/asio/use_awaitable.hpp>
25
+
26
+ namespace asio = boost::asio;
27
+ using tcp = asio::ip::tcp;
28
+
29
+ asio::awaitable<void> echo_session(tcp::socket socket) {
30
+ try {
31
+ char data[1024];
32
+ for (;;) {
33
+ std::size_t n = co_await socket.async_read_some(asio::buffer(data));
34
+ co_await async_write(socket, asio::buffer(data, n));
35
+ }
36
+ } catch (std::exception&) {
37
+ // Connection closed or error — coroutine ends
38
+ }
39
+ }
40
+
41
+ asio::awaitable<void> listener(tcp::acceptor acceptor) {
42
+ for (;;) {
43
+ auto socket = co_await acceptor.async_accept();
44
+ co_spawn(acceptor.get_executor(), echo_session(std::move(socket)), asio::detached);
45
+ }
46
+ }
47
+
48
+ int main() {
49
+ asio::io_context io(1); // concurrency_hint=1 for single-threaded
50
+ tcp::acceptor acceptor(io, {tcp::v4(), 8080});
51
+ co_spawn(io, listener(std::move(acceptor)), asio::detached);
52
+ io.run();
53
+ }
54
+ ```
55
+
56
+ **Key rules:**
57
+ - `co_spawn(executor, coroutine, completion_token)` launches a coroutine
58
+ - Without explicit token, async ops use `deferred` (returns awaitable object for `co_await`)
59
+ - Errors become `system_error` exceptions by default inside coroutines
60
+ - Use `asio::detached` when you don't need the coroutine's result
61
+
62
+ ## Error Handling in Coroutines
63
+
64
+ **Default:** Errors throw `boost::system::system_error`.
65
+
66
+ **Explicit error handling with `as_tuple`:**
67
+ ```cpp
68
+ auto [ec, n] = co_await socket.async_read_some(
69
+ asio::buffer(data), asio::as_tuple(asio::use_awaitable));
70
+ if (ec) { /* handle error, no exception */ }
71
+ ```
72
+ **Wrap, don't use bare `as_tuple`.** Always write `as_tuple(use_awaitable)`. Bare `asio::as_tuple` resolves against the operation's *default* completion token (often `deferred`), which compiles in some contexts but fails in others — wrapping an explicit base token is unambiguous everywhere.
73
+
74
+ **With `redirect_error`:**
75
+ ```cpp
76
+ boost::system::error_code ec;
77
+ std::size_t n = co_await socket.async_read_some(
78
+ asio::buffer(data), asio::redirect_error(asio::use_awaitable, ec));
79
+ ```
80
+
81
+ ## Strands (Thread Safety)
82
+
83
+ **Rule: All async operations on a shared object MUST execute on the same strand.**
84
+
85
+ ```cpp
86
+ // Per-connection strand
87
+ asio::strand<asio::io_context::executor_type> strand(io.get_executor());
88
+ co_spawn(strand, session(std::move(socket)), asio::detached);
89
+
90
+ // Bind handler to strand
91
+ socket.async_read_some(asio::buffer(data),
92
+ asio::bind_executor(strand, [](error_code ec, size_t n) { /*...*/ }));
93
+ ```
94
+
95
+ **Implicit strands (no explicit strand needed):**
96
+ - Single-threaded `io_context::run()` — all handlers are sequential
97
+ - Single chain of async ops on one connection (half-duplex)
98
+
99
+ **Explicit strand required when:**
100
+ - Multiple threads call `io_context::run()`
101
+ - Full-duplex read+write on same socket
102
+ - Shared state accessed from multiple async chains
103
+
104
+ ## Full-Duplex: Strand + Write Queue
105
+
106
+ **A strand serializes handler *execution*, NOT whole composed operations.** Two `async_write`s started "concurrently" on the same strand still overlap and **interleave bytes on the wire** — the strand only orders the intermediate handlers, not the byte stream. For full-duplex (a read loop plus pushes/replies writing at the same time on one socket), a strand alone is **not** enough: you must serialize outbound writes yourself with a queue.
107
+
108
+ ```cpp
109
+ // Give each accepted socket its OWN strand, then run every chain (read loop,
110
+ // pushes, replies) on that strand. Passing an executor to async_accept means you
111
+ // must ALSO pass an explicit completion token — the default-deferred shortcut on
112
+ // the zero-arg form no longer applies.
113
+ auto socket = co_await acceptor.async_accept(asio::make_strand(io), asio::use_awaitable);
114
+ std::make_shared<connection>(std::move(socket))->start();
115
+
116
+ class connection : public std::enable_shared_from_this<connection> {
117
+ tcp::socket socket_; // bound to its own strand
118
+ std::deque<std::string> outbox_;
119
+ bool writing_ = false;
120
+ public:
121
+ explicit connection(tcp::socket s) : socket_(std::move(s)) {}
122
+
123
+ void start() {
124
+ // Each chain captures `self` so the connection outlives all its coroutines.
125
+ co_spawn(socket_.get_executor(),
126
+ [self = shared_from_this()] { return self->read_loop(); }, asio::detached);
127
+ }
128
+
129
+ // Call ONLY from the connection's strand (e.g. from its own coroutines).
130
+ // From another thread/strand: asio::dispatch(socket_.get_executor(), ...).
131
+ void send(std::string frame) {
132
+ outbox_.push_back(std::move(frame));
133
+ if (!writing_)
134
+ co_spawn(socket_.get_executor(),
135
+ [self = shared_from_this()] { return self->write_loop(); }, asio::detached);
136
+ }
137
+ private:
138
+ asio::awaitable<void> write_loop() {
139
+ writing_ = true;
140
+ while (!outbox_.empty()) {
141
+ co_await async_write(socket_, asio::buffer(outbox_.front()));
142
+ outbox_.pop_front(); // pop only AFTER the write completes
143
+ }
144
+ writing_ = false;
145
+ }
146
+ asio::awaitable<void> read_loop(); // reads frames, calls send() for replies
147
+ };
148
+ ```
149
+
150
+ **Why each rule matters:**
151
+ - One strand per connection → read loop and write loop never run their handlers concurrently.
152
+ - Write queue + `writing_` flag → at most one `async_write` in flight, so frames never interleave.
153
+ - `enable_shared_from_this` + capturing `self` in every `co_spawn` → the connection survives until all of its read/write/timer chains finish.
154
+ - The accepted socket from `async_accept(make_strand(...))` is `basic_stream_socket<tcp, strand<...>>`, **not** `tcp::socket`. Take it **by value** (`connection(tcp::socket s)`, store `tcp::socket socket_`) — the strand executor type-erases into `any_io_executor` on the move. Passing that accepted socket to a `tcp::socket&` (by reference) instead will **fail to compile** — use `auto` or accept by value.
155
+
156
+ **Strand from inside a coroutine** (when `io` isn't a captured local): get the executor from the coroutine and make a strand off it — no `io_context&` needed:
157
+ ```cpp
158
+ auto ex = co_await asio::this_coro::executor;
159
+ auto socket = co_await acceptor.async_accept(asio::make_strand(ex), asio::use_awaitable);
160
+ ```
161
+
162
+ **Run the read loop and idle watch together** — two `awaitable<void>` branches; don't inspect the result, the first to finish unwinds the other:
163
+ ```cpp
164
+ using namespace asio::experimental::awaitable_operators;
165
+ co_await (read_loop() || idle_watch(socket_, timer_)); // either returning tears down the connection
166
+ ```
167
+
168
+ **Stopping a detached side-coroutine** (e.g. a per-symbol ticker that must end on unsubscribe/close): a detached `co_spawn` won't stop itself. Either (a) have its loop re-check a flag each iteration and `co_return` when gone:
169
+ ```cpp
170
+ while (subscriptions_.contains(symbol) && socket_.is_open()) {
171
+ timer.expires_after(250ms);
172
+ co_await timer.async_wait(asio::as_tuple(asio::use_awaitable));
173
+ if (/* still subscribed */) send(make_tick(symbol));
174
+ }
175
+ ```
176
+ or (b) spawn it with a `cancellation_signal` and `emit()` cancellation on unsubscribe. The flag approach is simpler for per-subscription tickers.
177
+
178
+ ## Timers and Timeouts
179
+
180
+ ```cpp
181
+ asio::awaitable<void> with_timeout(tcp::socket& socket) {
182
+ asio::steady_timer timer(co_await asio::this_coro::executor);
183
+ timer.expires_after(std::chrono::seconds(30));
184
+
185
+ // Race: read vs timeout (requires awaitable_operators)
186
+ using namespace asio::experimental::awaitable_operators;
187
+
188
+ auto result = co_await (
189
+ socket.async_read_some(asio::buffer(data), asio::use_awaitable)
190
+ || timer.async_wait(asio::use_awaitable)
191
+ );
192
+
193
+ if (result.index() == 0) { /* read completed */ }
194
+ else { /* timeout — cancel the socket */ socket.close(); }
195
+ }
196
+ ```
197
+
198
+ **Re-armable idle timeout** (reset on every received frame — the common server pattern):
199
+ ```cpp
200
+ // Run as a long-lived parallel branch. Calling expires_after() again cancels the
201
+ // pending wait, resolving the in-flight async_wait with operation_aborted — that
202
+ // is the signal to keep waiting, NOT an error. Genuine expiry resolves with no error.
203
+ asio::awaitable<void> idle_watch(tcp::socket& sock, asio::steady_timer& timer) {
204
+ for (;;) {
205
+ auto [ec] = co_await timer.async_wait(asio::as_tuple(asio::use_awaitable));
206
+ if (ec == asio::error::operation_aborted) continue; // re-armed → keep waiting
207
+ if (ec) co_return; // timer error
208
+ sock.close(); // real timeout fired
209
+ co_return;
210
+ }
211
+ }
212
+ // On every frame received from the peer: timer.expires_after(30s);
213
+ ```
214
+
215
+ **Parallel operations (`&&` and `||`):**
216
+ ```cpp
217
+ #include <boost/asio/experimental/awaitable_operators.hpp>
218
+ using namespace asio::experimental::awaitable_operators;
219
+
220
+ // Wait for both (AND) — cancels other on failure
221
+ auto [read_n, write_n] = co_await (
222
+ async_read(sock, in_buf, use_awaitable) &&
223
+ async_write(sock, out_buf, use_awaitable)
224
+ );
225
+
226
+ // Wait for first (OR) — cancels other on success
227
+ auto result = co_await (
228
+ async_read(sock, buf, use_awaitable) ||
229
+ timer.async_wait(use_awaitable)
230
+ );
231
+ ```
232
+
233
+ **Note:** `||` and `&&` operators require explicit `use_awaitable` token, and the `awaitable_operators.hpp` header (Boost ≥ 1.77 — see the version floors in SKILL.md).
234
+
235
+ **Void branches:** when a branch returns `void` (e.g. two `awaitable<void>` chains), that arm contributes `std::monostate` to the result variant. If *both* branches are void the result is `variant<monostate, monostate>` — don't inspect `.index()`; just `co_await` the expression and let whichever finishes first unwind the other.
236
+
237
+ ## Cancellation
238
+
239
+ ```cpp
240
+ asio::awaitable<void> cancellable_work() {
241
+ // Check cancellation state
242
+ auto cs = co_await asio::this_coro::cancellation_state;
243
+ if (cs.cancelled() != asio::cancellation_type::none) {
244
+ co_return;
245
+ }
246
+
247
+ // Enable cancellation types
248
+ co_await asio::this_coro::reset_cancellation_state(
249
+ asio::enable_total_cancellation());
250
+ }
251
+ ```
252
+
253
+ ## TCP Server Pattern
254
+
255
+ ```cpp
256
+ asio::awaitable<void> server(asio::io_context& io, unsigned short port) {
257
+ tcp::acceptor acceptor(io, {tcp::v4(), port});
258
+ acceptor.set_option(tcp::acceptor::reuse_address(true));
259
+
260
+ for (;;) {
261
+ auto socket = co_await acceptor.async_accept();
262
+ co_spawn(
263
+ io.get_executor(), // or a strand for multi-threaded
264
+ handle_client(std::move(socket)),
265
+ [](std::exception_ptr ep) {
266
+ if (ep) std::rethrow_exception(ep);
267
+ }
268
+ );
269
+ }
270
+ }
271
+ ```
272
+
273
+ ## Buffers
274
+
275
+ | Type | Use |
276
+ |------|-----|
277
+ | `asio::buffer(data, size)` | Wrap existing memory (no ownership) |
278
+ | `asio::dynamic_buffer(vec)` | Growable buffer over `vector`/`string` |
279
+ | `asio::streambuf` | Legacy stream buffer |
280
+ | `asio::const_buffer` | Read-only view |
281
+ | `asio::mutable_buffer` | Writable view |
282
+
283
+ **Critical:** `asio::buffer()` does NOT own memory. The underlying storage must outlive the async operation.
284
+
285
+ ## Resolver (DNS)
286
+
287
+ ```cpp
288
+ asio::awaitable<void> connect_to(asio::io_context& io,
289
+ std::string host, std::string port) {
290
+ tcp::resolver resolver(io);
291
+ auto endpoints = co_await resolver.async_resolve(host, port);
292
+
293
+ tcp::socket socket(io);
294
+ co_await asio::async_connect(socket, endpoints);
295
+ // socket is now connected
296
+ }
297
+ ```
298
+
299
+ ## Multi-Threaded io_context
300
+
301
+ ```cpp
302
+ asio::io_context io;
303
+ std::vector<std::thread> threads;
304
+
305
+ for (int i = 0; i < std::thread::hardware_concurrency(); ++i) {
306
+ threads.emplace_back([&io] { io.run(); });
307
+ }
308
+
309
+ // All handlers MUST be strand-protected when sharing state
310
+ for (auto& t : threads) t.join();
311
+ ```
312
+
313
+ ## Composed Async Operations (Custom)
314
+
315
+ ```cpp
316
+ template <typename CompletionToken>
317
+ auto async_echo(tcp::socket& socket, CompletionToken&& token) {
318
+ return asio::async_initiate<CompletionToken, void(boost::system::error_code)>(
319
+ asio::co_composed<void(boost::system::error_code)>(
320
+ [](auto state, tcp::socket& socket) -> void {
321
+ state.throw_if_cancelled(true);
322
+ state.reset_cancellation_state(asio::enable_terminal_cancellation());
323
+ try {
324
+ char data[1024];
325
+ for (;;) {
326
+ std::size_t n = co_await socket.async_read_some(asio::buffer(data));
327
+ co_await async_write(socket, asio::buffer(data, n));
328
+ }
329
+ } catch (const boost::system::system_error& e) {
330
+ co_return {e.code()};
331
+ }
332
+ }, socket),
333
+ token, std::ref(socket));
334
+ }
335
+ ```
336
+
337
+ ## Line-Based Protocols
338
+
339
+ For newline-delimited protocols, prefer `async_read_until` over manual `async_read_some` + buffer parsing:
340
+
341
+ ```cpp
342
+ asio::awaitable<void> line_echo(tcp::socket socket) {
343
+ asio::streambuf buf;
344
+ for (;;) {
345
+ std::size_t n = co_await asio::async_read_until(socket, buf, '\n');
346
+ std::string line(asio::buffers_begin(buf.data()),
347
+ asio::buffers_begin(buf.data()) + n);
348
+ buf.consume(n);
349
+ co_await async_write(socket, asio::buffer(line));
350
+ }
351
+ }
352
+ ```
353
+
354
+ Or with `dynamic_buffer` over a `std::string`:
355
+ ```cpp
356
+ std::string buf;
357
+ std::size_t n = co_await asio::async_read_until(socket, asio::dynamic_buffer(buf), '\n');
358
+ std::string line = buf.substr(0, n);
359
+ buf.erase(0, n);
360
+ ```
361
+
362
+ ## Length-Prefixed Binary Framing
363
+
364
+ For binary protocols, read the fixed-size header fully, then the body fully — two sequential **composed** reads (`async_read` fills the whole buffer, handling short reads). Do NOT use `async_read_some` for framing.
365
+
366
+ ```cpp
367
+ // Frame: [4-byte big-endian length N][N-byte body]
368
+ asio::awaitable<std::string> read_frame(tcp::socket& sock) {
369
+ constexpr uint32_t max_frame_size = 16 * 1024 * 1024;
370
+ uint32_t len_be = 0;
371
+ co_await async_read(sock, asio::buffer(&len_be, sizeof len_be)); // exactly 4 bytes
372
+ uint32_t n = ntohl(len_be); // <arpa/inet.h>; or hand-roll endian swap
373
+ if (n > max_frame_size) {
374
+ throw std::length_error("frame exceeds 16 MiB limit"); // <stdexcept>
375
+ }
376
+ std::string body(n, '\0');
377
+ co_await async_read(sock, asio::buffer(body)); // exactly n bytes
378
+ co_return body;
379
+ }
380
+
381
+ asio::awaitable<void> write_frame(tcp::socket& sock, std::string_view body) {
382
+ uint32_t len_be = htonl(static_cast<uint32_t>(body.size()));
383
+ std::array<asio::const_buffer, 2> bufs{
384
+ asio::buffer(&len_be, sizeof len_be), asio::buffer(body)};
385
+ co_await async_write(sock, bufs); // gather-write header + body atomically
386
+ // len_be and body must outlive the write — they do here (co_await suspends in-frame).
387
+ }
388
+ ```
389
+
390
+ ## Graceful Shutdown (signal_set)
391
+
392
+ ```cpp
393
+ asio::signal_set signals(io, SIGINT, SIGTERM);
394
+ signals.async_wait([&](const boost::system::error_code&, int /*signo*/) {
395
+ acceptor.close(); // stop accepting; let in-flight sessions drain, then io.run() returns
396
+ // or, for an immediate stop: io.stop();
397
+ });
398
+ ```
399
+
400
+ For coroutine-style shutdown, `co_await signals.async_wait()` in a dedicated coroutine instead of a callback.
401
+
402
+ ## Quick Reference
403
+
404
+ | Operation | Function |
405
+ |-----------|----------|
406
+ | Launch coroutine | `co_spawn(executor, coro, token)` |
407
+ | Accept connection | `co_await acceptor.async_accept()` |
408
+ | Read some bytes | `co_await socket.async_read_some(buffer)` |
409
+ | Read exact/until | `co_await async_read(stream, buf)` / `async_read_until(stream, buf, delim)` |
410
+ | Write all | `co_await async_write(stream, buffer)` |
411
+ | Connect | `co_await async_connect(socket, endpoints)` |
412
+ | Resolve DNS | `co_await resolver.async_resolve(host, port)` |
413
+ | Wait timer | `co_await timer.async_wait()` |
414
+ | TLS handshake | `co_await stream.async_handshake(type)` |
415
+ | Get executor | `co_await asio::this_coro::executor` |