feat(modes): the kernel modes - a patch series and a mailing-list reply
Beside the push gates, crafter-review runs the pre-send review of a kernel contributor's workbench: --mode series over a format-patch export with its cover letter, --mode reply over a mail draft. The inventory partitions a cover letter, a plain mail and the camera review's shape; ten kernel lenses join the table with their gates (structure, code, artifact truth) and the persona gate - one maintainer-persona instance per --reviewers entry, its whole pass gating its blocks and pruning the units that are not theirs. A series' code units are keyed on the code like the push gates'; every text unit on its text, the references it cites and the anchor when it is dated. The references an artifact cites are prefetched through utilities/lore.py into the gate-state, with a digest in the run layer, and a persona's calibration map is its lore listing. The kernel schema carries the persona vocabulary, the kernel note kinds, the predicted review and the not-mine list; the matrix the persona holes, the thin and the modelled units; gates clean without a persona pass is GATES CLEAN. A reply's gate-state sits beside the draft and is read once; a kernel mode needs --subsystem, defaults the effort to max and the trees to work/linux,work/milos-linux, and excuses no unit from the holes. The kernel lenses and the common text ship verbatim; the table lists them after the push gates' lenses and the config template holds them off, so a repository that never turns them on changes nothing. Prompts, keys and plans were checked byte for byte against the pipeline these modes replace, on its own artifacts: two replies and a series, every system prompt, user prompt, job and reuse key identical.
This commit is contained in:
parent
4fc15fe1c2
commit
339f4b9b40
46 changed files with 5732 additions and 362 deletions
|
|
@ -267,8 +267,9 @@ inventory unit, against a frozen commit range.)";
|
|||
{"--verify", false, "", "push-time check: HEAD, a clean tree and a fresh export still match freeze.json"},
|
||||
}};
|
||||
|
||||
constexpr std::array<std::string_view, 1> Modes = {"commits"};
|
||||
constexpr std::array<std::string_view, 3> Modes = {"reply", "series", "commits"};
|
||||
constexpr std::array<std::string_view, 2> Harnesses = {"claude", "opencode"};
|
||||
constexpr std::array<std::string_view, 2> PersonaBlocks = {"pruned", "all"};
|
||||
|
||||
const std::vector<OptionSpec>& RunOptions() {
|
||||
static const std::string effort = std::format("reasoning effort of every lens process (default {})", Plan::Effort);
|
||||
|
|
@ -282,12 +283,14 @@ inventory unit, against a frozen commit range.)";
|
|||
static const std::string variant = std::format("opencode harness: the model's reasoning variant (default {})", OpenCode::DefaultVariant);
|
||||
static const std::vector<OptionSpec> specs = {
|
||||
{"--mode", true, "MODE", "", Kind::String, Modes, false, true},
|
||||
{"--artifact", true, "ARTIFACT", "the frozen artifact dir (.review-gates/push/<slug>/)", Kind::String, {}, true},
|
||||
{"--subsystem", true, "SUBSYSTEM", "journal subsystem: required for reply and series; optional in commits mode (names the ledger and captures the lenses may read)"},
|
||||
{"--artifact", true, "ARTIFACT", "the frozen artifact: a dir of *.patch (.review-gates/push/<slug>/, a series), or the reply draft file"},
|
||||
{"--hash", true, "HASH", ""},
|
||||
{"--reviewers", true, "REVIEWERS", "reply and series: the reviewers a persona reads as, comma-separated (the persona gate runs after the others)"},
|
||||
{"--dispositions", true, "DISPOSITIONS", ""},
|
||||
{"--base", true, "BASE", ""},
|
||||
{"--date", true, "DATE", ""},
|
||||
{"--trees", true, "TREES", "the checkout the lenses read, at the range HEAD: the repository itself (`.`, default) or the frozen worktree"},
|
||||
{"--trees", true, "TREES", "the checkout the lenses read, at the range HEAD: the repository itself (`.`, default) or the frozen worktree; reply and series default work/linux,work/milos-linux"},
|
||||
{"--context-paths", true, "CONTEXT_PATHS", "comma-separated paths of material the artifact discusses (captures, evidence) - mechanical pointers, no narrative"},
|
||||
{"--only", true, "ONLY", "comma-separated lens files (test only)"},
|
||||
{"--without", true, "WITHOUT", "comma-separated lens files to leave out of this run by owner ruling (logged; the run stays a full run)"},
|
||||
|
|
@ -302,6 +305,7 @@ inventory unit, against a frozen commit range.)";
|
|||
{"--reads", true, "READS", reads, Kind::Int},
|
||||
{"--no-reuse", false, "", ""},
|
||||
{"--no-salvage", false, "", "do not reuse finished process outputs of an earlier run on this hash"},
|
||||
{"--persona-blocks", true, "{pruned,all}", "reply and series: pruned (default) skips the block passes of the units a persona's whole pass marked not-mine", Kind::String, PersonaBlocks},
|
||||
{"--dry-run", false, "", ""},
|
||||
{"--claude-bin", true, "CLAUDE_BIN", ""},
|
||||
{"--models", true, "MODELS", models},
|
||||
|
|
@ -332,8 +336,12 @@ inventory unit, against a frozen commit range.)";
|
|||
Run::Arguments a;
|
||||
a.config = configPath;
|
||||
a.mode = parsed.Value("mode").value_or("commits");
|
||||
a.subsystem = parsed.Value("subsystem");
|
||||
a.artifact = parsed.Value("artifact").value_or("");
|
||||
if (a.artifact.empty()) return UsageError(UsageLine(Prog, specs), Prog, "the following arguments are required: --artifact");
|
||||
a.hash = parsed.Value("hash");
|
||||
a.reviewers = parsed.Value("reviewers").value_or("");
|
||||
a.personaBlocks = parsed.Value("persona-blocks").value_or("pruned");
|
||||
a.dispositions = parsed.Value("dispositions");
|
||||
a.base = parsed.Value("base").value_or("");
|
||||
a.date = parsed.Value("date").value_or(Platform::TodayIso());
|
||||
|
|
@ -361,7 +369,13 @@ inventory unit, against a frozen commit range.)";
|
|||
if (*a.concurrency < 1) return UsageError(UsageLine(Prog, specs), Prog, "argument --concurrency: a number from 1 up (leave it out for no cap)");
|
||||
}
|
||||
a.effort = parsed.Value("effort").value_or("");
|
||||
if (a.effort.empty()) a.effort = std::string(Plan::DefaultEffort());
|
||||
if (a.effort.empty()) a.effort = std::string(Plan::DefaultEffort(a.mode));
|
||||
if (a.mode == "commits") {
|
||||
if (!a.reviewers.empty()) return Die("commits mode has no persona gate (the owner is the last gate): drop --reviewers");
|
||||
} else {
|
||||
if (!a.subsystem || a.subsystem->empty()) return Die(std::format("{} mode: --subsystem is required", a.mode));
|
||||
if (!a.trees || a.trees->empty()) a.trees = "work/linux,work/milos-linux";
|
||||
}
|
||||
a.noReuse = parsed.Flag("no-reuse");
|
||||
a.noSalvage = parsed.Flag("no-salvage");
|
||||
a.dryRun = parsed.Flag("dry-run");
|
||||
|
|
@ -377,9 +391,10 @@ inventory unit, against a frozen commit range.)";
|
|||
a.variant = parsed.Value("variant");
|
||||
if (!a.variant || a.variant->empty()) a.variant = a.harness == "opencode" ? std::optional<std::string>(std::string(OpenCode::DefaultVariant)) : std::nullopt;
|
||||
a.opencodeBin = parsed.Value("opencode-bin");
|
||||
a.reads = Plan::ReadsFor(a.mode);
|
||||
if (const std::optional<std::string> reads = parsed.Value("reads")) {
|
||||
const std::int64_t n = *Dyn::ParseInt(*reads);
|
||||
if (n < 1) return Die("--reads: a number from 1 up");
|
||||
if (n < 1 || (a.mode == "reply" && n != 1)) return Die("--reads: a number from 1 up; a reply has no code units and is read once");
|
||||
a.reads = n;
|
||||
}
|
||||
for (const std::string& part : Dyn::Split(a.models, ",")) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue