@mrciphersmith/keryx 0.2.72 → 0.2.73
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +352 -22
- package/package.json +2 -2
- package/src/gdskills/bundled/rules/core/gproject-contracts.mdc +1 -1
- package/src/gdskills/bundled/rules/core/jobs-documentation.mdc +1 -1
- package/src/gdskills/bundled/rules/core/subagent-context-construction.md +1 -1
- package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.codex.md +326 -20
- package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.cursor.md +320 -22
- package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.opencode.md +326 -12
- package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.zed.md +333 -9
- package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.codex.md +92 -4
- package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.cursor.md +92 -4
- package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.opencode.md +92 -4
- package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.zed.md +92 -4
- package/src/gdskills/bundled/skills/orchestration/context-collector/orchestrator-prompt.md +1 -1
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.codex.md +154 -1098
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.cursor.md +154 -1098
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.opencode.md +154 -1098
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.zed.md +154 -1098
- package/src/gdskills/bundled/skills/orchestration/feature-analyzer/orchestrator-prompt.md +1 -1
- package/src/gdskills/bundled/skills/orchestration/feature-dev/SKILL.codex.md +101 -41
- package/src/gdskills/bundled/skills/orchestration/feature-dev/SKILL.cursor.md +101 -41
- package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.codex.md +115 -49
- package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.cursor.md +115 -49
- package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.opencode.md +115 -49
- package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.zed.md +115 -49
- package/src/gdskills/bundled/skills/orchestration/issue-analyzer/orchestrator-prompt.md +1 -1
- package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.codex.md +15 -6
- package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.cursor.md +15 -6
- package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.opencode.md +15 -6
- package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.zed.md +15 -6
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.codex.md +1 -1
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.cursor.md +1 -1
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.md +1 -1
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.opencode.md +1 -1
- package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.zed.md +1 -1
- package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.codex.md +300 -55
- package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.cursor.md +300 -55
- package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.md +120 -37
- package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.opencode.md +300 -55
- package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.zed.md +300 -55
- package/src/gdskills/bundled/skills/orchestration/task-implementer/input-contract.schema.json +56 -14
- package/src/gdskills/bundled/skills/orchestration/task-implementer/orchestrator-prompt.md +50 -23
- package/src/gdskills/bundled/skills/orchestration/task-implementer/output-contract.schema.json +6 -2
- package/src/gdskills/bundled/skills/orchestration/task-implementer/task-request.template.md +18 -12
- package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.codex.md +1 -1
- package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.cursor.md +1 -1
- package/src/gdskills/bundled/skills/planning/consistency-checker/SKILL.codex.md +168 -9
- package/src/gdskills/bundled/skills/planning/consistency-checker/SKILL.cursor.md +168 -9
- package/src/gdskills/bundled/skills/planning/interview/SKILL.codex.md +7 -1
- package/src/gdskills/bundled/skills/planning/interview/SKILL.cursor.md +7 -1
- package/src/gdskills/bundled/skills/planning/interviewer/SKILL.codex.md +7 -1
- package/src/gdskills/bundled/skills/planning/interviewer/SKILL.cursor.md +7 -1
- package/src/gdskills/bundled/skills/planning/patterns-researcher/SKILL.codex.md +215 -9
- package/src/gdskills/bundled/skills/planning/patterns-researcher/SKILL.cursor.md +215 -9
- package/src/gdskills/bundled/skills/planning/planner/SKILL.codex.md +168 -9
- package/src/gdskills/bundled/skills/planning/planner/SKILL.cursor.md +168 -9
- package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.codex.md +2 -2
- package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.cursor.md +2 -2
- package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.opencode.md +2 -2
- package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.zed.md +2 -2
- package/src/gdskills/bundled/skills/planning/problem-definer/SKILL.codex.md +133 -9
- package/src/gdskills/bundled/skills/planning/problem-definer/SKILL.cursor.md +133 -9
- package/src/gdskills/bundled/skills/planning/project-discovery/SKILL.codex.md +145 -9
- package/src/gdskills/bundled/skills/planning/project-discovery/SKILL.cursor.md +145 -9
- package/src/gdskills/bundled/skills/planning/spec-writer/SKILL.codex.md +210 -9
- package/src/gdskills/bundled/skills/planning/spec-writer/SKILL.cursor.md +210 -9
- package/src/gdskills/bundled/skills/planning/stack-advisor/SKILL.codex.md +161 -9
- package/src/gdskills/bundled/skills/planning/stack-advisor/SKILL.cursor.md +161 -9
- package/src/gdskills/bundled/skills/platform/claude-md-management/SKILL.codex.md +1 -1
- package/src/gdskills/bundled/skills/platform/claude-md-management/SKILL.cursor.md +1 -1
- package/src/gdskills/bundled/skills/platform/hookify/SKILL.codex.md +1 -1
- package/src/gdskills/bundled/skills/platform/hookify/SKILL.cursor.md +1 -1
- package/src/gdskills/bundled/skills/quality/changelog/SKILL.codex.md +1 -1
- package/src/gdskills/bundled/skills/quality/changelog/SKILL.cursor.md +1 -1
- package/src/gdskills/bundled/skills/quality/commit/SKILL.codex.md +1 -1
- package/src/gdskills/bundled/skills/quality/commit/SKILL.cursor.md +1 -1
- package/src/gdskills/bundled/skills/quality/db-migrate/SKILL.codex.md +1 -1
- package/src/gdskills/bundled/skills/quality/db-migrate/SKILL.cursor.md +1 -1
- package/src/gdskills/bundled/skills/quality/dependency-update/SKILL.codex.md +1 -1
- package/src/gdskills/bundled/skills/quality/dependency-update/SKILL.cursor.md +1 -1
- package/src/gdskills/bundled/skills/quality/deploy/SKILL.codex.md +1 -1
- package/src/gdskills/bundled/skills/quality/deploy/SKILL.cursor.md +1 -1
- package/src/gdskills/bundled/skills/quality/perf-check/SKILL.codex.md +1 -1
- package/src/gdskills/bundled/skills/quality/perf-check/SKILL.cursor.md +1 -1
- package/src/gdskills/bundled/skills/quality/pr/SKILL.codex.md +1 -1
- package/src/gdskills/bundled/skills/quality/pr/SKILL.cursor.md +1 -1
- package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.codex.md +15 -1
- package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.cursor.md +248 -165
- package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.opencode.md +15 -1
- package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.zed.md +359 -19
- package/src/gdskills/bundled/skills/quality/push/SKILL.codex.md +1 -1
- package/src/gdskills/bundled/skills/quality/push/SKILL.cursor.md +1 -1
- package/src/gdskills/bundled/skills/quality/security-audit/SKILL.codex.md +1 -1
- package/src/gdskills/bundled/skills/quality/security-audit/SKILL.cursor.md +1 -1
- package/src/gdskills/bundled/skills/quality/test-gen/SKILL.codex.md +1 -1
- package/src/gdskills/bundled/skills/quality/test-gen/SKILL.cursor.md +1 -1
- package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.codex.md +299 -24
- package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.cursor.md +296 -31
- package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.opencode.md +309 -18
- package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.zed.md +312 -17
- package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.codex.md +29 -30
- package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.cursor.md +29 -30
- package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.opencode.md +29 -30
- package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.zed.md +29 -30
- package/src/gdskills/bundled/skills/review/code-learned-review/SKILL.codex.md +21 -30
- package/src/gdskills/bundled/skills/review/code-learned-review/SKILL.cursor.md +21 -30
- package/src/gdskills/bundled/skills/review/code-learned-review/SKILL.opencode.md +21 -30
- package/src/gdskills/bundled/skills/review/code-learned-review/SKILL.zed.md +21 -30
- package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.codex.md +19 -23
- package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.cursor.md +19 -23
- package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.opencode.md +19 -23
- package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.zed.md +19 -23
- package/src/gdskills/bundled/skills/review/code-style-review/SKILL.codex.md +17 -24
- package/src/gdskills/bundled/skills/review/code-style-review/SKILL.cursor.md +17 -24
- package/src/gdskills/bundled/skills/review/code-style-review/SKILL.opencode.md +17 -24
- package/src/gdskills/bundled/skills/review/code-style-review/SKILL.zed.md +17 -24
- package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.md +16 -3
- package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.claude.md +0 -46
- package/src/gdskills/bundled/skills/planning/interviewer/SKILL.claude.md +0 -94
- package/src/gdskills/bundled/skills/quality/changelog/SKILL.claude.md +0 -45
- package/src/gdskills/bundled/skills/quality/db-migrate/SKILL.claude.md +0 -40
- package/src/gdskills/bundled/skills/quality/dependency-update/SKILL.claude.md +0 -45
- package/src/gdskills/bundled/skills/quality/deploy/SKILL.claude.md +0 -42
- package/src/gdskills/bundled/skills/quality/perf-check/SKILL.claude.md +0 -48
- package/src/gdskills/bundled/skills/quality/security-audit/SKILL.claude.md +0 -40
- package/src/gdskills/bundled/skills/quality/test-gen/SKILL.claude.md +0 -30
package/dist/cli.js
CHANGED
|
@@ -10618,7 +10618,13 @@ function renderCrossFamilyDecision(decision) {
|
|
|
10618
10618
|
lines.push("");
|
|
10619
10619
|
lines.push("## record");
|
|
10620
10620
|
lines.push("");
|
|
10621
|
-
lines.push("
|
|
10621
|
+
lines.push("Record it on the round, and read it back:");
|
|
10622
|
+
lines.push("");
|
|
10623
|
+
lines.push(" keryx providers cross-family --opt-in --json > cross-family.json");
|
|
10624
|
+
lines.push(" keryx review ingest ... --cross-family-review cross-family.json");
|
|
10625
|
+
lines.push(" keryx review status <review-id> # reads it off disk and refuses a self-contradictory record");
|
|
10626
|
+
lines.push("");
|
|
10627
|
+
lines.push("A round that records nothing reports `not recorded`, which is NOT `single-family`: nobody decided.");
|
|
10622
10628
|
lines.push("");
|
|
10623
10629
|
lines.push("```json");
|
|
10624
10630
|
lines.push(JSON.stringify({ cross_family_review: decision }, null, 2));
|
|
@@ -23811,6 +23817,74 @@ function isUnknownDisposition(task) {
|
|
|
23811
23817
|
}
|
|
23812
23818
|
return !GATE_PASSING_DISPOSITIONS.has(task.disposition);
|
|
23813
23819
|
}
|
|
23820
|
+
function isSatisfied(task) {
|
|
23821
|
+
return task?.status === "done";
|
|
23822
|
+
}
|
|
23823
|
+
function nextTask(tasks) {
|
|
23824
|
+
const byId = new Map(tasks.map((task) => [task.id.toUpperCase(), task]));
|
|
23825
|
+
const blocked = [];
|
|
23826
|
+
for (const task of tasks) {
|
|
23827
|
+
if (task.status === "done") {
|
|
23828
|
+
continue;
|
|
23829
|
+
}
|
|
23830
|
+
const waitingOn = (task.dependsOn ?? []).map((dependency) => dependency.toUpperCase()).filter((dependency) => !isSatisfied(byId.get(dependency)));
|
|
23831
|
+
if (waitingOn.length === 0) {
|
|
23832
|
+
return { kind: "ready", task };
|
|
23833
|
+
}
|
|
23834
|
+
blocked.push({ task, waitingOn });
|
|
23835
|
+
}
|
|
23836
|
+
return blocked.length === 0 ? { kind: "none" } : { kind: "blocked", blocked };
|
|
23837
|
+
}
|
|
23838
|
+
function dependencyIssues(tasks) {
|
|
23839
|
+
const known = new Set(tasks.map((task) => task.id.toUpperCase()));
|
|
23840
|
+
const issues = [];
|
|
23841
|
+
for (const task of tasks) {
|
|
23842
|
+
for (const raw of task.dependsOn ?? []) {
|
|
23843
|
+
const dependency = raw.toUpperCase();
|
|
23844
|
+
if (dependency === task.id.toUpperCase()) {
|
|
23845
|
+
issues.push({
|
|
23846
|
+
task: task.id,
|
|
23847
|
+
kind: "self-dependency",
|
|
23848
|
+
message: `${task.id} depends on itself, so it can never become ready`
|
|
23849
|
+
});
|
|
23850
|
+
continue;
|
|
23851
|
+
}
|
|
23852
|
+
if (!known.has(dependency)) {
|
|
23853
|
+
issues.push({
|
|
23854
|
+
task: task.id,
|
|
23855
|
+
kind: "unknown-dependency",
|
|
23856
|
+
message: `${task.id} depends on ${dependency}, which is not a task in this flow \u2014 the dependency can never be satisfied`
|
|
23857
|
+
});
|
|
23858
|
+
}
|
|
23859
|
+
}
|
|
23860
|
+
}
|
|
23861
|
+
for (const id of tasksInCycles(tasks)) {
|
|
23862
|
+
issues.push({
|
|
23863
|
+
task: id,
|
|
23864
|
+
kind: "cycle",
|
|
23865
|
+
message: `${id} is part of a dependsOn cycle, so neither it nor anything waiting on it can ever start`
|
|
23866
|
+
});
|
|
23867
|
+
}
|
|
23868
|
+
return issues;
|
|
23869
|
+
}
|
|
23870
|
+
function tasksInCycles(tasks) {
|
|
23871
|
+
const known = new Set(tasks.map((task) => task.id.toUpperCase()));
|
|
23872
|
+
const remaining = new Map(tasks.map((task) => [
|
|
23873
|
+
task.id.toUpperCase(),
|
|
23874
|
+
(task.dependsOn ?? []).map((dependency) => dependency.toUpperCase()).filter((dependency) => known.has(dependency) && dependency !== task.id.toUpperCase())
|
|
23875
|
+
]));
|
|
23876
|
+
let peeled = true;
|
|
23877
|
+
while (peeled) {
|
|
23878
|
+
peeled = false;
|
|
23879
|
+
for (const [id, dependencies] of remaining) {
|
|
23880
|
+
if (dependencies.every((dependency) => !remaining.has(dependency))) {
|
|
23881
|
+
remaining.delete(id);
|
|
23882
|
+
peeled = true;
|
|
23883
|
+
}
|
|
23884
|
+
}
|
|
23885
|
+
}
|
|
23886
|
+
return [...remaining.keys()].sort();
|
|
23887
|
+
}
|
|
23814
23888
|
function evaluateTaskGate(tasks) {
|
|
23815
23889
|
const open2 = [];
|
|
23816
23890
|
const failed = [];
|
|
@@ -26368,6 +26442,13 @@ var init_allocation = __esm(() => {
|
|
|
26368
26442
|
// src/flow/service.ts
|
|
26369
26443
|
import { mkdir as mkdir40, readFile as readFile58, rename as rename5 } from "fs/promises";
|
|
26370
26444
|
import path112 from "path";
|
|
26445
|
+
function recordAttempt(task, outcome, at, detail) {
|
|
26446
|
+
const attempts = task.attempts ?? { count: 0, log: [] };
|
|
26447
|
+
attempts.count += 1;
|
|
26448
|
+
attempts.log.push({ at, outcome, ...detail?.trim() ? { detail: detail.trim() } : {} });
|
|
26449
|
+
task.attempts = attempts;
|
|
26450
|
+
return attempts;
|
|
26451
|
+
}
|
|
26371
26452
|
function createFlowService(deps) {
|
|
26372
26453
|
const now = () => deps.now().toISOString();
|
|
26373
26454
|
async function load(cwd, id) {
|
|
@@ -26484,6 +26565,9 @@ function createFlowService(deps) {
|
|
|
26484
26565
|
async get({ cwd, id }) {
|
|
26485
26566
|
return (await load(cwd, id)).flow;
|
|
26486
26567
|
},
|
|
26568
|
+
async next({ cwd, id }) {
|
|
26569
|
+
return nextTask((await load(cwd, id)).flow.tasks);
|
|
26570
|
+
},
|
|
26487
26571
|
async freeze({ cwd, id }) {
|
|
26488
26572
|
return mutate(cwd, id, async ({ dir, flow }) => {
|
|
26489
26573
|
assertTransition(flow.status, "ready");
|
|
@@ -26541,6 +26625,9 @@ function createFlowService(deps) {
|
|
|
26541
26625
|
if (runLink !== undefined) {
|
|
26542
26626
|
task.runLink = runLink;
|
|
26543
26627
|
}
|
|
26628
|
+
if (task.disposition === "failed" || task.disposition === "blocked") {
|
|
26629
|
+
recordAttempt(task, task.disposition, now(), task.dispositionReason);
|
|
26630
|
+
}
|
|
26544
26631
|
return save(cwd, dir, flow, "task-done", `${task.id}: ${task.title}`);
|
|
26545
26632
|
});
|
|
26546
26633
|
},
|
|
@@ -26551,10 +26638,7 @@ function createFlowService(deps) {
|
|
|
26551
26638
|
if (!task) {
|
|
26552
26639
|
throw new Error(`Task not found: ${taskId}. Known: ${flow.tasks.map((t) => t.id).join(", ")}`);
|
|
26553
26640
|
}
|
|
26554
|
-
const attempts = task
|
|
26555
|
-
attempts.count += 1;
|
|
26556
|
-
attempts.log.push({ at: now(), outcome, ...detail?.trim() ? { detail: detail.trim() } : {} });
|
|
26557
|
-
task.attempts = attempts;
|
|
26641
|
+
const attempts = recordAttempt(task, outcome, now(), detail);
|
|
26558
26642
|
return save(cwd, dir, flow, "task-attempt", `${task.id}: ${outcome} (attempt ${attempts.count})${detail?.trim() ? ` \u2014 ${detail.trim()}` : ""}`);
|
|
26559
26643
|
});
|
|
26560
26644
|
},
|
|
@@ -26837,6 +26921,22 @@ function createFlowService(deps) {
|
|
|
26837
26921
|
if (flow.status === "done" && !flow.pr.url && !flow.merged) {
|
|
26838
26922
|
issues.push({ flow: dir, kind: "state", message: "done without a recorded PR" });
|
|
26839
26923
|
}
|
|
26924
|
+
for (const issue of dependencyIssues(flow.tasks)) {
|
|
26925
|
+
issues.push({ flow: dir, kind: "dependency", message: issue.message });
|
|
26926
|
+
}
|
|
26927
|
+
for (const task of flow.tasks) {
|
|
26928
|
+
if (task.disposition !== "failed" && task.disposition !== "blocked") {
|
|
26929
|
+
continue;
|
|
26930
|
+
}
|
|
26931
|
+
if ((task.attempts?.count ?? 0) > 0) {
|
|
26932
|
+
continue;
|
|
26933
|
+
}
|
|
26934
|
+
issues.push({
|
|
26935
|
+
flow: dir,
|
|
26936
|
+
kind: "attempts",
|
|
26937
|
+
message: `${task.id} is recorded ${task.disposition} with attempts.count 0 \u2014 a task that failed without a ` + "single recorded attempt describes work nobody can audit. Record what was tried with: " + `keryx flow task attempt ${flow.id} ${task.id} --outcome ${task.disposition} --detail "<what happened>"`
|
|
26938
|
+
});
|
|
26939
|
+
}
|
|
26840
26940
|
}
|
|
26841
26941
|
return { ok: issues.length === 0, issues };
|
|
26842
26942
|
}
|
|
@@ -32337,6 +32437,18 @@ var CONTRACTS = [
|
|
|
32337
32437
|
name: "subagent-result",
|
|
32338
32438
|
fileName: "subagent-result.schema.json",
|
|
32339
32439
|
description: "Subagent-to-orchestrator result payload."
|
|
32440
|
+
},
|
|
32441
|
+
{
|
|
32442
|
+
name: "task-implementer-input",
|
|
32443
|
+
fileName: "task-implementer-input-contract.schema.json",
|
|
32444
|
+
description: "Task request handed to task-implementer (task + workspace + automation), validated before dispatch.",
|
|
32445
|
+
sourcePath: "src/gdskills/bundled/skills/orchestration/task-implementer/input-contract.schema.json"
|
|
32446
|
+
},
|
|
32447
|
+
{
|
|
32448
|
+
name: "task-implementer-output",
|
|
32449
|
+
fileName: "task-implementer-output-contract.schema.json",
|
|
32450
|
+
description: "JSON result task-implementer writes in Phase 6.1 before it emits its STATUS line.",
|
|
32451
|
+
sourcePath: "src/gdskills/bundled/skills/orchestration/task-implementer/output-contract.schema.json"
|
|
32340
32452
|
}
|
|
32341
32453
|
];
|
|
32342
32454
|
function normalizeContractName(value) {
|
|
@@ -32431,6 +32543,18 @@ async function validateValue(value, schema, valuePath, errors, rootSchema, schem
|
|
|
32431
32543
|
message: `Expected number >= ${schema.minimum}`
|
|
32432
32544
|
});
|
|
32433
32545
|
}
|
|
32546
|
+
if (typeof value === "number" && schema.maximum !== undefined && value > schema.maximum) {
|
|
32547
|
+
errors.push({
|
|
32548
|
+
path: valuePath,
|
|
32549
|
+
message: `Expected number <= ${schema.maximum}`
|
|
32550
|
+
});
|
|
32551
|
+
}
|
|
32552
|
+
if (Array.isArray(value) && schema.minItems !== undefined && value.length < schema.minItems) {
|
|
32553
|
+
errors.push({
|
|
32554
|
+
path: valuePath,
|
|
32555
|
+
message: `Expected array length >= ${schema.minItems}`
|
|
32556
|
+
});
|
|
32557
|
+
}
|
|
32434
32558
|
if (typeof value === "string") {
|
|
32435
32559
|
if (schema.minLength !== undefined && value.length < schema.minLength) {
|
|
32436
32560
|
errors.push({
|
|
@@ -41936,7 +42060,8 @@ var BUNDLED_SKILL_CHECKS = [
|
|
|
41936
42060
|
"persona:marker",
|
|
41937
42061
|
"path:personal-home",
|
|
41938
42062
|
"xref:skill",
|
|
41939
|
-
"xref:path"
|
|
42063
|
+
"xref:path",
|
|
42064
|
+
"document:addressable"
|
|
41940
42065
|
];
|
|
41941
42066
|
var PERSONA_PATTERNS = [
|
|
41942
42067
|
{ pattern: /\bb091\b/i, why: "the reviewer's handle as it appeared on disk" },
|
|
@@ -42057,7 +42182,14 @@ function normaliseReferencePath(reference) {
|
|
|
42057
42182
|
const root = reference.split("/")[0] ?? "";
|
|
42058
42183
|
return CHECKED_PATH_ROOTS.includes(root) ? reference : undefined;
|
|
42059
42184
|
}
|
|
42060
|
-
|
|
42185
|
+
var SKILL_DOCUMENT_NAMES = new Set(HARNESS_SKILL_RUNTIMES.map((runtime) => skillBuildFileName(runtime)));
|
|
42186
|
+
var KNOWN_SKILL_COMPANION_DOCUMENTS = new Map([
|
|
42187
|
+
[
|
|
42188
|
+
"SKILL.detail.md",
|
|
42189
|
+
"overflow reference for `orchestration/feature-analyzer`, linked from its SKILL.md; carries no frontmatter and is not addressed by any runtime"
|
|
42190
|
+
]
|
|
42191
|
+
]);
|
|
42192
|
+
function walkSkillDocuments(root, accept) {
|
|
42061
42193
|
if (!existsSync23(root))
|
|
42062
42194
|
return [];
|
|
42063
42195
|
const out = [];
|
|
@@ -42068,26 +42200,40 @@ function bundledSkillFiles(root) {
|
|
|
42068
42200
|
walk4(full);
|
|
42069
42201
|
continue;
|
|
42070
42202
|
}
|
|
42071
|
-
if (entry.name
|
|
42203
|
+
if (accept(entry.name))
|
|
42072
42204
|
out.push(full);
|
|
42073
42205
|
}
|
|
42074
42206
|
};
|
|
42075
42207
|
walk4(root);
|
|
42076
42208
|
return out.sort();
|
|
42077
42209
|
}
|
|
42210
|
+
function bundledSkillFiles(root) {
|
|
42211
|
+
return walkSkillDocuments(root, (name) => name === "SKILL.md");
|
|
42212
|
+
}
|
|
42213
|
+
function bundledSkillDocuments(root) {
|
|
42214
|
+
return walkSkillDocuments(root, (name) => SKILL_DOCUMENT_NAMES.has(name));
|
|
42215
|
+
}
|
|
42078
42216
|
function defaultBundledRoot() {
|
|
42079
|
-
|
|
42217
|
+
const directPath = path125.join(import.meta.dir, "bundled");
|
|
42218
|
+
if (existsSync23(directPath))
|
|
42219
|
+
return directPath;
|
|
42220
|
+
const packagedPath = path125.join(import.meta.dir, "..", "src", "gdskills", "bundled");
|
|
42221
|
+
if (existsSync23(packagedPath))
|
|
42222
|
+
return packagedPath;
|
|
42223
|
+
return directPath;
|
|
42080
42224
|
}
|
|
42081
42225
|
function evaluateBundledTree(root = defaultBundledRoot()) {
|
|
42082
42226
|
const skillsRoot = path125.join(root, "skills");
|
|
42083
|
-
const
|
|
42084
|
-
const
|
|
42227
|
+
const canonical = bundledSkillFiles(skillsRoot);
|
|
42228
|
+
const files = bundledSkillDocuments(skillsRoot);
|
|
42229
|
+
const skillNames = [...new Set(canonical.map((file) => path125.basename(path125.dirname(file))))].sort();
|
|
42085
42230
|
const known = new Set(skillNames);
|
|
42086
42231
|
const findings = [];
|
|
42087
42232
|
const declaredNames = new Map;
|
|
42088
42233
|
const catalogued = new Set(BUNDLED_GDSKILLS.map((entry) => `${entry.category}/${entry.name}`));
|
|
42089
42234
|
for (const file of files) {
|
|
42090
|
-
const
|
|
42235
|
+
const skillDir = path125.dirname(file);
|
|
42236
|
+
const skill2 = path125.basename(skillDir);
|
|
42091
42237
|
const rel = path125.relative(skillsRoot, file).split(path125.sep).join("/");
|
|
42092
42238
|
const text = readFileSync8(file, "utf8");
|
|
42093
42239
|
const add = (check, line, message2) => {
|
|
@@ -42110,10 +42256,10 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
|
|
|
42110
42256
|
const declaredName = name === undefined ? undefined : name.replace(/^["']|["']$/g, "");
|
|
42111
42257
|
if (declaredName !== undefined && declaredName.length > 0) {
|
|
42112
42258
|
const previous = declaredNames.get(declaredName);
|
|
42113
|
-
if (previous
|
|
42114
|
-
|
|
42115
|
-
} else {
|
|
42116
|
-
|
|
42259
|
+
if (previous === undefined) {
|
|
42260
|
+
declaredNames.set(declaredName, { dir: skillDir, file: rel });
|
|
42261
|
+
} else if (previous.dir !== skillDir) {
|
|
42262
|
+
add("frontmatter:name-unique", 1, `frontmatter \`name: ${declaredName}\` is already declared by \`${previous.file}\`; a harness registers skills by this name and cannot tell two of them apart.`);
|
|
42117
42263
|
}
|
|
42118
42264
|
}
|
|
42119
42265
|
const description = keys.get("description");
|
|
@@ -42171,7 +42317,26 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
|
|
|
42171
42317
|
}
|
|
42172
42318
|
});
|
|
42173
42319
|
}
|
|
42174
|
-
|
|
42320
|
+
for (const dir of [...new Set(files.map((file) => path125.dirname(file)))].sort()) {
|
|
42321
|
+
for (const entry of readdirSync2(dir, { withFileTypes: true })) {
|
|
42322
|
+
if (!entry.isFile())
|
|
42323
|
+
continue;
|
|
42324
|
+
if (!/^SKILL\..+\.md$/.test(entry.name))
|
|
42325
|
+
continue;
|
|
42326
|
+
if (SKILL_DOCUMENT_NAMES.has(entry.name))
|
|
42327
|
+
continue;
|
|
42328
|
+
if (KNOWN_SKILL_COMPANION_DOCUMENTS.has(entry.name))
|
|
42329
|
+
continue;
|
|
42330
|
+
findings.push({
|
|
42331
|
+
check: "document:addressable",
|
|
42332
|
+
skill: path125.basename(dir),
|
|
42333
|
+
file: path125.relative(skillsRoot, path125.join(dir, entry.name)).split(path125.sep).join("/"),
|
|
42334
|
+
line: null,
|
|
42335
|
+
message: `\`${entry.name}\` is spelled like a harness build, but no runtime in HARNESS_SKILL_RUNTIMES resolves to it (${[...SKILL_DOCUMENT_NAMES].join(", ")}). Nothing exports it, installs it, or reads it \u2014 delete it, rename it to the build it was meant to be, or register it in KNOWN_SKILL_COMPANION_DOCUMENTS with a reason.`
|
|
42336
|
+
});
|
|
42337
|
+
}
|
|
42338
|
+
}
|
|
42339
|
+
return { root, skills: canonical.length, documents: files.length, skillNames, findings };
|
|
42175
42340
|
}
|
|
42176
42341
|
function renderBundledEvaluation(evaluation) {
|
|
42177
42342
|
const lines = [];
|
|
@@ -42179,9 +42344,10 @@ function renderBundledEvaluation(evaluation) {
|
|
|
42179
42344
|
lines.push("");
|
|
42180
42345
|
lines.push(`root: ${evaluation.root}`);
|
|
42181
42346
|
lines.push(`skills_evaluated: ${evaluation.skills}`);
|
|
42347
|
+
lines.push(`documents_evaluated: ${evaluation.documents} (SKILL.md + harness builds)`);
|
|
42182
42348
|
lines.push(`findings: ${evaluation.findings.length}`);
|
|
42183
42349
|
lines.push("");
|
|
42184
|
-
if (evaluation.
|
|
42350
|
+
if (evaluation.documents === 0) {
|
|
42185
42351
|
lines.push("NOTHING WAS EVALUATED. The root holds no SKILL.md, so `findings: 0` means the sweep walked an empty tree \u2014 not that the tree is clean.");
|
|
42186
42352
|
return lines.join(`
|
|
42187
42353
|
`);
|
|
@@ -44029,6 +44195,8 @@ async function flowCommand(args2) {
|
|
|
44029
44195
|
return await runSimple(args2.slice(1), "freeze");
|
|
44030
44196
|
case "start":
|
|
44031
44197
|
return await runSimple(args2.slice(1), "start");
|
|
44198
|
+
case "next":
|
|
44199
|
+
return await runNext(args2.slice(1));
|
|
44032
44200
|
case "task":
|
|
44033
44201
|
return await runTask(args2.slice(1));
|
|
44034
44202
|
case "ac":
|
|
@@ -44159,13 +44327,46 @@ async function runStatus4(args2) {
|
|
|
44159
44327
|
const doneCount = flow.tasks.filter((task) => task.status === "done").length;
|
|
44160
44328
|
heading(`Tasks (${doneCount}/${flow.tasks.length})`);
|
|
44161
44329
|
for (const task of flow.tasks) {
|
|
44162
|
-
|
|
44330
|
+
const attempts = task.attempts?.count ?? 0;
|
|
44331
|
+
const declared = task.dependsOn ?? [];
|
|
44332
|
+
const annotations = [
|
|
44333
|
+
...declared.length === 0 ? [] : [`depends on ${declared.join(", ")}`],
|
|
44334
|
+
...attempts === 0 ? [] : [`${attempts} attempt(s)`]
|
|
44335
|
+
];
|
|
44336
|
+
statusLine(`${task.id} ${task.title}${annotations.length === 0 ? "" : ` ${style.dim(`[${annotations.join("; ")}]`)}`}`, task.status === "done", task.kind);
|
|
44163
44337
|
}
|
|
44164
44338
|
heading("Recent history");
|
|
44165
44339
|
for (const event of flow.history.slice(-5)) {
|
|
44166
44340
|
console.log(` ${style.dim(event.at)} ${event.event}${event.detail ? style.dim(`: ${event.detail}`) : ""}`);
|
|
44167
44341
|
}
|
|
44168
44342
|
}
|
|
44343
|
+
async function runNext(args2) {
|
|
44344
|
+
const id = requireId(args2);
|
|
44345
|
+
const decision = await getService3().next({ cwd: process.cwd(), id });
|
|
44346
|
+
if (args2.includes("--json")) {
|
|
44347
|
+
console.log(JSON.stringify(decision, null, 2));
|
|
44348
|
+
if (decision.kind === "blocked") {
|
|
44349
|
+
process.exitCode = 1;
|
|
44350
|
+
}
|
|
44351
|
+
return;
|
|
44352
|
+
}
|
|
44353
|
+
if (decision.kind === "ready") {
|
|
44354
|
+
console.log(` ${style.cyan(symbols.arrow)} ${style.bold(decision.task.id)} ${decision.task.title} ${style.dim(`(${decision.task.kind})`)}`);
|
|
44355
|
+
const declared = decision.task.dependsOn ?? [];
|
|
44356
|
+
note(declared.length === 0 ? "no declared dependencies; this is the first task that is not done" : `all declared dependencies are done: ${declared.join(", ")}`);
|
|
44357
|
+
return;
|
|
44358
|
+
}
|
|
44359
|
+
if (decision.kind === "none") {
|
|
44360
|
+
console.log(` ${style.green(symbols.ok)} Every task is done.`);
|
|
44361
|
+
return;
|
|
44362
|
+
}
|
|
44363
|
+
heading(`${style.red(symbols.cross)} ${decision.blocked.length} task(s) remain and none can start`);
|
|
44364
|
+
for (const entry of decision.blocked) {
|
|
44365
|
+
console.log(` ${style.red(symbols.cross)} ${style.bold(entry.task.id)} ${entry.task.title} ${style.dim(`waiting on ${entry.waitingOn.join(", ")}`)}`);
|
|
44366
|
+
}
|
|
44367
|
+
note("A dependency that is not done, does not exist, or forms a cycle. `keryx flow check` names which.");
|
|
44368
|
+
process.exitCode = 1;
|
|
44369
|
+
}
|
|
44169
44370
|
async function runSimple(args2, action) {
|
|
44170
44371
|
const id = requireId(args2);
|
|
44171
44372
|
const flow = await getService3()[action]({ cwd: process.cwd(), id });
|
|
@@ -44367,6 +44568,7 @@ function printHelp10() {
|
|
|
44367
44568
|
"keryx flow status <id>",
|
|
44368
44569
|
"keryx flow freeze <id>",
|
|
44369
44570
|
"keryx flow start <id>",
|
|
44571
|
+
"keryx flow next <id> [--json] (first task not done whose dependsOn are all done)",
|
|
44370
44572
|
'keryx flow task add <id> --title "<t>" [--kind context|implement|test|verify|review|docs] [--depends T1,T2]',
|
|
44371
44573
|
'keryx flow task done <id> <taskId> [--disposition completed|blocked|failed|skipped] [--reason "<why>"]',
|
|
44372
44574
|
'keryx flow task attempt <id> <taskId> --outcome started|failed|blocked [--detail "<what happened>"]',
|
|
@@ -44486,7 +44688,7 @@ var IMPLEMENT = [
|
|
|
44486
44688
|
{ id: "implement", type: "implement", agent: "task-implementer", depends: ["tests-creator"] },
|
|
44487
44689
|
{ id: "sanity-check", type: "check", agent: "orchestrator", depends: ["implement"] },
|
|
44488
44690
|
{ id: "verify", type: "verify", agent: "code-verifier", depends: ["sanity-check"] },
|
|
44489
|
-
{ id: "review", type: "review", agent: "
|
|
44691
|
+
{ id: "review", type: "review", agent: "review-orchestrator", depends: ["verify"] },
|
|
44490
44692
|
{ id: "security", type: "security", agent: "security-audit", depends: ["implement"], conditional: true },
|
|
44491
44693
|
{ id: "fix", type: "fix", agent: "task-implementer", depends: ["review"], conditional: true },
|
|
44492
44694
|
{ id: "verify-post-fix", type: "verify", agent: "code-verifier", depends: ["fix"], conditional: true },
|
|
@@ -47544,6 +47746,7 @@ function packagePath(cwd, mode, reviewId, flowMatch) {
|
|
|
47544
47746
|
return path133.join(reviewsRoot(cwd), reviewId);
|
|
47545
47747
|
}
|
|
47546
47748
|
function buildManifest2(args2) {
|
|
47749
|
+
const crossFamily = args2.input.crossFamilyReview;
|
|
47547
47750
|
const artifactPath = (name) => path133.relative(args2.input.cwd, path133.join(args2.packageDir, name));
|
|
47548
47751
|
const manifest = {
|
|
47549
47752
|
schemaVersion: 1,
|
|
@@ -47561,6 +47764,7 @@ function buildManifest2(args2) {
|
|
|
47561
47764
|
},
|
|
47562
47765
|
coverage: args2.coverage,
|
|
47563
47766
|
filter_stats: args2.filterStats,
|
|
47767
|
+
...crossFamily === undefined ? {} : { cross_family_review: crossFamily },
|
|
47564
47768
|
createdAt: args2.at,
|
|
47565
47769
|
updatedAt: args2.at
|
|
47566
47770
|
};
|
|
@@ -48282,6 +48486,94 @@ async function loadDocpackSchema(cwd) {
|
|
|
48282
48486
|
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed : null;
|
|
48283
48487
|
}
|
|
48284
48488
|
|
|
48489
|
+
// src/review/cross-family.ts
|
|
48490
|
+
var CROSS_FAMILY_MODES = ["cross-family", "single-family"];
|
|
48491
|
+
function parseCrossFamilyReviewInput(raw, source) {
|
|
48492
|
+
const candidate = typeof raw === "object" && raw !== null && !Array.isArray(raw) && "cross_family_review" in raw ? raw.cross_family_review : raw;
|
|
48493
|
+
const problems = checkCrossFamilyReview(candidate);
|
|
48494
|
+
if (problems.length > 0) {
|
|
48495
|
+
throw new Error(`${source} is not a usable cross_family_review block: ${problems.map((problem) => `[${problem.code}] ${problem.message}`).join("; ")}. Produce one with \`keryx providers cross-family --json\`.`);
|
|
48496
|
+
}
|
|
48497
|
+
return candidate;
|
|
48498
|
+
}
|
|
48499
|
+
function checkCrossFamilyReview(value) {
|
|
48500
|
+
if (value === undefined || value === null) {
|
|
48501
|
+
return [
|
|
48502
|
+
{
|
|
48503
|
+
code: "not-recorded",
|
|
48504
|
+
message: "cross_family_review: not recorded. The round did not run `keryx providers cross-family`, or the package predates the field. This is NOT `single-family` \u2014 nobody decided."
|
|
48505
|
+
}
|
|
48506
|
+
];
|
|
48507
|
+
}
|
|
48508
|
+
if (typeof value !== "object" || Array.isArray(value)) {
|
|
48509
|
+
return [{ code: "shape", message: "cross_family_review is not an object." }];
|
|
48510
|
+
}
|
|
48511
|
+
const record = value;
|
|
48512
|
+
const problems = [];
|
|
48513
|
+
const shape = (message2) => {
|
|
48514
|
+
problems.push({ code: "shape", message: message2 });
|
|
48515
|
+
};
|
|
48516
|
+
const contradiction = (message2) => {
|
|
48517
|
+
problems.push({ code: "contradiction", message: message2 });
|
|
48518
|
+
};
|
|
48519
|
+
if (record.schemaVersion !== 1) {
|
|
48520
|
+
shape(`schemaVersion must be 1; found ${JSON.stringify(record.schemaVersion)}.`);
|
|
48521
|
+
}
|
|
48522
|
+
const mode = record.mode;
|
|
48523
|
+
if (typeof mode !== "string" || !CROSS_FAMILY_MODES.includes(mode)) {
|
|
48524
|
+
shape(`mode must be one of ${CROSS_FAMILY_MODES.join(", ")}; found ${JSON.stringify(mode)}.`);
|
|
48525
|
+
}
|
|
48526
|
+
if (typeof record.requested !== "boolean") {
|
|
48527
|
+
shape(`requested must be a boolean; found ${JSON.stringify(record.requested)}.`);
|
|
48528
|
+
}
|
|
48529
|
+
for (const field3 of ["author_family", "reviewer_family", "reviewer_provider", "reviewer_model"]) {
|
|
48530
|
+
const held = record[field3];
|
|
48531
|
+
if (held !== null && typeof held !== "string") {
|
|
48532
|
+
shape(`${field3} must be a string or null; found ${JSON.stringify(held)}.`);
|
|
48533
|
+
}
|
|
48534
|
+
}
|
|
48535
|
+
if (typeof record.reason !== "string" || record.reason.trim() === "") {
|
|
48536
|
+
shape("reason must be a non-empty string \u2014 the decision is unreadable later without it.");
|
|
48537
|
+
}
|
|
48538
|
+
const candidates = record.candidates;
|
|
48539
|
+
if (!Array.isArray(candidates)) {
|
|
48540
|
+
shape(`candidates must be an array; found ${JSON.stringify(candidates)}.`);
|
|
48541
|
+
}
|
|
48542
|
+
if (problems.length > 0) {
|
|
48543
|
+
return problems;
|
|
48544
|
+
}
|
|
48545
|
+
const reviewerFamily = record.reviewer_family;
|
|
48546
|
+
const reviewerProvider = record.reviewer_provider;
|
|
48547
|
+
const authorFamily = record.author_family;
|
|
48548
|
+
const options = candidates;
|
|
48549
|
+
if (mode === "cross-family") {
|
|
48550
|
+
if (record.requested !== true) {
|
|
48551
|
+
contradiction("mode is `cross-family` while requested is false. Cross-family review is opt-in; a granted review nobody asked for means the record was assembled rather than decided.");
|
|
48552
|
+
}
|
|
48553
|
+
if (reviewerFamily === null || reviewerProvider === null) {
|
|
48554
|
+
contradiction("mode is `cross-family` but the reviewer family or provider is null. `cross-family` without naming the other side cannot be grouped later, which is the whole reason the block exists.");
|
|
48555
|
+
}
|
|
48556
|
+
if (reviewerFamily !== null && reviewerFamily === authorFamily) {
|
|
48557
|
+
contradiction(`mode is \`cross-family\` and the reviewer family (${reviewerFamily}) is the family that authored the change. That is a single-family review recorded as a cross-family one.`);
|
|
48558
|
+
}
|
|
48559
|
+
if (reviewerFamily !== null && !options.some((option) => option.family === reviewerFamily && option.provider === reviewerProvider)) {
|
|
48560
|
+
contradiction(`the chosen reviewer (${reviewerFamily} via ${reviewerProvider}) is not among the ${options.length} candidate(s) recorded on the decision. A reviewer that was never an option was not chosen from them.`);
|
|
48561
|
+
}
|
|
48562
|
+
} else {
|
|
48563
|
+
if (reviewerFamily !== null || reviewerProvider !== null || record.reviewer_model !== null) {
|
|
48564
|
+
contradiction("mode is `single-family` while a reviewer family, provider or model is named. Single-family review has no other side; naming one says a dispatch happened that the mode denies.");
|
|
48565
|
+
}
|
|
48566
|
+
}
|
|
48567
|
+
return problems;
|
|
48568
|
+
}
|
|
48569
|
+
function renderCrossFamilyReviewLine(decision) {
|
|
48570
|
+
if (decision === undefined) {
|
|
48571
|
+
return "cross_family_review: not recorded (no --cross-family-review supplied to this ingest; this is NOT `single-family`)";
|
|
48572
|
+
}
|
|
48573
|
+
const reviewer = decision.mode === "cross-family" ? `${decision.reviewer_family ?? "?"} via ${decision.reviewer_provider ?? "?"}${decision.reviewer_model === null ? "" : ` (${decision.reviewer_model})`}` : "none (single-family review)";
|
|
48574
|
+
return `cross_family_review: mode=${decision.mode} requested=${decision.requested ? "yes" : "no"} author_family=${decision.author_family ?? "not classified"} reviewer=${reviewer} candidates=${decision.candidates.length}`;
|
|
48575
|
+
}
|
|
48576
|
+
|
|
48285
48577
|
// src/commands/review.ts
|
|
48286
48578
|
init_github();
|
|
48287
48579
|
init_query();
|
|
@@ -48706,6 +48998,7 @@ var CREATE_FLAGS = [
|
|
|
48706
48998
|
"--verification-mode",
|
|
48707
48999
|
"--scope",
|
|
48708
49000
|
"--blast-radius",
|
|
49001
|
+
"--cross-family-review",
|
|
48709
49002
|
"--refuted",
|
|
48710
49003
|
"--max-findings",
|
|
48711
49004
|
"--spent",
|
|
@@ -48901,6 +49194,7 @@ async function runCreate(mode, args2) {
|
|
|
48901
49194
|
verificationMode: parseVerificationMode(optionValue(args2, "--verification-mode")),
|
|
48902
49195
|
scope: await readScope(optionValue(args2, "--scope")),
|
|
48903
49196
|
blastRadius: await readBlastRadiusRecord(optionValue(args2, "--blast-radius")),
|
|
49197
|
+
crossFamilyReview: await readCrossFamilyReview(optionValue(args2, "--cross-family-review")),
|
|
48904
49198
|
maxFindingsPerReviewer: parseNonNegativeInteger(optionValue(args2, "--max-findings"), "--max-findings"),
|
|
48905
49199
|
spend: parseMoney(optionValue(args2, "--spent"), "--spent"),
|
|
48906
49200
|
spendCeiling: parseMoney(optionValue(args2, "--spend-ceiling"), "--spend-ceiling"),
|
|
@@ -48929,6 +49223,7 @@ async function runCreate(mode, args2) {
|
|
|
48929
49223
|
console.log(preFilter === undefined ? "pre-filter: not recorded (no --scope supplied; this is not `dropped 0`)" : `pre-filter: files_dropped=${preFilter.counts.filesDropped} blocks_dropped=${preFilter.counts.blocksDropped} changed_lines_dropped=${preFilter.counts.changedLinesDropped} drop_rows=${preFilter.drops.length}`);
|
|
48930
49224
|
console.log(renderFilterStatsLine(result.filterStats));
|
|
48931
49225
|
reportFilterStatsProblems(result.filterStats, `${result.path}/manifest.json`);
|
|
49226
|
+
console.log(renderCrossFamilyReviewLine(result.manifest.cross_family_review));
|
|
48932
49227
|
const caps = result.caps;
|
|
48933
49228
|
const findingsCap = caps.findings;
|
|
48934
49229
|
if (findingsCap !== undefined) {
|
|
@@ -49494,6 +49789,19 @@ async function readBlastRadiusRecord(source) {
|
|
|
49494
49789
|
}
|
|
49495
49790
|
return { ...parsed, files: parsed.files, changedFiles: parsed.changedFiles };
|
|
49496
49791
|
}
|
|
49792
|
+
async function readCrossFamilyReview(source) {
|
|
49793
|
+
if (source === undefined) {
|
|
49794
|
+
return;
|
|
49795
|
+
}
|
|
49796
|
+
const raw = source === "-" ? await Bun.stdin.text() : await Bun.file(source).text();
|
|
49797
|
+
let parsed;
|
|
49798
|
+
try {
|
|
49799
|
+
parsed = JSON.parse(raw);
|
|
49800
|
+
} catch (error2) {
|
|
49801
|
+
throw new Error(`--cross-family-review ${source} is not JSON: ${error2 instanceof Error ? error2.message : String(error2)}. Pass the output of \`keryx providers cross-family --json\`.`);
|
|
49802
|
+
}
|
|
49803
|
+
return parseCrossFamilyReviewInput(parsed, `--cross-family-review ${source}`);
|
|
49804
|
+
}
|
|
49497
49805
|
function parseVerificationMode(raw) {
|
|
49498
49806
|
if (raw === undefined) {
|
|
49499
49807
|
return DEFAULT_VERIFICATION_MODE;
|
|
@@ -49665,6 +49973,8 @@ async function runStatus6(args2) {
|
|
|
49665
49973
|
console.log(`head: ${manifest.target.head ?? "not recorded (`flow complete` will refuse this round)"}`);
|
|
49666
49974
|
console.log(`flow: ${manifest.flow?.id ?? "none"}`);
|
|
49667
49975
|
console.log(`coverage: ${manifest.coverage.length}`);
|
|
49976
|
+
console.log(renderCrossFamilyReviewLine(manifest.cross_family_review));
|
|
49977
|
+
reportCrossFamilyReviewProblems(manifest.cross_family_review, `${ref}/manifest.json`);
|
|
49668
49978
|
const stats = manifest.filter_stats;
|
|
49669
49979
|
if (stats === undefined) {
|
|
49670
49980
|
console.log("filter_stats: not recorded \u2014 this package predates it. Re-ingest the round to record what it filtered.");
|
|
@@ -49679,6 +49989,16 @@ async function runStatus6(args2) {
|
|
|
49679
49989
|
}
|
|
49680
49990
|
reportFilterStatsProblems(stats, `${ref}/manifest.json`);
|
|
49681
49991
|
}
|
|
49992
|
+
function reportCrossFamilyReviewProblems(decision, where) {
|
|
49993
|
+
const problems = checkCrossFamilyReview(decision).filter((problem) => problem.code !== "not-recorded");
|
|
49994
|
+
if (problems.length === 0) {
|
|
49995
|
+
return;
|
|
49996
|
+
}
|
|
49997
|
+
for (const problem of problems) {
|
|
49998
|
+
console.error(`cross_family_review [${problem.code}] in ${where}: ${problem.message}`);
|
|
49999
|
+
}
|
|
50000
|
+
process.exitCode = 1;
|
|
50001
|
+
}
|
|
49682
50002
|
function reportFilterStatsProblems(stats, where) {
|
|
49683
50003
|
const problems = checkFilterStats(stats).filter((problem) => problem.code !== "not-recorded");
|
|
49684
50004
|
if (problems.length === 0) {
|
|
@@ -49767,6 +50087,7 @@ Usage:
|
|
|
49767
50087
|
keryx review ingest --report <path> [--flow <id>] --ref <ref> [--head <sha>]
|
|
49768
50088
|
[--verifications <file|->] [--verification-mode ${VERIFICATION_MODES.join("|")}]
|
|
49769
50089
|
[--scope <scope.json>] [--blast-radius <blast-radius.json>]
|
|
50090
|
+
[--cross-family-review <file|->]
|
|
49770
50091
|
[--refuted <file|->]
|
|
49771
50092
|
[--max-findings <n>] [--spent <usd>] [--spend-ceiling <usd>]
|
|
49772
50093
|
[--parallel <n>] [--outstanding <n>]
|
|
@@ -49831,6 +50152,15 @@ blast-radius:
|
|
|
49831
50152
|
Requires a built graph (\`keryx gdgraph build\`); an absent graph is refused
|
|
49832
50153
|
rather than reported as an empty radius.
|
|
49833
50154
|
|
|
50155
|
+
cross-family-review:
|
|
50156
|
+
The block \`keryx providers cross-family --json\` prints: which model family
|
|
50157
|
+
reviewed this round, and why. Recorded on \`manifest.cross_family_review\` and
|
|
50158
|
+
read back \u2014 and CHECKED \u2014 by \`keryx review status\`, which exits non-zero on a
|
|
50159
|
+
record that contradicts itself (\`cross-family\` naming no reviewer, naming the
|
|
50160
|
+
authoring family, or naming a reviewer that was never a candidate).
|
|
50161
|
+
Omitted, the round records NOTHING, and \`status\` says \`not recorded\`.
|
|
50162
|
+
That is not \`single-family\`: nobody decided.
|
|
50163
|
+
|
|
49834
50164
|
complete:
|
|
49835
50165
|
--finding/--disposition/--evidence record what became of a named finding, and
|
|
49836
50166
|
are repeatable. States: ${FINDING_DISPOSITION_STATES.join(", ")}.
|
|
@@ -62549,7 +62879,7 @@ import { spawnSync as spawnSync2 } from "child_process";
|
|
|
62549
62879
|
// package.json
|
|
62550
62880
|
var package_default = {
|
|
62551
62881
|
name: "@mrciphersmith/keryx",
|
|
62552
|
-
version: "0.2.
|
|
62882
|
+
version: "0.2.73",
|
|
62553
62883
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
62554
62884
|
private: false,
|
|
62555
62885
|
publishConfig: {
|
|
@@ -62591,7 +62921,7 @@ var package_default = {
|
|
|
62591
62921
|
check: "tsc --noEmit && bun test",
|
|
62592
62922
|
"check:doc-links": "bun scripts/check-doc-links.ts",
|
|
62593
62923
|
"baseline:review-precision": "bun scripts/review-precision-baseline.ts",
|
|
62594
|
-
"test:guards": "bun test src/lib/config-dir.ast.test.ts src/lib/config-dir.readers.test.ts src/lib/production-graph.test.ts src/harness/policy/profiles.test.ts src/lib/serve-server.test.ts"
|
|
62924
|
+
"test:guards": "bun test src/lib/config-dir.ast.test.ts src/lib/config-dir.readers.test.ts src/lib/production-graph.test.ts src/harness/policy/profiles.test.ts src/lib/serve-server.test.ts src/gdskills/agent-catalogue-xref.test.ts src/gdskills/enforcement-claims.test.ts"
|
|
62595
62925
|
},
|
|
62596
62926
|
files: [
|
|
62597
62927
|
"dist",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrciphersmith/keryx",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.73",
|
|
4
4
|
"description": "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
"check": "tsc --noEmit && bun test",
|
|
43
43
|
"check:doc-links": "bun scripts/check-doc-links.ts",
|
|
44
44
|
"baseline:review-precision": "bun scripts/review-precision-baseline.ts",
|
|
45
|
-
"test:guards": "bun test src/lib/config-dir.ast.test.ts src/lib/config-dir.readers.test.ts src/lib/production-graph.test.ts src/harness/policy/profiles.test.ts src/lib/serve-server.test.ts"
|
|
45
|
+
"test:guards": "bun test src/lib/config-dir.ast.test.ts src/lib/config-dir.readers.test.ts src/lib/production-graph.test.ts src/harness/policy/profiles.test.ts src/lib/serve-server.test.ts src/gdskills/agent-catalogue-xref.test.ts src/gdskills/enforcement-claims.test.ts"
|
|
46
46
|
},
|
|
47
47
|
"files": [
|
|
48
48
|
"dist",
|
|
@@ -33,7 +33,7 @@ Every dispatch from gproject-orchestrator MUST include:
|
|
|
33
33
|
```yaml
|
|
34
34
|
# Required (from base protocol)
|
|
35
35
|
description: "<phase>: <task>"
|
|
36
|
-
subagent_type: "general"
|
|
36
|
+
subagent_type: "general-purpose"
|
|
37
37
|
prompt: "<structured prompt — see template below>"
|
|
38
38
|
|
|
39
39
|
# gproject-specific (inside prompt)
|
|
@@ -80,7 +80,7 @@ Every job folder MUST contain at minimum:
|
|
|
80
80
|
...
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
-
> **`results/` folder:**
|
|
83
|
+
> **`results/` folder:** Written by `task-implementer` during Phase 6.1 (one file per task), then validated with `keryx skills contracts validate … --schema task-implementer-output` and recorded with `keryx job document <job-name> --type implementation-report --file <that file>`, which refuses if the file was never written. There is no `wave-executor` agent: the orchestrator dispatches each task itself, never receives a full task result inline, and reads a `results/` file only when a task reports `DONE_WITH_CONCERNS` or `BLOCKED`. This keeps the orchestrator context bounded.
|
|
84
84
|
|
|
85
85
|
## README.md Format
|
|
86
86
|
|