feat(pipeline): the pure pipeline, checked against its goldens
The modules that turn a frozen range into passes and passes into a verdict: :Schema, :Tools (the DENY, git and base lists, a leaf, so the config and the pool both read them), :Plan, :Artifact, :Inventory, :Dispositions, :State, :Normalize, :Stream, :Prompt, :Config, and the URL half of :PgWire. Units, jobs, results, the config and the claude stream stay JSON from end to end, so every key keeps its place; :Text gives JSON values truthiness, get, ==, membership and Str(), and throws Dyn::Error under a name for each kind of unexpected data. Exactness where the bytes are seen: - the stream classifiers are hand-written scanners with case-insensitive folds (the Kelvin sign is a k), Unicode \b and \s, and `.` stopping at \n; the SQL call detection takes `<exe> sql` with any path before it; - config problems carry the JSON scanner's and the UTF-8 decoder's own texts, positions included; - a float total is Neumaier's compensated sum, and the build passes -ffp-contract=off: -march=native would fuse a*b+c; - `crafter-review inventory` and `config` print what the goldens record, byte for byte (the fixture and 14 configs, broken ones included); a message names this binary as the command to run. tests/Goldens: the recorded answers over the fixtures next to them (the commits fixture, a rich versioned series with CRLF, a folded subject, Markdown headings in and out of fences, the code-key rewrites); the suite compares 2304 results as DumpFile writes them, so a key out of order fails too. All agree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
b02de3ceff
commit
741e3a656d
70 changed files with 30565 additions and 21 deletions
|
|
@ -37,6 +37,56 @@ export namespace Crafter::Review {
|
|||
return sorted.dump(-1, ' ', true, nlohmann::json::error_handler_t::replace);
|
||||
}
|
||||
|
||||
// What another program wrote — a line of the claude stream, a shared
|
||||
// file — read leniently: an escaped lone surrogate ("\ud800" with no low
|
||||
// half after it), which nlohmann refuses, is read as U+FFFD.
|
||||
inline Json ParseLoose(std::string_view text) {
|
||||
try {
|
||||
return Json::parse(text);
|
||||
} catch (const nlohmann::json::parse_error&) {
|
||||
auto hexAt = [&](std::size_t at) -> std::int32_t {
|
||||
if (at + 4 > text.size()) return -1;
|
||||
std::int32_t value = 0;
|
||||
for (std::size_t i = at; i < at + 4; ++i) {
|
||||
const char c = text[i];
|
||||
const std::int32_t digit = c >= '0' && c <= '9' ? c - '0' : (c >= 'a' && c <= 'f' ? c - 'a' + 10 : (c >= 'A' && c <= 'F' ? c - 'A' + 10 : -1));
|
||||
if (digit < 0) return -1;
|
||||
value = value * 16 + digit;
|
||||
}
|
||||
return value;
|
||||
};
|
||||
std::string fixed;
|
||||
bool changed = false;
|
||||
for (std::size_t at = 0; at < text.size();) {
|
||||
if (text[at] != '\\' || at + 1 >= text.size()) {
|
||||
fixed.push_back(text[at++]);
|
||||
continue;
|
||||
}
|
||||
if (text[at + 1] != 'u') {
|
||||
fixed.append(text.substr(at, 2));
|
||||
at += 2;
|
||||
continue;
|
||||
}
|
||||
const std::int32_t unit = hexAt(at + 2);
|
||||
const bool high = unit >= 0xD800 && unit <= 0xDBFF;
|
||||
const bool low = unit >= 0xDC00 && unit <= 0xDFFF;
|
||||
if (high && at + 12 <= text.size() && text[at + 6] == '\\' && text[at + 7] == 'u' && hexAt(at + 8) >= 0xDC00 && hexAt(at + 8) <= 0xDFFF) {
|
||||
fixed.append(text.substr(at, 12));
|
||||
at += 12;
|
||||
} else if (high || low) {
|
||||
fixed += "\\ufffd";
|
||||
changed = true;
|
||||
at += 6;
|
||||
} else {
|
||||
fixed.append(text.substr(at, 2));
|
||||
at += 2;
|
||||
}
|
||||
}
|
||||
if (!changed) throw;
|
||||
return Json::parse(fixed);
|
||||
}
|
||||
}
|
||||
|
||||
// On one line, with ", " and ": " between items: where a reader matches
|
||||
// on the bytes, such as the first line of an opencode output, `{"type":
|
||||
// "review_gates_meta", …`, which salvage looks for.
|
||||
|
|
|
|||
Loading…
Reference in a new issue