// SPDX-License-Identifier: AGPL-3.0-only // SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts® // The claude stream: its envelopes. import std; import Crafter.Review; namespace fs = std::filesystem; using namespace Crafter::Review; namespace { std::int32_t Failures = 0; void Check(std::string_view name, bool cond, std::string_view detail = {}) { std::println("{}{}{}", cond ? " ok " : " FAIL ", name, !cond && !detail.empty() ? std::format(" ({})", detail) : std::string()); if (!cond) ++Failures; } // The result line mutated, or dropped when the mutation returns nullopt. std::string Variant(const std::string& text, const std::function(Json)>& mutate) { std::vector out; for (const std::string& line : Dyn::SplitLines(text)) { if (line.starts_with('{') && Dyn::Replace(line, " ", "").contains("\"type\":\"result\"")) { const std::optional changed = mutate(Json::parse(line)); if (!changed) continue; out.push_back(DumpSpaced(*changed)); } else { out.push_back(line); } } return Dyn::Join(out, "\n"); } std::function(Json)> Failed(const Json& update) { return [update](Json o) -> std::optional { o.update(update); o.erase("structured_output"); return o; }; } std::string Status(const std::string& text, std::optional code, std::string_view err = {}) { return Stream::Classify(Stream::ParseStream(text), code, err).first; } } int main() { std::println("stream envelopes"); const std::string text = Dyn::ReadText(fs::current_path() / "tests" / "Goldens" / "fixture" / "stream-success.jsonl"); const Json p = Stream::ParseStream(text); Check("success: classify ok", Stream::Classify(p, 0, "") == std::pair{"ok", ""}); const std::optional so = Stream::StructuredOutput(p.at("result")); Check("success: structured_output parsed", so && so->at("verdict") == "SEND-READY" && so->at("coverage").at(0).at("item") == "R1"); Check("success: init parsed", Dyn::Truthy(p.at("init")) && Dyn::Get(p.at("init"), "mcp_servers") == Json::array()); Check("success: usage from the result line + firstTurn", p.at("turns") == 2 && p.at("firstTurn").at("cache_creation_input_tokens").get() > 0 && p.at("usage").at("output").get() > 0 && !p.at("costUsd").is_null() && p.at("denials") == Json::array()); const Json w = Stream::Windows(p.at("rateLimit")); Check("success: rate windows as percent", w.contains("five_hour") && w.at("five_hour").at("utilization").get() > 0 && w.at("five_hour").at("utilization").get() < 100 && Dyn::Truthy(w.at("seven_day").at("resetsAt")), DumpCompactSorted(w)); Check("error_max_turns -> error", Status(Variant(text, Failed({{"subtype", "error_max_turns"}, {"is_error", true}, {"result", "Reached max turns (1)"}})), 1) == "error"); Check("429 -> limit", Status(Variant(text, Failed({{"subtype", "error_during_execution"}, {"is_error", true}, {"api_error_status", 429}, {"result", "API Error: 429 rate_limit_error"}})), 1) == "limit"); Check("401 -> auth", Status(Variant(text, Failed({{"subtype", "error_during_execution"}, {"is_error", true}, {"api_error_status", 401}, {"result", "API Error: 401 authentication_error"}})), 1) == "auth"); const std::string noResult = Variant(text, [](Json) -> std::optional { return std::nullopt; }); Check("403 in stderr, no result -> auth", Status(noResult, 1, "Error: 403 Forbidden") == "auth"); Check("no result line -> error", Status(noResult, 1) == "error"); Check("no result line + usage limit text -> limit", Status(noResult, 1, "You're out of extra usage") == "limit"); Check("timeout flag / exit 124 -> timeout", Stream::Classify(p, std::nullopt, "", true).first == "timeout" && Stream::Classify(p, 124, "").first == "timeout"); const std::string doubled = Variant(text, [](Json o) -> std::optional { o["structured_output"] = DumpSpaced(o.at("structured_output")); return o; }); Check("double-encoded structured_output decoded", Stream::StructuredOutput(Stream::ParseStream(doubled).at("result")) == so); const std::string inResult = Variant(text, [](Json o) -> std::optional { const Json inner = o.at("structured_output"); o.erase("structured_output"); o["result"] = DumpSpaced(inner); return o; }); Check("structured output inside result text decoded", Stream::StructuredOutput(Stream::ParseStream(inResult).at("result")) == so); const std::string noFindings = Variant(text, [](Json o) -> std::optional { o["structured_output"] = {{"verdict", "SEND-READY"}}; return o; }); Check("missing findings array -> error (no structured output)", Status(noFindings, 0) == "error"); Check("credit balance too low -> credits", Status(Variant(text, Failed({{"subtype", "error_during_execution"}, {"is_error", true}, {"api_error_status", 400}, {"result", "API Error: 400 Your credit balance is too low to access the Anthropic API."}})), 1) == "credits"); // The helper's spelling is a SQL call wherever it stands, and a stream // line is cut at \n only: a raw U+2028 in a JSON string stays inside its // line. Check("this binary's helper is a SQL call, alone, after a path, in a compound command; another verb is not", Stream::IsSqlCall("/app/bin/crafter-review sql <<'SQL'\nselect 1;\nSQL") && Stream::IsSqlCall("cd . && crafter-review sql 'select 1'") && Stream::IsSqlCall("/x/bin/crafter.review-exe-v/crafter-review sql") && !Stream::IsSqlCall("/app/bin/crafter-review sqlx") && !Stream::IsSqlCall("/app/bin/crafter-review totp f.json")); const std::string split = "{\"type\":\"result\",\"subtype\":\"success\",\"is_error\":false,\"result\":\"a\u2028b\",\"structured_output\":{\"verdict\":\"SEND-READY\",\"findings\":[],\"notes\":[],\"coverage\":[]}}"; Check("a result line with a raw U+2028 in a string is still the result line", Stream::Classify(Stream::ParseStream(split), 0, "").first == "ok"); return Failures ? 1 : 0; }