@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.
Files changed (126) hide show
  1. package/dist/cli.js +352 -22
  2. package/package.json +2 -2
  3. package/src/gdskills/bundled/rules/core/gproject-contracts.mdc +1 -1
  4. package/src/gdskills/bundled/rules/core/jobs-documentation.mdc +1 -1
  5. package/src/gdskills/bundled/rules/core/subagent-context-construction.md +1 -1
  6. package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.codex.md +326 -20
  7. package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.cursor.md +320 -22
  8. package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.opencode.md +326 -12
  9. package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.zed.md +333 -9
  10. package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.codex.md +92 -4
  11. package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.cursor.md +92 -4
  12. package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.opencode.md +92 -4
  13. package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.zed.md +92 -4
  14. package/src/gdskills/bundled/skills/orchestration/context-collector/orchestrator-prompt.md +1 -1
  15. package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.codex.md +154 -1098
  16. package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.cursor.md +154 -1098
  17. package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.opencode.md +154 -1098
  18. package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.zed.md +154 -1098
  19. package/src/gdskills/bundled/skills/orchestration/feature-analyzer/orchestrator-prompt.md +1 -1
  20. package/src/gdskills/bundled/skills/orchestration/feature-dev/SKILL.codex.md +101 -41
  21. package/src/gdskills/bundled/skills/orchestration/feature-dev/SKILL.cursor.md +101 -41
  22. package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.codex.md +115 -49
  23. package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.cursor.md +115 -49
  24. package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.opencode.md +115 -49
  25. package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.zed.md +115 -49
  26. package/src/gdskills/bundled/skills/orchestration/issue-analyzer/orchestrator-prompt.md +1 -1
  27. package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.codex.md +15 -6
  28. package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.cursor.md +15 -6
  29. package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.opencode.md +15 -6
  30. package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.zed.md +15 -6
  31. package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.codex.md +1 -1
  32. package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.cursor.md +1 -1
  33. package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.md +1 -1
  34. package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.opencode.md +1 -1
  35. package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.zed.md +1 -1
  36. package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.codex.md +300 -55
  37. package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.cursor.md +300 -55
  38. package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.md +120 -37
  39. package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.opencode.md +300 -55
  40. package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.zed.md +300 -55
  41. package/src/gdskills/bundled/skills/orchestration/task-implementer/input-contract.schema.json +56 -14
  42. package/src/gdskills/bundled/skills/orchestration/task-implementer/orchestrator-prompt.md +50 -23
  43. package/src/gdskills/bundled/skills/orchestration/task-implementer/output-contract.schema.json +6 -2
  44. package/src/gdskills/bundled/skills/orchestration/task-implementer/task-request.template.md +18 -12
  45. package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.codex.md +1 -1
  46. package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.cursor.md +1 -1
  47. package/src/gdskills/bundled/skills/planning/consistency-checker/SKILL.codex.md +168 -9
  48. package/src/gdskills/bundled/skills/planning/consistency-checker/SKILL.cursor.md +168 -9
  49. package/src/gdskills/bundled/skills/planning/interview/SKILL.codex.md +7 -1
  50. package/src/gdskills/bundled/skills/planning/interview/SKILL.cursor.md +7 -1
  51. package/src/gdskills/bundled/skills/planning/interviewer/SKILL.codex.md +7 -1
  52. package/src/gdskills/bundled/skills/planning/interviewer/SKILL.cursor.md +7 -1
  53. package/src/gdskills/bundled/skills/planning/patterns-researcher/SKILL.codex.md +215 -9
  54. package/src/gdskills/bundled/skills/planning/patterns-researcher/SKILL.cursor.md +215 -9
  55. package/src/gdskills/bundled/skills/planning/planner/SKILL.codex.md +168 -9
  56. package/src/gdskills/bundled/skills/planning/planner/SKILL.cursor.md +168 -9
  57. package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.codex.md +2 -2
  58. package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.cursor.md +2 -2
  59. package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.opencode.md +2 -2
  60. package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.zed.md +2 -2
  61. package/src/gdskills/bundled/skills/planning/problem-definer/SKILL.codex.md +133 -9
  62. package/src/gdskills/bundled/skills/planning/problem-definer/SKILL.cursor.md +133 -9
  63. package/src/gdskills/bundled/skills/planning/project-discovery/SKILL.codex.md +145 -9
  64. package/src/gdskills/bundled/skills/planning/project-discovery/SKILL.cursor.md +145 -9
  65. package/src/gdskills/bundled/skills/planning/spec-writer/SKILL.codex.md +210 -9
  66. package/src/gdskills/bundled/skills/planning/spec-writer/SKILL.cursor.md +210 -9
  67. package/src/gdskills/bundled/skills/planning/stack-advisor/SKILL.codex.md +161 -9
  68. package/src/gdskills/bundled/skills/planning/stack-advisor/SKILL.cursor.md +161 -9
  69. package/src/gdskills/bundled/skills/platform/claude-md-management/SKILL.codex.md +1 -1
  70. package/src/gdskills/bundled/skills/platform/claude-md-management/SKILL.cursor.md +1 -1
  71. package/src/gdskills/bundled/skills/platform/hookify/SKILL.codex.md +1 -1
  72. package/src/gdskills/bundled/skills/platform/hookify/SKILL.cursor.md +1 -1
  73. package/src/gdskills/bundled/skills/quality/changelog/SKILL.codex.md +1 -1
  74. package/src/gdskills/bundled/skills/quality/changelog/SKILL.cursor.md +1 -1
  75. package/src/gdskills/bundled/skills/quality/commit/SKILL.codex.md +1 -1
  76. package/src/gdskills/bundled/skills/quality/commit/SKILL.cursor.md +1 -1
  77. package/src/gdskills/bundled/skills/quality/db-migrate/SKILL.codex.md +1 -1
  78. package/src/gdskills/bundled/skills/quality/db-migrate/SKILL.cursor.md +1 -1
  79. package/src/gdskills/bundled/skills/quality/dependency-update/SKILL.codex.md +1 -1
  80. package/src/gdskills/bundled/skills/quality/dependency-update/SKILL.cursor.md +1 -1
  81. package/src/gdskills/bundled/skills/quality/deploy/SKILL.codex.md +1 -1
  82. package/src/gdskills/bundled/skills/quality/deploy/SKILL.cursor.md +1 -1
  83. package/src/gdskills/bundled/skills/quality/perf-check/SKILL.codex.md +1 -1
  84. package/src/gdskills/bundled/skills/quality/perf-check/SKILL.cursor.md +1 -1
  85. package/src/gdskills/bundled/skills/quality/pr/SKILL.codex.md +1 -1
  86. package/src/gdskills/bundled/skills/quality/pr/SKILL.cursor.md +1 -1
  87. package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.codex.md +15 -1
  88. package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.cursor.md +248 -165
  89. package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.opencode.md +15 -1
  90. package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.zed.md +359 -19
  91. package/src/gdskills/bundled/skills/quality/push/SKILL.codex.md +1 -1
  92. package/src/gdskills/bundled/skills/quality/push/SKILL.cursor.md +1 -1
  93. package/src/gdskills/bundled/skills/quality/security-audit/SKILL.codex.md +1 -1
  94. package/src/gdskills/bundled/skills/quality/security-audit/SKILL.cursor.md +1 -1
  95. package/src/gdskills/bundled/skills/quality/test-gen/SKILL.codex.md +1 -1
  96. package/src/gdskills/bundled/skills/quality/test-gen/SKILL.cursor.md +1 -1
  97. package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.codex.md +299 -24
  98. package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.cursor.md +296 -31
  99. package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.opencode.md +309 -18
  100. package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.zed.md +312 -17
  101. package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.codex.md +29 -30
  102. package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.cursor.md +29 -30
  103. package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.opencode.md +29 -30
  104. package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.zed.md +29 -30
  105. package/src/gdskills/bundled/skills/review/code-learned-review/SKILL.codex.md +21 -30
  106. package/src/gdskills/bundled/skills/review/code-learned-review/SKILL.cursor.md +21 -30
  107. package/src/gdskills/bundled/skills/review/code-learned-review/SKILL.opencode.md +21 -30
  108. package/src/gdskills/bundled/skills/review/code-learned-review/SKILL.zed.md +21 -30
  109. package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.codex.md +19 -23
  110. package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.cursor.md +19 -23
  111. package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.opencode.md +19 -23
  112. package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.zed.md +19 -23
  113. package/src/gdskills/bundled/skills/review/code-style-review/SKILL.codex.md +17 -24
  114. package/src/gdskills/bundled/skills/review/code-style-review/SKILL.cursor.md +17 -24
  115. package/src/gdskills/bundled/skills/review/code-style-review/SKILL.opencode.md +17 -24
  116. package/src/gdskills/bundled/skills/review/code-style-review/SKILL.zed.md +17 -24
  117. package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.md +16 -3
  118. package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.claude.md +0 -46
  119. package/src/gdskills/bundled/skills/planning/interviewer/SKILL.claude.md +0 -94
  120. package/src/gdskills/bundled/skills/quality/changelog/SKILL.claude.md +0 -45
  121. package/src/gdskills/bundled/skills/quality/db-migrate/SKILL.claude.md +0 -40
  122. package/src/gdskills/bundled/skills/quality/dependency-update/SKILL.claude.md +0 -45
  123. package/src/gdskills/bundled/skills/quality/deploy/SKILL.claude.md +0 -42
  124. package/src/gdskills/bundled/skills/quality/perf-check/SKILL.claude.md +0 -48
  125. package/src/gdskills/bundled/skills/quality/security-audit/SKILL.claude.md +0 -40
  126. 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("Embed this block in the round's structured output so a later recall comparison can group rounds by the family that reviewed them.");
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.attempts ?? { count: 0, log: [] };
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
- function bundledSkillFiles(root) {
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 === "SKILL.md")
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
- return path125.join(import.meta.dir, "bundled");
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 files = bundledSkillFiles(skillsRoot);
42084
- const skillNames = [...new Set(files.map((file) => path125.basename(path125.dirname(file))))].sort();
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 skill2 = path125.basename(path125.dirname(file));
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 !== undefined) {
42114
- add("frontmatter:name-unique", 1, `frontmatter \`name: ${declaredName}\` is already declared by \`${previous}\`; a harness registers skills by this name and cannot tell two of them apart.`);
42115
- } else {
42116
- declaredNames.set(declaredName, rel);
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
- return { root, skills: files.length, skillNames, findings };
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.skills === 0) {
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
- statusLine(`${task.id} ${task.title}`, task.status === "done", task.kind);
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: "code-review", depends: ["verify"] },
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.72",
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.72",
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:** Created by `task-implementer` during Phase 6 (one file per task). The orchestrator and wave-executor never receive full task results inline — they read these files only when a task reports `DONE_WITH_CONCERNS` or `BLOCKED`. This keeps the orchestrator context bounded.
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
 
@@ -62,7 +62,7 @@ Use this template for every subagent dispatch:
62
62
  ```
63
63
  Task({
64
64
  description: "<one-line summary for logs>",
65
- subagent_type: "general",
65
+ subagent_type: "general-purpose",
66
66
  prompt: |
67
67
  ## Task
68
68
  <Exactly what to do — no ambiguity>