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
72
interfaces/Crafter.Review-Inventory.cppm
Normal file
72
interfaces/Crafter.Review-Inventory.cppm
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
|
||||
export module Crafter.Review:Inventory;
|
||||
import std;
|
||||
import :Json;
|
||||
|
||||
// The partition of a frozen commit range — a `git format-patch` export — into
|
||||
// review units for the coverage contract. It is MECHANICAL: no agent decides
|
||||
// what the units are. Every non-blank line of the artifact belongs to
|
||||
// exactly one unit, and the inventory fails when that does not hold. Fixed
|
||||
// slots give what is NOT in the text a row of the coverage matrix too.
|
||||
//
|
||||
// Units (ids):
|
||||
// pK:subject commit K's subject
|
||||
// pK:msg commit K's message body (before the trailers), when not empty
|
||||
// pK:trailers commit K's trailer block, when not empty
|
||||
// pK:diffstat commit K's diffstat
|
||||
// pK:<file>#hdr the diff header of <file> in commit K (mode, rename, blobs)
|
||||
// pK:<file>#H hunk H of <file> in commit K; a hunk of a documentation file
|
||||
// that holds more than one heading-delimited part is cut into
|
||||
// #H.1, #H.2, ... (DocParts)
|
||||
// pK:outside fixed slot: the in-repository users and callers of what
|
||||
// commit K changes, outside its diff
|
||||
// pK:absent fixed slot: what a reviewer expects in commit K and does not
|
||||
// find
|
||||
// whole:* fixed slots (WholeCommits)
|
||||
export namespace Crafter::Review::Inventory {
|
||||
struct WholeSlot {
|
||||
std::string_view id;
|
||||
std::string_view description;
|
||||
};
|
||||
constexpr std::array<WholeSlot, 3> WholeCommits = {{
|
||||
{"whole:order", "commit ordering and bisectability — each commit a self-contained step that leaves the repository consistent (no later commit in the range repairs an earlier one, nothing is used before the commit that adds it), the declared base"},
|
||||
{"whole:absent", "what a reviewer of this repository expects the range to contain and it does not — a test, a version/changelog bump, a doc or packaging update, a migration for existing installs, a message for a body-less commit"},
|
||||
{"whole:behaviour", "the user-visible behaviour of the range, exercised end to end in the running application — every behaviour the code and the messages promise, as a user sees it; the flows the range touches and the adjacent flows that share what it changes; what a user expects the range to deliver and does not get"},
|
||||
}};
|
||||
|
||||
// [a, b) is one hunk of a Markdown file, lines[a] its @@ line: its
|
||||
// partition at the headings on the new side of the diff, outside fenced
|
||||
// code blocks, once the part before holds anything but headings.
|
||||
std::vector<std::pair<std::size_t, std::size_t>> DocParts(std::span<const std::string> lines, std::size_t a, std::size_t b);
|
||||
// The first non-blank line, stripped and cut to 90 code points.
|
||||
std::string Excerpt(std::span<const std::string> lines);
|
||||
|
||||
struct Series {
|
||||
// Every patch's lines, one after the other: what unit lines number.
|
||||
std::vector<std::string> lines;
|
||||
// Units ({id, lines, excerpt}), the whole slots not yet among them.
|
||||
Json units;
|
||||
};
|
||||
Series PartitionSeries(const std::filesystem::path& dir, bool splitDocs = true);
|
||||
|
||||
// A partition that broke the invariant.
|
||||
class InvariantError : public std::runtime_error {
|
||||
public:
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
struct Built {
|
||||
// What `inventory commits <dir>` prints: {mode, artifact, hash8,
|
||||
// units, invariant, count}.
|
||||
Json out;
|
||||
std::vector<std::string> lines;
|
||||
};
|
||||
// `artifact` is kept as it is spelled: the inventory records it so.
|
||||
Built Build(std::string_view mode, std::string_view artifact);
|
||||
|
||||
// `crafter-review inventory commits <dir>`: the partition as indented
|
||||
// JSON on stdout. Exit 1 on a broken invariant or a bad command line.
|
||||
std::int32_t Main(std::span<const std::string_view> args);
|
||||
}
|
||||
Loading…
Reference in a new issue