@mrciphersmith/keryx 0.3.20 → 0.3.22
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
CHANGED
|
@@ -9080,6 +9080,24 @@ var init_config = __esm(() => {
|
|
|
9080
9080
|
});
|
|
9081
9081
|
|
|
9082
9082
|
// src/security/detect/entropy.ts
|
|
9083
|
+
function isAdjacentLabel(content, start) {
|
|
9084
|
+
const before = lastLineOf(content.slice(Math.max(0, start - LABEL_WINDOW), start));
|
|
9085
|
+
if (ADJACENT_LABEL.test(before)) {
|
|
9086
|
+
return true;
|
|
9087
|
+
}
|
|
9088
|
+
if (before.trim().length > 0) {
|
|
9089
|
+
return false;
|
|
9090
|
+
}
|
|
9091
|
+
const lineStart = content.lastIndexOf(`
|
|
9092
|
+
`, start - 1);
|
|
9093
|
+
if (lineStart < 0) {
|
|
9094
|
+
return false;
|
|
9095
|
+
}
|
|
9096
|
+
const prevLineStart = content.lastIndexOf(`
|
|
9097
|
+
`, lineStart - 1) + 1;
|
|
9098
|
+
const prevLine = content.slice(prevLineStart, lineStart);
|
|
9099
|
+
return CONTINUATION_LABEL_LINE_RE.test(prevLine);
|
|
9100
|
+
}
|
|
9083
9101
|
function isHexBlob(value) {
|
|
9084
9102
|
return HEX_BLOB.test(value) && /[a-f]/i.test(value);
|
|
9085
9103
|
}
|
|
@@ -9279,12 +9297,19 @@ function looksSecretShapedIn(text) {
|
|
|
9279
9297
|
return secretShapedCandidatesIn(text).length > 0;
|
|
9280
9298
|
}
|
|
9281
9299
|
function labelledPieceQualifies(piece) {
|
|
9282
|
-
const base = bareShapeQualifies(piece);
|
|
9283
|
-
if (base.qualifies) {
|
|
9284
|
-
return { entropy: base.entropy };
|
|
9285
|
-
}
|
|
9286
9300
|
if (isAllowShapedValue(piece)) {
|
|
9287
|
-
return { entropy:
|
|
9301
|
+
return { entropy: shannonEntropy(piece) };
|
|
9302
|
+
}
|
|
9303
|
+
if (isHexBlob(piece)) {
|
|
9304
|
+
return { entropy: shannonEntropy(piece) };
|
|
9305
|
+
}
|
|
9306
|
+
if (!/[0-9]/.test(piece) && !/[+=]/.test(piece)) {
|
|
9307
|
+
return null;
|
|
9308
|
+
}
|
|
9309
|
+
const withoutSeparators = piece.replace(/[-_.+]/g, "");
|
|
9310
|
+
const entropy = shannonEntropy(withoutSeparators.length > 0 ? withoutSeparators : piece);
|
|
9311
|
+
if (entropy >= 3.6) {
|
|
9312
|
+
return { entropy };
|
|
9288
9313
|
}
|
|
9289
9314
|
return null;
|
|
9290
9315
|
}
|
|
@@ -9323,9 +9348,9 @@ function detectEntropy(content) {
|
|
|
9323
9348
|
if (urlSpans.some((u) => start < u.end && end > u.start)) {
|
|
9324
9349
|
continue;
|
|
9325
9350
|
}
|
|
9326
|
-
const
|
|
9327
|
-
if (
|
|
9328
|
-
matches.push(buildMatch(start, end, value, entropy));
|
|
9351
|
+
const found = labelledPieceQualifies(value);
|
|
9352
|
+
if (found !== null) {
|
|
9353
|
+
matches.push(buildMatch(start, end, value, found.entropy));
|
|
9329
9354
|
}
|
|
9330
9355
|
}
|
|
9331
9356
|
TOKEN.lastIndex = 0;
|
|
@@ -9340,8 +9365,7 @@ function detectEntropy(content) {
|
|
|
9340
9365
|
if (matches.some((existing) => start < existing.end && end > existing.start)) {
|
|
9341
9366
|
continue;
|
|
9342
9367
|
}
|
|
9343
|
-
|
|
9344
|
-
if (!ADJACENT_LABEL.test(before)) {
|
|
9368
|
+
if (!isAdjacentLabel(content, start)) {
|
|
9345
9369
|
continue;
|
|
9346
9370
|
}
|
|
9347
9371
|
const head = TOKEN_HEAD.exec(value)?.[0] ?? value;
|
|
@@ -9363,9 +9387,10 @@ function detectEntropy(content) {
|
|
|
9363
9387
|
}
|
|
9364
9388
|
return matches.sort((a, b) => a.start - b.start);
|
|
9365
9389
|
}
|
|
9366
|
-
var SENSITIVE_LABEL_WORDS = "key|secret|token|password|passwd|api|credential|auth|bearer", caseless = (words) => words.replace(/[a-z]/g, (ch) => `[${ch}${ch.toUpperCase()}]`), ADJACENT_LABEL, LABEL_ASSIGNMENT_RE, TOKEN, TOKEN_HEAD, LABEL_WINDOW = 40, HEX_BLOB, FULL_GIT_SHA_RE, UUID_RE, INTEGRITY_RE, TAG_MAX_LETTER_RUN = 8, SLUG_HEX_TAIL_RE, URL_SPAN_RE, URL_TRAILING_PUNCTUATION_RE, URL_COMPONENT_MIN_LENGTH = 20;
|
|
9390
|
+
var SENSITIVE_LABEL_WORDS = "key|secret|token|password|passwd|api|credential|auth|bearer", caseless = (words) => words.replace(/[a-z]/g, (ch) => `[${ch}${ch.toUpperCase()}]`), FILLER_WORDS = "is|was|set|to|the|value|now", ADJACENT_LABEL, CONTINUATION_LABEL_LINE_RE, LABEL_ASSIGNMENT_RE, TOKEN, TOKEN_HEAD, LABEL_WINDOW = 40, HEX_BLOB, FULL_GIT_SHA_RE, UUID_RE, INTEGRITY_RE, TAG_MAX_LETTER_RUN = 8, SLUG_HEX_TAIL_RE, URL_SPAN_RE, URL_TRAILING_PUNCTUATION_RE, URL_COMPONENT_MIN_LENGTH = 20;
|
|
9367
9391
|
var init_entropy = __esm(() => {
|
|
9368
|
-
ADJACENT_LABEL = new RegExp(`(?:^|[^A-Za-z0-9])(?:${caseless(SENSITIVE_LABEL_WORDS)})[A-Za-z0-9_-]
|
|
9392
|
+
ADJACENT_LABEL = new RegExp(`(?:^|[^A-Za-z0-9])(?:${caseless(SENSITIVE_LABEL_WORDS)})[A-Za-z0-9_-]*` + `(?:\\s+(?:${caseless(FILLER_WORDS)})){0,2}` + `["']?\\s*[:=]?\\s*["']?$`);
|
|
9393
|
+
CONTINUATION_LABEL_LINE_RE = new RegExp(`(?:^|[^A-Za-z0-9])(?:${caseless(SENSITIVE_LABEL_WORDS)})[A-Za-z0-9_-]*["']?\\s*[:=]?\\s*\\\\\\s*$`);
|
|
9369
9394
|
LABEL_ASSIGNMENT_RE = new RegExp(`(?:^|[^A-Za-z0-9])(?:${SENSITIVE_LABEL_WORDS})[A-Za-z0-9_-]*"?\\s*=\\s*"?([A-Za-z0-9+/_=-]{6,})`, "gi");
|
|
9370
9395
|
TOKEN = /[A-Za-z0-9+/=_-]{20,}(?:\.[A-Za-z0-9+/=_-]{6,})*/g;
|
|
9371
9396
|
TOKEN_HEAD = /^[A-Za-z0-9+/=_-]+/;
|
|
@@ -18093,7 +18118,7 @@ var package_default;
|
|
|
18093
18118
|
var init_package = __esm(() => {
|
|
18094
18119
|
package_default = {
|
|
18095
18120
|
name: "@mrciphersmith/keryx",
|
|
18096
|
-
version: "0.3.
|
|
18121
|
+
version: "0.3.22",
|
|
18097
18122
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
18098
18123
|
private: false,
|
|
18099
18124
|
publishConfig: {
|
|
@@ -129152,6 +129177,37 @@ function createSystemWebWorkerRunner() {
|
|
|
129152
129177
|
|
|
129153
129178
|
// src/harness/web/outbound-secret.ts
|
|
129154
129179
|
init_service4();
|
|
129180
|
+
var HEX32_FRAGMENT = "[0-9a-f]{32}";
|
|
129181
|
+
var SHA40_FRAGMENT = "[0-9a-f]{40}";
|
|
129182
|
+
var GIST_HEX32_PATH_RE = new RegExp(`^/(?:[^/]+/)?(${HEX32_FRAGMENT})/?$`, "i");
|
|
129183
|
+
var GITHUB_COMMIT_OR_BLOB_PATH_RE = new RegExp(`/(?:commit|blob)/(${SHA40_FRAGMENT})(?:/|$)`, "i");
|
|
129184
|
+
var GITLAB_COMMIT_PATH_RE = new RegExp(`/-/commit/(${SHA40_FRAGMENT})(?:/|$)`, "i");
|
|
129185
|
+
function knownPublicIdentifierIn(url) {
|
|
129186
|
+
const host = url.hostname.toLowerCase();
|
|
129187
|
+
if (host === "gist.github.com") {
|
|
129188
|
+
return GIST_HEX32_PATH_RE.exec(url.pathname)?.[1] ?? null;
|
|
129189
|
+
}
|
|
129190
|
+
if (host === "github.com") {
|
|
129191
|
+
return GITHUB_COMMIT_OR_BLOB_PATH_RE.exec(url.pathname)?.[1] ?? null;
|
|
129192
|
+
}
|
|
129193
|
+
if (host === "gitlab.com") {
|
|
129194
|
+
return GITLAB_COMMIT_PATH_RE.exec(url.pathname)?.[1] ?? null;
|
|
129195
|
+
}
|
|
129196
|
+
return null;
|
|
129197
|
+
}
|
|
129198
|
+
function stripKnownPublicIdentifier(text) {
|
|
129199
|
+
let url;
|
|
129200
|
+
try {
|
|
129201
|
+
url = new URL(text);
|
|
129202
|
+
} catch {
|
|
129203
|
+
return text;
|
|
129204
|
+
}
|
|
129205
|
+
const identifier = knownPublicIdentifierIn(url);
|
|
129206
|
+
if (identifier === null) {
|
|
129207
|
+
return text;
|
|
129208
|
+
}
|
|
129209
|
+
return text.replace(identifier, "0".repeat(identifier.length));
|
|
129210
|
+
}
|
|
129155
129211
|
function safeDecodeComponent(value) {
|
|
129156
129212
|
try {
|
|
129157
129213
|
return decodeURIComponent(value);
|
|
@@ -129195,13 +129251,14 @@ function anyComponentLooksSecret(text) {
|
|
|
129195
129251
|
return false;
|
|
129196
129252
|
}
|
|
129197
129253
|
function containsOutboundSecret(text) {
|
|
129198
|
-
|
|
129254
|
+
const scanText = stripKnownPublicIdentifier(text);
|
|
129255
|
+
if (detectSecrets(scanText).length > 0) {
|
|
129199
129256
|
return true;
|
|
129200
129257
|
}
|
|
129201
|
-
if (isEntropyBackendEnabled() && detectEntropy(
|
|
129258
|
+
if (isEntropyBackendEnabled() && detectEntropy(scanText).length > 0) {
|
|
129202
129259
|
return true;
|
|
129203
129260
|
}
|
|
129204
|
-
return anyComponentLooksSecret(
|
|
129261
|
+
return anyComponentLooksSecret(scanText);
|
|
129205
129262
|
}
|
|
129206
129263
|
async function recordOutboundSecretFinding(cwd, tool, subject) {
|
|
129207
129264
|
try {
|
|
@@ -151074,6 +151131,15 @@ function reinsertMainQueueItem(items, at, item) {
|
|
|
151074
151131
|
next.splice(insertAt, 0, item);
|
|
151075
151132
|
return next;
|
|
151076
151133
|
}
|
|
151134
|
+
function drainIdleMainQueue(items, options) {
|
|
151135
|
+
if (!options.isIdle())
|
|
151136
|
+
return false;
|
|
151137
|
+
const next = options.takeForced() ?? items.shift();
|
|
151138
|
+
if (next === undefined)
|
|
151139
|
+
return false;
|
|
151140
|
+
options.dispatch(next);
|
|
151141
|
+
return true;
|
|
151142
|
+
}
|
|
151077
151143
|
|
|
151078
151144
|
// src/tui/queue-nav.ts
|
|
151079
151145
|
var QUEUE_NAV_ACTIONS = ["force", "edit", "delete"];
|
|
@@ -159970,6 +160036,14 @@ Staying in the current session.
|
|
|
159970
160036
|
const id = `mq${mainQueueSeq++}`;
|
|
159971
160037
|
mainQueue.push({ id, question: line, displayQuestion: displayLine });
|
|
159972
160038
|
paintMainQueue();
|
|
160039
|
+
drainIdleMainQueue(mainQueue, {
|
|
160040
|
+
isIdle: () => !destroyed && !chrome.isBusy() && !foregroundOperation.isActive && !forceHandoff.isAwaitingSettlement && leaseView()?.held() !== true,
|
|
160041
|
+
takeForced: () => forceHandoff.takeNext(),
|
|
160042
|
+
dispatch: (next) => {
|
|
160043
|
+
paintMainQueue();
|
|
160044
|
+
runLine(next.question);
|
|
160045
|
+
}
|
|
160046
|
+
});
|
|
159973
160047
|
} else {
|
|
159974
160048
|
appendUserEcho(otui, r, transcript, {
|
|
159975
160049
|
id: `side-q${uid++}`,
|
package/dist/core.js
CHANGED
|
@@ -4029,6 +4029,24 @@ var init_config2 = __esm(() => {
|
|
|
4029
4029
|
});
|
|
4030
4030
|
|
|
4031
4031
|
// src/security/detect/entropy.ts
|
|
4032
|
+
function isAdjacentLabel(content, start) {
|
|
4033
|
+
const before = lastLineOf(content.slice(Math.max(0, start - LABEL_WINDOW), start));
|
|
4034
|
+
if (ADJACENT_LABEL.test(before)) {
|
|
4035
|
+
return true;
|
|
4036
|
+
}
|
|
4037
|
+
if (before.trim().length > 0) {
|
|
4038
|
+
return false;
|
|
4039
|
+
}
|
|
4040
|
+
const lineStart = content.lastIndexOf(`
|
|
4041
|
+
`, start - 1);
|
|
4042
|
+
if (lineStart < 0) {
|
|
4043
|
+
return false;
|
|
4044
|
+
}
|
|
4045
|
+
const prevLineStart = content.lastIndexOf(`
|
|
4046
|
+
`, lineStart - 1) + 1;
|
|
4047
|
+
const prevLine = content.slice(prevLineStart, lineStart);
|
|
4048
|
+
return CONTINUATION_LABEL_LINE_RE.test(prevLine);
|
|
4049
|
+
}
|
|
4032
4050
|
function isHexBlob(value) {
|
|
4033
4051
|
return HEX_BLOB.test(value) && /[a-f]/i.test(value);
|
|
4034
4052
|
}
|
|
@@ -4228,12 +4246,19 @@ function looksSecretShapedIn(text) {
|
|
|
4228
4246
|
return secretShapedCandidatesIn(text).length > 0;
|
|
4229
4247
|
}
|
|
4230
4248
|
function labelledPieceQualifies(piece) {
|
|
4231
|
-
const base = bareShapeQualifies(piece);
|
|
4232
|
-
if (base.qualifies) {
|
|
4233
|
-
return { entropy: base.entropy };
|
|
4234
|
-
}
|
|
4235
4249
|
if (isAllowShapedValue(piece)) {
|
|
4236
|
-
return { entropy:
|
|
4250
|
+
return { entropy: shannonEntropy(piece) };
|
|
4251
|
+
}
|
|
4252
|
+
if (isHexBlob(piece)) {
|
|
4253
|
+
return { entropy: shannonEntropy(piece) };
|
|
4254
|
+
}
|
|
4255
|
+
if (!/[0-9]/.test(piece) && !/[+=]/.test(piece)) {
|
|
4256
|
+
return null;
|
|
4257
|
+
}
|
|
4258
|
+
const withoutSeparators = piece.replace(/[-_.+]/g, "");
|
|
4259
|
+
const entropy = shannonEntropy(withoutSeparators.length > 0 ? withoutSeparators : piece);
|
|
4260
|
+
if (entropy >= 3.6) {
|
|
4261
|
+
return { entropy };
|
|
4237
4262
|
}
|
|
4238
4263
|
return null;
|
|
4239
4264
|
}
|
|
@@ -4272,9 +4297,9 @@ function detectEntropy(content) {
|
|
|
4272
4297
|
if (urlSpans.some((u) => start < u.end && end > u.start)) {
|
|
4273
4298
|
continue;
|
|
4274
4299
|
}
|
|
4275
|
-
const
|
|
4276
|
-
if (
|
|
4277
|
-
matches.push(buildMatch(start, end, value, entropy));
|
|
4300
|
+
const found = labelledPieceQualifies(value);
|
|
4301
|
+
if (found !== null) {
|
|
4302
|
+
matches.push(buildMatch(start, end, value, found.entropy));
|
|
4278
4303
|
}
|
|
4279
4304
|
}
|
|
4280
4305
|
TOKEN.lastIndex = 0;
|
|
@@ -4289,8 +4314,7 @@ function detectEntropy(content) {
|
|
|
4289
4314
|
if (matches.some((existing) => start < existing.end && end > existing.start)) {
|
|
4290
4315
|
continue;
|
|
4291
4316
|
}
|
|
4292
|
-
|
|
4293
|
-
if (!ADJACENT_LABEL.test(before)) {
|
|
4317
|
+
if (!isAdjacentLabel(content, start)) {
|
|
4294
4318
|
continue;
|
|
4295
4319
|
}
|
|
4296
4320
|
const head = TOKEN_HEAD.exec(value)?.[0] ?? value;
|
|
@@ -4312,9 +4336,10 @@ function detectEntropy(content) {
|
|
|
4312
4336
|
}
|
|
4313
4337
|
return matches.sort((a, b) => a.start - b.start);
|
|
4314
4338
|
}
|
|
4315
|
-
var SENSITIVE_LABEL_WORDS = "key|secret|token|password|passwd|api|credential|auth|bearer", caseless = (words) => words.replace(/[a-z]/g, (ch) => `[${ch}${ch.toUpperCase()}]`), ADJACENT_LABEL, LABEL_ASSIGNMENT_RE, TOKEN, TOKEN_HEAD, LABEL_WINDOW = 40, HEX_BLOB, FULL_GIT_SHA_RE, UUID_RE, INTEGRITY_RE, TAG_MAX_LETTER_RUN = 8, SLUG_HEX_TAIL_RE, URL_SPAN_RE, URL_TRAILING_PUNCTUATION_RE, URL_COMPONENT_MIN_LENGTH = 20;
|
|
4339
|
+
var SENSITIVE_LABEL_WORDS = "key|secret|token|password|passwd|api|credential|auth|bearer", caseless = (words) => words.replace(/[a-z]/g, (ch) => `[${ch}${ch.toUpperCase()}]`), FILLER_WORDS = "is|was|set|to|the|value|now", ADJACENT_LABEL, CONTINUATION_LABEL_LINE_RE, LABEL_ASSIGNMENT_RE, TOKEN, TOKEN_HEAD, LABEL_WINDOW = 40, HEX_BLOB, FULL_GIT_SHA_RE, UUID_RE, INTEGRITY_RE, TAG_MAX_LETTER_RUN = 8, SLUG_HEX_TAIL_RE, URL_SPAN_RE, URL_TRAILING_PUNCTUATION_RE, URL_COMPONENT_MIN_LENGTH = 20;
|
|
4316
4340
|
var init_entropy = __esm(() => {
|
|
4317
|
-
ADJACENT_LABEL = new RegExp(`(?:^|[^A-Za-z0-9])(?:${caseless(SENSITIVE_LABEL_WORDS)})[A-Za-z0-9_-]
|
|
4341
|
+
ADJACENT_LABEL = new RegExp(`(?:^|[^A-Za-z0-9])(?:${caseless(SENSITIVE_LABEL_WORDS)})[A-Za-z0-9_-]*` + `(?:\\s+(?:${caseless(FILLER_WORDS)})){0,2}` + `["']?\\s*[:=]?\\s*["']?$`);
|
|
4342
|
+
CONTINUATION_LABEL_LINE_RE = new RegExp(`(?:^|[^A-Za-z0-9])(?:${caseless(SENSITIVE_LABEL_WORDS)})[A-Za-z0-9_-]*["']?\\s*[:=]?\\s*\\\\\\s*$`);
|
|
4318
4343
|
LABEL_ASSIGNMENT_RE = new RegExp(`(?:^|[^A-Za-z0-9])(?:${SENSITIVE_LABEL_WORDS})[A-Za-z0-9_-]*"?\\s*=\\s*"?([A-Za-z0-9+/_=-]{6,})`, "gi");
|
|
4319
4344
|
TOKEN = /[A-Za-z0-9+/=_-]{20,}(?:\.[A-Za-z0-9+/=_-]{6,})*/g;
|
|
4320
4345
|
TOKEN_HEAD = /^[A-Za-z0-9+/=_-]+/;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrciphersmith/keryx",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.22",
|
|
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": {
|
|
@@ -1587,10 +1587,12 @@ Fix: <concrete>
|
|
|
1587
1587
|
- <what was checked> — <how>
|
|
1588
1588
|
|
|
1589
1589
|
### How this review was run
|
|
1590
|
-
- **
|
|
1590
|
+
- **Workflow:** `review-orchestrator`
|
|
1591
1591
|
- **Scope:** `<base>..<head>`, round N, PR #N
|
|
1592
|
-
- **
|
|
1593
|
-
-
|
|
1592
|
+
- **Models:** <actual models and reviewers, or not recorded>
|
|
1593
|
+
- **Tools:** <tools actually invoked, or not recorded>
|
|
1594
|
+
- **Skills:** <skills actually used, or not recorded>
|
|
1595
|
+
- **Subagents:** <dispatched reviewers and models, or none when confirmed; otherwise not recorded>
|
|
1594
1596
|
- **Not run:** <reviewer — reason>
|
|
1595
1597
|
- **Verification:** <mode>; confirmed N, refuted N, unverifiable N, unverified N
|
|
1596
1598
|
```
|
|
@@ -1663,17 +1665,15 @@ Automation values, names unchanged:
|
|
|
1663
1665
|
|
|
1664
1666
|
Default is do not publish. No resolvable PR number means skip and say so.
|
|
1665
1667
|
|
|
1666
|
-
The comment is the report rendered from `templates/review-report.md`, English, with the domain file chosen above (`templates/pr-comment-frontend.md` / `templates/pr-comment-backend.md`) — never a summary written freehand. It does not use a tool heading, a finding table, or a meta table. It does not carry a co-author line, a `Generated with` trailer, or any sentence that names a vendor or a product as the author.
|
|
1668
|
+
The comment is the report rendered from `templates/review-report.md`, English, with the domain file chosen above (`templates/pr-comment-frontend.md` / `templates/pr-comment-backend.md`) — never a summary written freehand. It does not use a tool heading, a finding table, or a meta table. It does not carry a co-author line, a `Generated with` trailer, or any sentence that names a vendor or a product as the author. Name the workflow and reviewers that actually ran, with models, tools, skills and subagents. Never identify the operator from `gh auth status`, git config or a credential; do not sign the comment as a person, reviewer or model.
|
|
1667
1669
|
|
|
1668
1670
|
The follow-up file path and the metadata rules (real model names, Run vs Not run, no `adaptive` in the model slot) live in that same template. Write the body to a temp file and post with `gh pr comment <n> --body-file <file>`.
|
|
1669
1671
|
|
|
1670
1672
|
**No GitHub write without an approved draft.** Before ANY write — the comment, a thread reply, `keryx review comments reply`, a review — show the user the rendered body and wait for explicit approval of that body.
|
|
1671
1673
|
Picking A or B above chooses *whether* to publish, not *what*; a comment is public and cannot be unsent. With no user to answer (a dispatched run), do not write: hand the rendered body back to the caller.
|
|
1672
1674
|
|
|
1673
|
-
Re-read head and
|
|
1674
|
-
|
|
1675
|
-
Do not post a finding another reviewer already filed on this head; if yours
|
|
1676
|
-
extends theirs, cite theirs.
|
|
1675
|
+
Re-read head and thread before posting. If head moved, re-check findings
|
|
1676
|
+
and name unchecked commits. Do not duplicate findings; cite extended ones.
|
|
1677
1677
|
|
|
1678
1678
|
## Job Context Awareness
|
|
1679
1679
|
|
|
@@ -95,14 +95,19 @@ Do not list a checklist item that was not checked. Do not praise.
|
|
|
95
95
|
|
|
96
96
|
## How this review was run
|
|
97
97
|
|
|
98
|
-
A
|
|
98
|
+
A factual run inventory, not an author or account signature. List only steps
|
|
99
|
+
confirmed by the run record; never infer an operator identity from `gh auth`,
|
|
100
|
+
git config, a credential or a PR author. Use `not recorded` when evidence for
|
|
101
|
+
a tool, skill or model is unavailable; use `none` only when the run proves none.
|
|
99
102
|
|
|
100
103
|
```markdown
|
|
101
104
|
### How this review was run
|
|
102
|
-
- **
|
|
105
|
+
- **Workflow:** `review-orchestrator`; severity and lanes per the repository review rules, else the canonical rubric in this skill.
|
|
103
106
|
- **Scope:** `<base>..<head>`, round N, PR #N. Unchecked commits: <list or none>.
|
|
104
|
-
- **
|
|
105
|
-
-
|
|
107
|
+
- **Models:** <actual model names and the reviewers that ran on each; or not recorded>.
|
|
108
|
+
- **Tools:** <tools actually invoked for this review and their roles; or not recorded>.
|
|
109
|
+
- **Skills:** <skills actually loaded or executed and their roles; or not recorded>.
|
|
110
|
+
- **Subagents:** <dispatched subagents, their reviewer roles and actual models; or none when confirmed, otherwise not recorded>.
|
|
106
111
|
- **Fallback:** <reviewer> via `general-purpose` because <native agent type unavailable | no bundled agent>. Or `none`.
|
|
107
112
|
- **Not run:** <reviewer> — <short reason>. Or `none`.
|
|
108
113
|
- **Selection:** <auto-detected scope | explicit flags | optional groups the user picked>
|
|
@@ -118,8 +123,8 @@ Model rules, unchanged from the old metadata table:
|
|
|
118
123
|
- Name the model that actually ran. Never write `adaptive`, `inherit`,
|
|
119
124
|
`unsupported`, or `current-session` in the model slot.
|
|
120
125
|
- Group reviewers under the model they ran on. One bullet per model.
|
|
121
|
-
- `Model strategy` (`ask` or `adaptive`) goes
|
|
122
|
-
|
|
126
|
+
- `Model strategy` (`ask` or `adaptive`) goes under Selection, never in
|
|
127
|
+
place of an actual model name.
|
|
123
128
|
- Keep **Run** complete. If Not run is long, group it with counts and notable
|
|
124
129
|
names; the full list belongs in the follow-up file.
|
|
125
130
|
|