From 0a267b77fc411fc46f3e85299ed9b42a49d67a3a Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Wed, 16 Sep 2026 11:17:49 +0700 Subject: [PATCH 01/14] release: bump CMake version to 1.0.0 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f8d966b..aa8041d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.21) project( cpp_request - VERSION 0.9.0 + VERSION 1.0.0 DESCRIPTION "A lightweight synchronous C++17 HTTP/1.1 client built on native sockets" LANGUAGES CXX ) From c764cb98286fdf5048c54e038dcc454a2c948906 Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Wed, 16 Sep 2026 11:17:59 +0700 Subject: [PATCH 02/14] release: bump manifest version to 1.0.0 --- vcpkg.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vcpkg.json b/vcpkg.json index 0dd2e9a..56654fa 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,6 +1,6 @@ { "name": "cpp-request", - "version": "0.9.0", + "version": "1.0.0", "description": "A lightweight synchronous C++17 HTTP/1.1 client built on native sockets.", "builtin-baseline": "a1cae005c39be7b18ba319fced856b68d7276271", "dependencies": [ From d70187dff61a171742d335c6c2bb5040c12a9e56 Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Wed, 16 Sep 2026 11:19:15 +0700 Subject: [PATCH 03/14] docs: freeze v1.0 public API contract --- docs/api/public-api.md | 42 ++++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/docs/api/public-api.md b/docs/api/public-api.md index 96250b6..0b15502 100644 --- a/docs/api/public-api.md +++ b/docs/api/public-api.md @@ -4,15 +4,15 @@ - Project: `cpp_request` - Target release: MVP v1.0 -- Status: Proposed API baseline +- Status: Frozen v1.0 contract - Language baseline: C++17 - Namespace: `cpp_request` -This document defines the intended public API shape for v1.0. Internal implementation details remain free to change as long as the documented behavior is preserved. +This document defines the frozen public API contract for v1.0. Internal implementation details remain free to change as long as the documented behavior and source-level compatibility are preserved. ## Design Principles -The public API should be: +The public API is designed to be: 1. small, 2. explicit, @@ -38,7 +38,7 @@ The v1 public surface consists primarily of: - `Error` - `Result` -Supporting configuration types may be introduced when they materially improve clarity, but the v1 API should avoid unnecessary wrappers. +The v1 API intentionally avoids unnecessary wrappers beyond the configuration types required by the stable surface. ```mermaid classDiagram @@ -75,7 +75,7 @@ Responsibilities: - execute sequential HTTP requests, - expose convenience member functions for common methods. -Conceptual interface: +Public interface shape: ```cpp namespace cpp_request { @@ -114,7 +114,7 @@ public: } // namespace cpp_request ``` -Exact overload count may change before implementation, but the behavioral contract above is the v1 target. +The declarations above describe the v1.0 public surface. Post-v1 additions must preserve the compatibility expectations of the stable v1 line. ### Thread safety @@ -159,7 +159,7 @@ Zero is a real limit rather than an unlimited sentinel. Full enforcement details `Request` represents a complete logical HTTP request description before execution. -Conceptual interface: +Public interface shape: ```cpp namespace cpp_request { @@ -200,7 +200,7 @@ public: ### Borrowed and owned request data -The base URL and body may remain non-owning views. Their lifetime requirements are defined in `lifetime.md`. +The base URL and body are non-owning views. Their lifetime requirements are defined in `lifetime.md`. Headers and query parameters added through the request object own their copied text independently from the caller's source buffers. @@ -231,7 +231,7 @@ The raw URL parser does not silently repair malformed percent escapes. A raw pat The response owns its body because v1 only exposes completed in-memory responses. -Conceptual interface: +Public interface shape: ```cpp namespace cpp_request { @@ -250,7 +250,7 @@ public: } // namespace cpp_request ``` -The exact body accessor names are not frozen by this document, but the following behavior is: +The v1.0 accessors above are the stable response surface. The following behavior is guaranteed: - completed response body is owned by `Response`, - callers can access it without copying, @@ -270,7 +270,7 @@ Required behavior: - efficient iteration, - no requirement to canonicalize original field-name casing. -Conceptual API: +Public API: ```cpp namespace cpp_request { @@ -293,7 +293,7 @@ For v1: - `get(name)` returns the first matching field value, - absence is represented by an empty view, -- APIs for enumerating all duplicate values may be added if required by implementation/tests without breaking this base contract. +- ordered duplicate fields remain available through iteration. --- @@ -311,7 +311,7 @@ Required observable components: - query, - request target. -Conceptual interface: +Public interface shape: ```cpp namespace cpp_request { @@ -349,7 +349,7 @@ Required semantic states: - success with `T`, - failure with `Error`. -Conceptual interface: +Public interface shape: ```cpp namespace cpp_request { @@ -379,11 +379,11 @@ The internal representation is intentionally not frozen here. `Error` is a library-owned structured error value. -The exact taxonomy is intentionally deferred to the dedicated error-model document. +The stable v1 taxonomy is defined by the dedicated error-model document and `error.hpp`. The public contract requires that callers can distinguish major failure classes without parsing diagnostic strings. -Expected categories include: +Categories include: - invalid URL, - unsupported scheme, @@ -400,9 +400,7 @@ Expected categories include: ## Free Convenience Functions -The library should expose stateless convenience helpers for simple one-shot requests. - -Conceptually: +The library exposes stateless convenience helpers for simple one-shot requests. ```cpp namespace cpp_request { @@ -417,7 +415,7 @@ Result del(std::string_view url); } // namespace cpp_request ``` -These helpers may internally create a temporary `Client` and therefore do not promise connection reuse across separate calls. +These helpers internally use a temporary `Client` and therefore do not promise connection reuse across separate calls. --- @@ -455,13 +453,13 @@ For v1 public API: - avoid platform-specific terminology, - avoid exposing internal parser/transport types. -`delete` is a C++ keyword, so the convenience member/free function uses `del()` unless a better non-keyword name is selected before freeze. +`delete` is a C++ keyword, so the stable v1 convenience member/free function is named `del()`. --- ## Explicit v1 API Non-Goals -The public API will not include dedicated abstractions for: +The public API does not include dedicated abstractions for: - TLS configuration, - async handles/futures, From 2a71481ece155d0ce04c69fe8d3816d8a6323839 Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Wed, 16 Sep 2026 11:19:34 +0700 Subject: [PATCH 04/14] docs: freeze v1.0 lifetime contract --- docs/api/lifetime.md | 55 ++++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 33 deletions(-) diff --git a/docs/api/lifetime.md b/docs/api/lifetime.md index deea028..83f264b 100644 --- a/docs/api/lifetime.md +++ b/docs/api/lifetime.md @@ -4,10 +4,10 @@ - Project: `cpp_request` - Target release: MVP v1.0 -- Status: Proposed API lifetime baseline +- Status: Frozen v1.0 contract - Language baseline: C++17 -This document defines ownership and lifetime rules for public API data, especially where `std::string_view` is used. +This document defines the stable ownership and lifetime rules for public API data, especially where `std::string_view` is used. --- @@ -45,14 +45,9 @@ cpp_request::Request req{ }; ``` -If `Request` stores the supplied view directly, the temporary string is destroyed immediately and the view dangles. +`Request` stores the supplied view directly, so a temporary string would be destroyed immediately and leave the view dangling. -Therefore, either: - -- callers keep borrowed storage alive, or -- implementation/API overloads explicitly copy when ownership is requested. - -The default v1 model should prefer borrowing rather than implicit allocation. +Callers must keep borrowed URL storage alive while the `Request` may read it. ### Request body @@ -62,9 +57,7 @@ Because v1 request execution is synchronous, the library does not retain the bod ### Request headers -Header storage strategy may be owned internally by `Headers` to avoid fragile user-side lifetime requirements for individually inserted fields. - -The public contract should prefer safety here: after `Headers::add()` or `Headers::set()` returns successfully, the caller should not be required to keep the source header strings alive. +`Headers` owns inserted header names and values. After `Headers::add()` or `Headers::set()` returns successfully, the caller does not need to keep the source header strings alive. This allows temporary values to be used safely when constructing headers while keeping `std::string_view` primarily as an input parameter optimization. @@ -72,7 +65,7 @@ This allows temporary values to be used safely when constructing headers while k ## Response-Side Lifetime -`Response` owns its completed response body and any storage required to expose stable response metadata. +`Response` owns its completed response body and the storage required to expose stable response metadata. A `std::string_view` returned from a `Response` accessor remains valid only while: @@ -97,9 +90,7 @@ Views into a destroyed response are invalid. ## `Url` Lifetime -A parsed `Url` should expose stable component views for the lifetime of the `Url` object. - -Therefore, if parsing requires normalized or reconstructed storage, `Url` should own that storage internally rather than exposing views into temporary parser buffers. +A parsed `Url` owns normalized URL storage internally and exposes stable component views for the lifetime of the `Url` object. The public contract is: @@ -114,13 +105,13 @@ The public contract is: Views returned from header lookup remain valid until the corresponding `Headers` object is mutated in a way that can invalidate internal storage or until it is destroyed. -The implementation should document iterator/reference invalidation rules once the final container representation is selected. +Iteration/reference invalidation follows the owning container's mutation behavior and should be treated conservatively by callers. --- ## Move Semantics -Resource-owning or storage-owning public types should support efficient move semantics where appropriate. +Resource-owning or storage-owning public types support efficient move semantics where appropriate. After moving from an object: @@ -137,21 +128,19 @@ This keeps the contract simple and avoids binding the API to container-specific ## Copy Semantics -Value-oriented types that own only ordinary data may be copyable where useful. - -Native socket ownership must never be duplicated by copy. +Value-oriented types that own ordinary data are copyable where their declarations allow it. -`Client` copyability is not guaranteed in v1 because it may own reusable transport state. +Native socket ownership is never duplicated by copy. -The expected safe default is: +The stable v1 rules are: -- `Client`: non-copyable, movable if practical, -- internal socket owners: non-copyable, movable, -- `Response`: movable and copyability may be implementation-dependent, -- `Headers`: regular owning value type where practical, -- `Url`: regular owning value type where practical. +- `Client`: non-copyable and movable, +- internal socket owners: non-copyable and movable, +- `Response`: ordinary owning value type according to its generated special members, +- `Headers`: owning value type, +- `Url`: owning value type. -Exact special-member declarations are finalized during implementation/public-header review. +The declarations in the installed public headers are authoritative for exact special-member availability. --- @@ -174,13 +163,13 @@ sequenceDiagram Note over App,Req: Borrowed input no longer needed by execution ``` -The library must not retain request-side borrowed views for asynchronous work after the synchronous call returns. +The library does not retain request-side borrowed views for asynchronous work after the synchronous call returns. --- ## Invalid Lifetime Patterns -The following are explicitly unsafe unless an owning overload copies the data: +The following are explicitly unsafe: - constructing a stored `Request` view from a temporary `std::string`, - storing a response-derived view after destroying the `Response`, @@ -191,10 +180,10 @@ The following are explicitly unsafe unless an owning overload copies the data: ## Design Rationale -The v1 lifetime model intentionally balances performance and development complexity: +The v1 lifetime model intentionally balances performance and implementation complexity: - `std::string_view` avoids unnecessary copies for request inputs, - synchronous execution bounds how long borrowed request data is needed, - response data remains owned for safe user access, -- ownership-heavy structures such as headers can copy at construction time where lifetime safety is more valuable than micro-optimizing tiny strings, +- ownership-heavy structures such as headers copy at construction time where lifetime safety is more valuable than micro-optimizing tiny strings, - no custom string-view implementation is required because C++17 is the project baseline. From 340d4a0026545637af33e04c766fb1ead4cbd822 Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Wed, 16 Sep 2026 11:19:58 +0700 Subject: [PATCH 05/14] docs: freeze v1.0 error contract --- docs/api/error-model.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/api/error-model.md b/docs/api/error-model.md index 1a8757d..0280af6 100644 --- a/docs/api/error-model.md +++ b/docs/api/error-model.md @@ -4,12 +4,12 @@ - Project: `cpp_request` - Target release: MVP v1.0 -- Status: Proposed v1 contract +- Status: Frozen v1.0 contract - Language baseline: C++17 ## Goals -The v1 error model is designed to be: +The v1 error model is: 1. **Structured** — callers branch on stable library-owned codes, not diagnostic strings. 2. **Non-exception-based** — expected URL, DNS, socket, timeout, and protocol failures are returned through `Result`. @@ -19,7 +19,7 @@ The v1 error model is designed to be: ## Public Error Shape -The public contract should conceptually expose: +The stable public contract is: ```cpp enum class ErrorCode { @@ -60,7 +60,7 @@ struct Error { }; ``` -The exact physical representation may change during implementation, but the semantic contract above is frozen for v1 unless implementation proves a concrete correctness issue. +The physical representation may evolve internally, but the semantic contract above is frozen for the v1 stable line unless a correctness fix requires a compatible change. ## `native_code` @@ -72,7 +72,7 @@ Rules: - POSIX errors may preserve `errno`. - Windows errors may preserve the result of `WSAGetLastError()` or an equivalent native code. - callers must not use `native_code` as the primary portable error contract. -- library behavior must branch on `ErrorCode`, not platform-native numbers. +- library behavior branches on `ErrorCode`, not platform-native numbers. ## Error Categories @@ -124,7 +124,7 @@ Rules: ### Fallback -`Unknown` is reserved for failures that cannot yet be classified safely. New implementation paths should prefer a specific stable code whenever practical. +`Unknown` is reserved for failures that cannot be classified safely. New implementation paths should prefer a specific stable code whenever practical. ## HTTP Status Codes Are Not Library Errors @@ -138,7 +138,7 @@ flowchart LR D --> E[Response.status_code may be 2xx, 4xx, 5xx, etc.] ``` -The library must not convert HTTP status codes into `ErrorCode` values automatically. +The library does not convert HTTP status codes into `ErrorCode` values automatically. This separation allows callers to distinguish: @@ -158,8 +158,8 @@ flowchart TD Rules: - platform-specific numeric values never replace `ErrorCode`. -- lower-layer failures should not be reclassified unless the upper layer has additional semantic information. -- errors must preserve the most specific meaningful portable classification available. +- lower-layer failures are not reclassified unless the upper layer has additional semantic information. +- errors preserve the most specific meaningful portable classification available. ## Timeout Classification @@ -169,7 +169,7 @@ Timeouts remain distinct by operation: - `ReadTimeout` - `WriteTimeout` -They must not collapse into one generic timeout code in v1 because callers may need different recovery/logging behavior for each stage. +They do not collapse into one generic timeout code in v1 because callers may need different recovery/logging behavior for each stage. ## EOF Semantics @@ -195,7 +195,7 @@ Exceeding a configured limit: ## Diagnostic Message Function -The library may expose a lightweight function such as: +The library exposes a lightweight function: ```cpp std::string_view error_message(ErrorCode code) noexcept; @@ -210,6 +210,6 @@ Requirements: ## Exception Policy -Expected runtime failures represented by this document must not require exceptions. +Expected runtime failures represented by this document do not require exceptions. -The v1 contract does not require a global `noexcept` guarantee for every public function, because standard-library allocation may still fail. However, network/protocol error reporting itself must use `Result` rather than throwing library-specific exceptions. +The v1 contract does not provide a global `noexcept` guarantee for every public function, because standard-library allocation may still fail. Network/protocol error reporting itself uses `Result` rather than library-specific exceptions. From 41fab5a75675cde5101262fd9e832b262028a57c Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Wed, 16 Sep 2026 11:20:18 +0700 Subject: [PATCH 06/14] docs: freeze v1.0 result contract --- docs/api/result.md | 76 ++++++++++++++++------------------------------ 1 file changed, 27 insertions(+), 49 deletions(-) diff --git a/docs/api/result.md b/docs/api/result.md index f500cfc..901a06d 100644 --- a/docs/api/result.md +++ b/docs/api/result.md @@ -4,7 +4,7 @@ - Project: `cpp_request` - Target release: MVP v1.0 -- Status: Proposed v1 contract +- Status: Frozen v1.0 contract - Language baseline: C++17 ## Purpose @@ -24,11 +24,11 @@ stateDiagram-v2 Failure --> [*] ``` -A `Result` must never represent both states simultaneously and must not require exception handling for expected network or protocol failures. +A `Result` never represents both states simultaneously and does not require exception handling for expected network or protocol failures. -## Conceptual Public Interface +## Stable Public Interface -The public contract should support semantics equivalent to: +The stable v1 semantics are equivalent to: ```cpp template @@ -40,18 +40,18 @@ public: bool has_value() const noexcept; explicit operator bool() const noexcept; - T& value() &; - const T& value() const &; - T&& value() &&; + T& value() & noexcept; + const T& value() const & noexcept; + T&& value() && noexcept; - Error& error() &; - const Error& error() const &; + Error& error() & noexcept; + const Error& error() const & noexcept; }; ``` -Construction details may differ in implementation, but the success/failure semantics are frozen. +Construction details are implementation-specific, but the success/failure semantics above are frozen for v1.0. -For normal `T != Error` usage, convenient direct construction from a success value or from `Error` may be provided. The explicit factories remain available when the desired state should be unambiguous. +For normal `T != Error` usage, direct construction from a success value or from `Error` is available. Explicit factories remain available when the desired state should be unambiguous. ## Explicit Factories @@ -74,7 +74,7 @@ This keeps both states representable without introducing `ErrorCode::None` or an ## `[[nodiscard]]` -`Result` should be declared `[[nodiscard]]` so silently ignoring a potentially failed network operation produces a compiler diagnostic where supported. +`Result` is declared `[[nodiscard]]` so silently ignoring a potentially failed network operation produces a compiler diagnostic where supported. Example: @@ -85,7 +85,7 @@ client.get("http://example.com"); // should warn when result is discarded ## State Inspection -Two equivalent checks are permitted: +Two equivalent checks are supported: ```cpp if (result.has_value()) { @@ -97,17 +97,15 @@ if (result) { } ``` -`operator bool()` must be `explicit` to avoid unintended arithmetic or implicit conversions. +`operator bool()` is `explicit` to avoid unintended implicit conversions. ## Value Access `value()` is valid only when `has_value() == true`. -The v1 contract intentionally does not require `value()` to throw when called in the failure state. +Calling the wrong-state accessor is a programmer error and violates a documented precondition. -Calling the wrong-state accessor is a programmer error and has a documented precondition. - -Implementations must not rely on a hidden `std::bad_variant_access` path while simultaneously declaring the accessor `noexcept`. A violated accessor precondition is outside normal runtime error handling and must not be modeled as a network/protocol failure. +The implementation does not rely on a hidden `std::bad_variant_access` path while declaring the accessor `noexcept`. A violated accessor precondition is outside normal runtime error handling and is not modeled as a network/protocol failure. Recommended usage: @@ -139,50 +137,32 @@ Therefore: - moving a result transfers its active state according to `T` / `Error` move semantics, - copying is available only when the contained type permits it. -Move-only payloads must remain usable as successful result values. +Move-only payloads remain usable as successful result values. ## Storage Representation -The exact physical storage is intentionally not part of the public API contract. - -Acceptable implementation approaches may include: - -- `std::variant`, -- manually managed discriminated storage, -- another zero-extra-allocation representation. +The physical storage is intentionally not part of the public API contract. -The implementation must be benchmarked/inspected before choosing a more complex custom representation solely for performance. +The v1 implementation uses a discriminated value/error representation without a separate heap allocation solely for the result state. Internal representation may change compatibly in later releases. -C++17 makes `std::variant` a valid baseline candidate and avoids unnecessary custom lifetime machinery during initial development. - -If the physical alternatives have identical types, state inspection and access must use the result discriminator rather than type-based lookup. +If the physical alternatives have identical types, state inspection and access use the result discriminator rather than type-based lookup. ## Allocation Policy -`Result` itself must not require a separate heap allocation solely to store its success/error discriminator. +`Result` itself does not require a separate heap allocation solely to store its success/error discriminator. Any allocation performed by `T` remains a property of `T`, not of the result abstraction. ## `Result` -Operations that can fail but do not naturally return a value may use a `Result` specialization or an equivalent project-owned success type. +`Result` is not part of the v1.0 public surface. The primary template rejects `void` explicitly. -Conceptually: - -```cpp -Result operation(); -``` - -The specialization must preserve the same state-inspection and `error()` semantics. - -Exact implementation remains deferred until a concrete internal/public operation needs it. The primary template may reject `void` explicitly in the meantime. +A future specialization may be introduced if a concrete API needs it, provided that addition preserves the stable v1 contract. ## No `ErrorCode::None` Requirement Success is represented by the `Result` state itself, not by an `ErrorCode::None` sentinel. -This keeps the model explicit: - ```mermaid flowchart LR R[Result] -->|success| V[T] @@ -193,9 +173,7 @@ There is no valid state where a failure contains a "no error" code. ## Error Propagation -Internal functions should propagate failures without converting them to text and reparsing them later. - -Example conceptual flow: +Internal functions propagate failures without converting them to text and reparsing them later. ```mermaid sequenceDiagram @@ -217,7 +195,7 @@ sequenceDiagram `Result` is responsible for expected operation failures, not catastrophic runtime conditions such as allocation failure. -The contract therefore distinguishes: +The contract distinguishes: - expected network/protocol failures → `Result` failure, - programmer contract violations → accessor precondition violation, @@ -225,11 +203,11 @@ The contract therefore distinguishes: ## Non-Goals -For v1, `Result` does not need to provide a large functional-combinator API such as: +For v1, `Result` does not provide a large functional-combinator API such as: - `and_then` - `transform` - `or_else` - monadic pipelines -These may be added later if real usage demonstrates value. The initial API should stay small and focused. +These may be added later if real usage demonstrates value. The v1 API stays small and focused. From 4c1fc5f41183941a0998fe58a160f5f6d1fc6652 Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Wed, 16 Sep 2026 11:20:45 +0700 Subject: [PATCH 07/14] docs: mark getting started as stable v1.0 --- docs/getting-started.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/getting-started.md b/docs/getting-started.md index 887459e..1549984 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1,6 +1,6 @@ # Getting started -This guide covers the public v0.9 API that is intended to become the v1 baseline. +This guide covers the stable cpp_request v1.0 public API. ## Requirements From dadfd3660d0f4a9d207e56c206eba17dca65252f Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Wed, 16 Sep 2026 11:21:10 +0700 Subject: [PATCH 08/14] docs: mark v1.0 promotion complete --- docs/roadmap.md | 106 ++++++++++++++++++------------------------------ 1 file changed, 39 insertions(+), 67 deletions(-) diff --git a/docs/roadmap.md b/docs/roadmap.md index 86d5a97..6a9ad0c 100644 --- a/docs/roadmap.md +++ b/docs/roadmap.md @@ -3,16 +3,14 @@ ## Status - Project: `cpp_request` -- Target release: MVP v1.0 -- Current development version: v0.9.0 +- Target release: v1.0.0 +- Current project version: v1.0.0 - Language baseline: C++17 - Protocol scope: synchronous HTTP/1.1 over plaintext TCP -- Current milestone: v1.0 promotion pending +- Current milestone: v1.0 implementation and acceptance complete; release tag pending - Last roadmap review: 2026-09-16 -The frozen functional and non-functional requirements remain authoritative for **what** v1 must provide. This roadmap records implementation milestones and the remaining release work. - -Detailed acceptance evidence is maintained in [`docs/release/v1.0-acceptance.md`](release/v1.0-acceptance.md), with the manual review in [`docs/release/v1.0-manual-review.md`](release/v1.0-manual-review.md). +The frozen functional and non-functional requirements remain authoritative for **what** v1 provides. Detailed acceptance evidence is maintained in [`docs/release/v1.0-acceptance.md`](release/v1.0-acceptance.md), with the manual review in [`docs/release/v1.0-manual-review.md`](release/v1.0-manual-review.md). --- @@ -29,7 +27,7 @@ Detailed acceptance evidence is maintained in [`docs/release/v1.0-acceptance.md` | v0.7 | Redirect handling | ✅ Complete | | v0.8 | Protocol/API correctness hardening | ✅ Complete | | v0.9 | Packaging, benchmarks, examples, documentation | ✅ Complete | -| v1.0 | Release hardening and acceptance gate | ✅ Acceptance complete; promotion pending | +| v1.0 | Release hardening, acceptance, stable API promotion | ✅ Complete | --- @@ -89,68 +87,24 @@ Installable CMake package, stable `cpp_request::cpp_request` target, install-tre References: PR #26–#29. ---- - -# v1.0 — Release Hardening and Acceptance Gate ✅ - -Goal: verify the frozen requirements as one coherent release candidate without adding new feature scope. - -## Automated verification - -Completed and green on the release-hardening candidate: - -- Windows Debug/Release CI, -- Linux Debug/Release CI, -- macOS Debug/Release CI, -- C++17 build/test coverage, -- C++20 compatibility coverage, -- ASan + UBSan test execution, -- warnings-as-errors release-gate builds, -- unit and loopback integration tests, -- install-tree consumer test, -- C++20 installed-consumer test, -- benchmark smoke jobs, -- examples compilation, -- public-header isolation without `src/` includes. - -Release-hardening implementation reference: PR #30. - -## Manual acceptance +### v1.0 — Release hardening and stable promotion -Completed on 2026-09-16. The review covered: +Completed work includes: -- HTTP message framing and connection reuse, -- public API/lifetime/error contracts, -- exported dependency boundaries, -- benchmark/performance-claim discipline, -- frozen-scope compliance, -- release/version/tag metadata. +- Windows/Linux/macOS Debug and Release CI, +- C++17 baseline and C++20 compatibility coverage, +- ASan + UBSan release-gate execution, +- warnings-as-errors hardening builds, +- unit and local-loopback integration tests, +- install-tree and C++20 consumer verification, +- benchmark smoke execution, +- public-header isolation, +- HTTP framing/connection-reuse manual review, +- public API/lifetime/error/result contract freeze, +- synchronized CMake and manifest version metadata at `1.0.0`, +- final v1.0 release notes. -No code/API feature blocker remains. See [`docs/release/v1.0-manual-review.md`](release/v1.0-manual-review.md). - -## Remaining promotion work - -The project deliberately remains at `0.9.0` until a focused promotion PR: - -1. bumps CMake and manifest versions to `1.0.0`, -2. marks v1.0 released/complete in documentation, -3. freezes remaining editorial pre-release API wording, -4. finalizes v1.0 release notes, -5. reruns the release gates. - -After that PR is merged, create tag `v1.0.0` and the GitHub release as a separate explicit action. - -```text -v1.0 hardening gates green - ↓ -manual acceptance review ✅ - ↓ -focused 0.9.0 → 1.0.0 release PR ← next - ↓ -merge - ↓ -v1.0 tag / release -``` +Release-hardening references: PR #30 and #31. The final promotion PR completes the `1.0.0` version transition. --- @@ -172,7 +126,25 @@ The following remain intentionally outside v1.0: - automatic retries - response cache -No item above should enter the v1 release path unless the frozen requirements are explicitly revised first. +These are post-v1 features and require separate requirements/design work before implementation. + +--- + +# Release sequence + +```text +v1.0 hardening gates green ✅ + ↓ +manual acceptance review ✅ + ↓ +1.0.0 promotion PR + final gates ← current + ↓ +merge + ↓ +tag v1.0.0 / GitHub Release +``` + +The tag and GitHub Release are intentionally separate from the promotion PR and must only be created after the promotion PR is green and merged. --- From 88dee5c116fbd8186087d1b469e6ab0655c05e32 Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Wed, 16 Sep 2026 11:21:29 +0700 Subject: [PATCH 09/14] docs: finalize v1.0 release notes --- docs/release/v1.0-notes.md | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/docs/release/v1.0-notes.md b/docs/release/v1.0-notes.md index 0225515..da15fb8 100644 --- a/docs/release/v1.0-notes.md +++ b/docs/release/v1.0-notes.md @@ -2,11 +2,11 @@ ## Status -Draft release notes for the v1.0 promotion. The repository remains version `0.9.0` until the focused promotion PR is merged. +Final release notes for `cpp_request` v1.0.0. The Git tag and GitHub Release are created only after the 1.0.0 promotion PR is green and merged. ## Overview -`cpp_request` v1.0 is a lightweight synchronous HTTP/1.1 client for C++17 built directly on native operating-system sockets. +`cpp_request` v1.0.0 is a lightweight synchronous HTTP/1.1 client for C++17 built directly on native operating-system sockets. The release intentionally focuses on a small, predictable surface: plaintext HTTP over TCP, explicit structured errors, bounded synchronous I/O, connection reuse, redirects, installable CMake packaging, and zero third-party runtime dependencies. @@ -66,6 +66,20 @@ target_link_libraries(my_app PRIVATE cpp_request::cpp_request) The exported production target does not expose GoogleTest or Google Benchmark dependencies. +The CMake project version and `vcpkg.json` manifest are both `1.0.0` for this release. + +## Stable v1 contracts + +The stable public contracts are documented under `docs/api/`: + +- `public-api.md` +- `lifetime.md` +- `error-model.md` +- `result.md` +- `response-limits.md` + +The release acceptance evidence is recorded in `docs/release/v1.0-acceptance.md`, with the manual review in `docs/release/v1.0-manual-review.md`. + ## Intentional v1.0 non-goals The following are deferred to post-v1 design work: @@ -85,4 +99,4 @@ The following are deferred to post-v1 design work: ## Upgrade note -v1.0 is the first stable release line. The public v1 contracts are documented under `docs/api/`, with requirement evidence in `docs/release/v1.0-acceptance.md` and the manual review record in `docs/release/v1.0-manual-review.md`. +v1.0.0 is the first stable release line. Users should treat the documented v1 public contracts as the compatibility baseline for subsequent v1.x releases. From 7f58337ac77a8ddd9bdfcd05558aecc56cf6cb3c Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Wed, 16 Sep 2026 11:22:01 +0700 Subject: [PATCH 10/14] docs: update v1.0 acceptance for promotion --- docs/release/v1.0-acceptance.md | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/release/v1.0-acceptance.md b/docs/release/v1.0-acceptance.md index b8285d7..5a6aed3 100644 --- a/docs/release/v1.0-acceptance.md +++ b/docs/release/v1.0-acceptance.md @@ -3,16 +3,16 @@ ## Status - Project: `cpp_request` -- Target: v1.0 release candidate -- Current project version during hardening: v0.9.0 +- Target: v1.0.0 stable release +- Current project version on promotion branch: v1.0.0 - Scope: synchronous HTTP/1.1 over plaintext TCP -- Automated release gates: **Passed** +- Release-hardening automated gates: **Passed** - Manual acceptance review: **Passed (2026-09-16)** -- Next step: focused `0.9.0 -> 1.0.0` promotion PR +- Current step: final 1.0.0 promotion PR verification -This document is a release gate, not a replacement for `docs/requirements/functional.md` or `docs/requirements/non-functional.md`. A requirement remains authoritative in those frozen documents. This matrix records how the repository verifies it before the version is promoted to v1.0.0. +This document is a release gate, not a replacement for `docs/requirements/functional.md` or `docs/requirements/non-functional.md`. Those frozen requirement documents remain authoritative. This matrix records the evidence used to accept the v1.0.0 stable line. -A v1.0 tag/release must not happen until the focused promotion PR is green and merged. +A `v1.0.0` tag/GitHub Release must not be created until the promotion PR is green and merged. --- @@ -79,7 +79,7 @@ A v1.0 tag/release must not happen until the focused promotion PR is green and m ## Automated v1 release gates -All required automated gates were green on the release-hardening candidate merged through PR #30. +All required automated gates were green on the release-hardening candidate merged through PR #30 and are rerun on the final promotion PR where path filters apply. 1. `C++ CI Build` - Windows, Linux, macOS. @@ -90,7 +90,7 @@ All required automated gates were green on the release-hardening candidate merge 2. `Benchmark Smoke` - Windows, Linux, macOS Release builds. - - all v0.9 benchmark executables build and execute locally. + - all committed benchmark executables build and execute locally. 3. `v1 Release Gate / ASan + UBSan (Clang, C++17)` - Debug Clang build. @@ -126,18 +126,18 @@ Reviewed areas: - frozen-scope compliance, - versioning/tagging/release metadata. -No code/API feature blocker remains. The repository manifest version mismatch found during review is corrected by the acceptance-cleanup branch before promotion. +No code/API feature blocker remains. The stale manifest metadata found during review was corrected before the promotion branch and both CMake/manifest versions are now promoted together to `1.0.0`. --- ## Promotion rule -The next focused release PR should: +The final promotion PR must: -1. change both CMake and manifest versions from `0.9.0` to `1.0.0`, -2. update roadmap/release status to v1.0 complete, -3. freeze remaining editorial pre-release API-document wording, -4. finalize `v1.0-notes.md`, -5. pass all required CI/release gates again. +1. carry `1.0.0` consistently in CMake and the manifest, +2. mark v1.0 implementation/acceptance complete, +3. freeze the stable public API/lifetime/error/result documentation, +4. contain final v1.0 release notes, +5. pass all required CI/release gates triggered by the promotion changes. -Create tag `v1.0.0` and the GitHub release only after that promotion PR is merged. +Create tag `v1.0.0` and the GitHub Release only after that promotion PR is green and merged. From 6429653734b846376cd521a5aeacebde010acfef Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Wed, 16 Sep 2026 11:22:42 +0700 Subject: [PATCH 11/14] docs: freeze v1.0 architecture status --- docs/architecture/overview.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/architecture/overview.md b/docs/architecture/overview.md index 7b1579c..0b041cc 100644 --- a/docs/architecture/overview.md +++ b/docs/architecture/overview.md @@ -4,7 +4,7 @@ - Project: `cpp_request` - Target release: MVP v1.0 -- Status: Proposed architecture baseline +- Status: Frozen v1.0 architecture - Language baseline: C++17 - Protocol scope: synchronous HTTP/1.1 over plaintext TCP From faff71826b6d8cb250fcbe79756cb5c8cb513621 Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Wed, 16 Sep 2026 11:43:57 +0700 Subject: [PATCH 12/14] docs: expand core public types diagram --- docs/api/public-api.md | 119 +++++++++++++++++++++++++++++++++++------ 1 file changed, 103 insertions(+), 16 deletions(-) diff --git a/docs/api/public-api.md b/docs/api/public-api.md index 0b15502..de1c7c3 100644 --- a/docs/api/public-api.md +++ b/docs/api/public-api.md @@ -40,26 +40,113 @@ The v1 public surface consists primarily of: The v1 API intentionally avoids unnecessary wrappers beyond the configuration types required by the stable surface. +The diagram below summarizes the stable public-facing state and operations of each core type. Private transport/parser state is intentionally omitted. + ```mermaid classDiagram - class Client - class Request - class Response - class ResponseLimits - class Headers - class Url - class Error - class Result~T~ - - Client --> Request : executes - Client --> Response : returns - Client --> ResponseLimits : configures - Request --> Headers : contains - Request --> Url : targets - Response --> Headers : contains - Result~T~ --> Error : failure + direction LR + + class Client { + +request(Request) Result~Response~ + +get(string_view) Result~Response~ + +head(string_view) Result~Response~ + +post(string_view, string_view) Result~Response~ + +put(string_view, string_view) Result~Response~ + +patch(string_view, string_view) Result~Response~ + +del(string_view) Result~Response~ + +set_connect_timeout(milliseconds) + +set_read_timeout(milliseconds) + +set_write_timeout(milliseconds) + +set_follow_redirects(bool) + +set_max_redirects(size_t) + +set_response_limits(ResponseLimits) + +response_limits() ResponseLimits + } + + class Request { + +method() Method + +url() string_view + +headers() Headers + +set_body(string_view) + +body() string_view + +add_query_param(string_view, string_view) + +query_params() QueryParam[] + } + + class Response { + +status_code() int + +reason() string_view + +headers() Headers + +body() string_view + +body_storage() string + } + + class ResponseLimits { + +max_head_bytes size_t + +max_body_bytes size_t + +max_chunk_line_bytes size_t + +max_trailer_bytes size_t + } + + class Headers { + +add(string_view, string_view) + +set(string_view, string_view) + +contains(string_view) bool + +get(string_view) string_view + +size() size_t + +empty() bool + +begin() const_iterator + +end() const_iterator + } + + class Url { + +parse(string_view)$ Result~Url~ + +scheme() string_view + +host() string_view + +path() string_view + +query() string_view + +target() string_view + +port() uint16_t + +has_explicit_port() bool + +host_is_ipv6_literal() bool + } + + class Error { + +code ErrorCode + +native_code int + } + + class Result~T~ { + +success(T)$ Result~T~ + +failure(Error)$ Result~T~ + +has_value() bool + +value() T + +error() Error + } + + Client ..> Request : executes + Client ..> Url : resolves request target + Client --> ResponseLimits : owns configuration + Client ..> Response : returns via Result + + Request *-- Headers : owns + Request ..> Url : URL text is parsed as + + Response *-- Headers : owns + + Result~T~ o-- Error : failure state + Result~T~ o-- Response : success example ``` +Relationship semantics: + +- `Client` executes a `Request` and returns the completed `Response` through `Result`. +- `Client` owns its `ResponseLimits` configuration and mutable connection-reuse state; transport state is intentionally omitted from this public diagram. +- `Request` owns its `Headers` and appended query-parameter strings, while its base URL and body remain borrowed `std::string_view` data. +- `Response` owns its response metadata/body storage, including its `Headers`. +- `Url` owns the normalized/parsing storage required to keep its component views valid. +- `Result` represents either a success payload `T` or an `Error` failure state. + --- ## `Client` From 26296017b6052c4984a97f01ef622e8212f2e067 Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Wed, 16 Sep 2026 12:10:39 +0700 Subject: [PATCH 13/14] fix: use major-version package compatibility --- cmake/modules/install.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/modules/install.cmake b/cmake/modules/install.cmake index 76bd1f0..1a136a4 100644 --- a/cmake/modules/install.cmake +++ b/cmake/modules/install.cmake @@ -17,7 +17,7 @@ function(cpp_request_configure_install) write_basic_package_version_file( "${PROJECT_BINARY_DIR}/cpp_requestConfigVersion.cmake" VERSION "${PROJECT_VERSION}" - COMPATIBILITY SameMinorVersion + COMPATIBILITY SameMajorVersion ) install( From aff8c3afe4d61563f39d3aee2a170f56f6f4783a Mon Sep 17 00:00:00 2001 From: Rafi Indra Pramudhito Zuhayr Date: Wed, 16 Sep 2026 12:11:16 +0700 Subject: [PATCH 14/14] docs: align Result contract with installed header --- docs/api/public-api.md | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/docs/api/public-api.md b/docs/api/public-api.md index de1c7c3..1028010 100644 --- a/docs/api/public-api.md +++ b/docs/api/public-api.md @@ -120,8 +120,11 @@ classDiagram +success(T)$ Result~T~ +failure(Error)$ Result~T~ +has_value() bool - +value() T - +error() Error + +value() T& + +value() const T& + +value() T&& + +error() Error& + +error() const Error& } Client ..> Request : executes @@ -447,15 +450,19 @@ public: bool has_value() const noexcept; explicit operator bool() const noexcept; - T& value(); - const T& value() const; + T& value() & noexcept; + const T& value() const& noexcept; + T&& value() && noexcept; - Error error() const noexcept; + Error& error() & noexcept; + const Error& error() const& noexcept; }; } // namespace cpp_request ``` +`value()` and `error()` are state-dependent accessors with the preconditions documented in `result.md`. The ref-qualified signatures above are part of the stable v1 surface and match the installed header. + The internal representation is intentionally not frozen here. `Result` must not require heap allocation solely to represent success/failure state when avoidable.