karajan-code 4.58.0 → 4.59.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "karajan-code",
3
- "version": "4.58.0",
3
+ "version": "4.59.0",
4
4
  "description": "Local multi-agent coding orchestrator with TDD, SonarQube, and code review pipeline",
5
5
  "type": "module",
6
6
  "license": "AGPL-3.0",
@@ -262,6 +262,7 @@ export function registerPipeline(program, { pkgVersion }) {
262
262
  .option("--verify-pr <n>", "CI check: every commit of the PR is listed, with its diff hash, in the kj review a member published")
263
263
  .option("--third-party", "Review someone else's PR (--range base...head): reviewer only, no own-work gates, no verdict stored")
264
264
  .option("--json", "With --third-party: print the review as one JSON document on stdout")
265
+ .option("--lens <lens>", "With --third-party: what the reviewer looks at; qa = test coverage, weak assertions, flakiness, test data")
265
266
  .action(async (task, flags) => {
266
267
  await withConfig(pkgVersion, "review", flags, async ({ config, logger }) => {
267
268
  // KJC-TSK-1075 (#2227): a third-party PR, for reviewer agents; own work keeps every gate.
@@ -82,6 +82,16 @@ const SEMVER_JOIN = String.raw`\s*(?:(?:\|\||-)\s*${SEMVER_OP}?|${SEMVER_OP})\s*
82
82
  const SEMVER_VALUE = new RegExp(String.raw`^\s*${SEMVER_OP}?\s*${SEMVER_TOKEN}(?:${SEMVER_JOIN}${SEMVER_TOKEN})*\s*$`);
83
83
 
84
84
  /** KJC-BUG-0380: a pair is discarded only when it is a real latitude, longitude. */
85
+ // KJC-TSK-1118: the test card numbers payment providers publish (Stripe, Braintree,
86
+ // Adyen, PayPal docs). Only these exact numbers; a card one digit away still warns.
87
+ const TEST_CARDS = new Set([
88
+ "4242424242424242", "4000056655665556", "4111111111111111", "4012888888881881", "4000000000000002",
89
+ "5555555555554444", "5105105105105100", "2223003122003222", "5200828282828210",
90
+ "378282246310005", "371449635398431", "6011111111111117", "6011000990139424",
91
+ "3056930009020004", "36227206271667", "3530111333300000", "3566002020360505",
92
+ ]);
93
+ const isTestCard = (match) => TEST_CARDS.has(match.replaceAll(/[\s-]/g, ""));
94
+
85
95
  function inGpsRange(_match, lat, lng) {
86
96
  return Math.abs(Number(lat)) <= 90 && Math.abs(Number(lng)) <= 180;
87
97
  }
@@ -124,6 +134,14 @@ const CONTEXT_DISCARDS = [
124
134
  // range (latitude up to 90, longitude up to 180), joined by a comma. A lone
125
135
  // number or a pair out of range still warns.
126
136
  { type: "gps-pair", re: /(?<![\d.])(-?\d{1,3}\.\d{4,})\s*,\s*(-?\d{1,3}\.\d{4,})(?![\d.])/g, keep: inGpsRange },
137
+ // KJC-TSK-1118: numbers reserved for fiction and tests, the good practice for test data.
138
+ // NANP 555-0100..0199 (with an optional +1 and area code); Ofcom's drama ranges:
139
+ // 07700 900xxx, 020 7946 0xxx and 01632 960xxx (0 or +44). Nothing around them.
140
+ { type: "fictional-phone", re: /(?<![\d+])(?:\+?1[\s.-]?)?\(?\d{3}\)?[\s.-]?555[\s.-]?01\d{2}(?!\d)/g },
141
+ { type: "fictional-phone-uk", re: /(?<![\d+])(?:\+44\s?|0)7700\s?900\d{3}(?!\d)/g },
142
+ { type: "fictional-phone-uk", re: /(?<![\d+])(?:\+44\s?|0)20\s?7946\s?0\d{3}(?!\d)/g },
143
+ { type: "fictional-phone-uk", re: /(?<![\d+])(?:\+44\s?|0)1632\s?960\d{3}(?!\d)/g },
144
+ { type: "test-card", re: /(?<!\d)\d(?:[\s-]?\d){13,18}(?!\d)/g, keep: isTestCard },
127
145
  { type: "plan-id", re:/(?<![A-Za-z0-9])plan-20\d\d(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\d|3[01])(?:[01]\d|2[0-3])[0-5]\d[0-5]\d-[a-z0-9]{4}(?![A-Za-z0-9])/g },
128
146
  ];
129
147
 
@@ -0,0 +1,19 @@
1
+ /**
2
+ * KJC-TSK-1117: a review lens changes what the reviewer looks at, not how strict
3
+ * it is (that is review_mode). The qa lens puts a QA rubric before the task, so a
4
+ * PR of test automation is judged on whether its tests would catch bugs.
5
+ */
6
+ import { readFile } from "node:fs/promises";
7
+ import { join } from "node:path";
8
+ import { getTemplatesRoot } from "../utils/templates-root.js";
9
+
10
+ const RUBRICS = { qa: "reviewer-qa.md" };
11
+ export const LENSES = Object.keys(RUBRICS);
12
+
13
+ /** The reviewer's task under a lens; no lens leaves it as it is. */
14
+ export async function lensTask(lens, task) {
15
+ if (!lens) return task;
16
+ if (!Object.hasOwn(RUBRICS, lens)) throw new Error(`unknown review lens "${lens}": use ${LENSES.join(", ")}`);
17
+ const rubric = (await readFile(join(getTemplatesRoot(), "roles", RUBRICS[lens]), "utf8")).trim();
18
+ return task ? `${rubric}\n\n## Task\n\n${task}` : rubric;
19
+ }
@@ -14,6 +14,7 @@ import { isGenerated } from "./loc-budget.js";
14
14
  import { prSecurityChecks } from "./pr-security.js";
15
15
  import { sonarCheck } from "./pr-sonar.js";
16
16
  import { batchDiff, splitByFile } from "./diff-batches.js";
17
+ import { lensTask } from "./review-lens.js";
17
18
  import { DIFF_CLIP_CHARS } from "../prompts/diff-clip.js";
18
19
 
19
20
  // KJC-BUG-0378: a reviewer call per batch; past this many, files are left out and named.
@@ -91,7 +92,9 @@ export async function thirdPartyReviewCommand({ flags, config, out = process.std
91
92
  const log = (msg) => err.write(`${msg}\n`);
92
93
  const logger = { info: log, warn: log, error: log, debug: () => {} };
93
94
  try {
94
- print(await review({ range: flags.range, config, projectDir: config.projectDir || process.cwd(), logger, task: flags.task ?? null }));
95
+ // KJC-TSK-1117: a lens (qa) puts its rubric before the task.
96
+ const task = await lensTask(flags.lens ?? null, flags.task ?? null);
97
+ print(await review({ range: flags.range, config, projectDir: config.projectDir || process.cwd(), logger, task }));
95
98
  return 0;
96
99
  } catch (e) {
97
100
  print({ error: e.message });
@@ -0,0 +1,18 @@
1
+ # Review lens: QA
2
+
3
+ Review this PR as a QA engineer, not only as a developer. Most of it may be test code: judge whether the tests would catch the bugs they exist for.
4
+
5
+ ## Blocking (in the diff only)
6
+
7
+ - **No test for new behavior**: the change adds or changes behavior and no test exercises it.
8
+ - **Tests that cannot fail**: no assertion, assertions only on mocks, `toBeTruthy`/`assert True` on values that are always truthy, a caught exception that swallows the failure.
9
+ - **Flaky by construction**: fixed sleeps or timeouts instead of waiting on a condition, order dependence between tests, shared mutable state, real clock, randomness or network without control, retries that hide a failure.
10
+ - **Real personal data or production credentials in test data.**
11
+
12
+ ## Suggestions (non-blocking)
13
+
14
+ - **Missing scenarios**: name the edge and error cases the new tests skip (empty, boundary, invalid input, permissions, concurrency).
15
+ - **Weak assertions**: only a status code, only "element exists", snapshots standing in for a precise check.
16
+ - **Fragile selectors**: CSS chains, XPath by index or text that changes; prefer roles, labels or `data-testid`.
17
+ - **Test data**: prefer values reserved for testing (emails on `example.com`/`example.org`/`.test`, fictional phone ranges such as 555-01xx or 07700 900xxx, the test card numbers each payment provider publishes) or generated data (factories, faker), created and cleaned up by each test.
18
+ - **Structure**: one behavior per test, names that say the scenario and the expected result, page objects and flows that keep selectors out of the test body, no duplicated setup.