@hivelore/core 0.39.2 → 0.42.1
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/index.d.ts +182 -61
- package/dist/index.js +296 -41
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -28,8 +28,13 @@ var AnchorSchema = z.object({
|
|
|
28
28
|
symbols: z.array(z.string()).default([])
|
|
29
29
|
});
|
|
30
30
|
var SensorSchema = z.object({
|
|
31
|
-
kind: z.enum(["regex", "shell", "test"]).default("regex"),
|
|
32
|
-
/**
|
|
31
|
+
kind: z.enum(["regex", "ast", "shell", "test"]).default("regex"),
|
|
32
|
+
/**
|
|
33
|
+
* kind=regex: regex source, matched against added diff lines / file content.
|
|
34
|
+
* kind=ast: an ast-grep structural pattern (e.g. `stripe.paymentIntents.create($$$)`) — matched
|
|
35
|
+
* on the AST of changed files, so comments and string literals can never false-positive. Requires
|
|
36
|
+
* the optional `@ast-grep/napi` engine; without it the sensor is unrunnable (warn, never block).
|
|
37
|
+
*/
|
|
33
38
|
pattern: z.string().optional(),
|
|
34
39
|
/**
|
|
35
40
|
* Optional "correct-usage" regex (kind=regex). When `pattern` (the risky call) matches but this
|
|
@@ -56,6 +61,13 @@ var SensorSchema = z.object({
|
|
|
56
61
|
* the incident the test exists to prevent". Surfaced in the block message and the prevention receipt.
|
|
57
62
|
*/
|
|
58
63
|
incident: z.string().optional(),
|
|
64
|
+
/**
|
|
65
|
+
* kind=shell|test only: the oracle was PROVEN to fail (RED) on the incident state at arming time
|
|
66
|
+
* (`sensors propose --red-ref`): the command passed on the presumed-correct tree AND failed on
|
|
67
|
+
* the replayed incident tree. Distinguishes "a test is routed" from "the test demonstrably
|
|
68
|
+
* catches the incident". Surfaced in the prevention receipt.
|
|
69
|
+
*/
|
|
70
|
+
red_proven: z.boolean().optional(),
|
|
59
71
|
/** `warn` surfaces in review; `block` can hard-block the commit (only when the gate opts in). */
|
|
60
72
|
severity: z.enum(["warn", "block"]).default("warn"),
|
|
61
73
|
/** True when Hivelore generated this sensor automatically (vs. hand-authored). */
|
|
@@ -951,7 +963,8 @@ function buildPreventionReceipt(events, memories, usage, options) {
|
|
|
951
963
|
stage: event.stage ?? null,
|
|
952
964
|
exit_code: event.exit_code ?? null,
|
|
953
965
|
message: sensor?.message ?? null,
|
|
954
|
-
incident: sensor?.incident ?? null
|
|
966
|
+
incident: sensor?.incident ?? null,
|
|
967
|
+
red_proven: sensor?.red_proven === true
|
|
955
968
|
};
|
|
956
969
|
}).sort((a, b) => b.at.localeCompare(a.at));
|
|
957
970
|
const preventedCountTotal = Object.values(usage.by_id).reduce((sum, item) => sum + item.prevented_count, 0);
|
|
@@ -976,7 +989,8 @@ function renderPreventionReceipt(receipt) {
|
|
|
976
989
|
const exit = row.exit_code === null ? "" : `, exit ${row.exit_code}`;
|
|
977
990
|
const stage = row.stage ? ` \u2014 caught at ${row.stage}` : "";
|
|
978
991
|
const incident = row.incident ? ` \u21A9 incident: ${row.incident}` : "";
|
|
979
|
-
|
|
992
|
+
const red = row.red_proven ? " \u2713 RED-proven" : "";
|
|
993
|
+
lines.push(` \u2717\u2192\u2713 ${row.at.slice(0, 10)} ${row.id.padEnd(32)} (${kind}${exit}${stage})${incident}${red}`);
|
|
980
994
|
}
|
|
981
995
|
lines.push(
|
|
982
996
|
` Trend: ${receipt.total} this window vs ${receipt.previous_total} previous window (${receipt.total <= receipt.previous_total ? "recurrences declining" : "recurrences rising"}).`
|
|
@@ -1175,6 +1189,43 @@ async function recordProjectContextEmission(paths, hash, now = Date.now()) {
|
|
|
1175
1189
|
});
|
|
1176
1190
|
}
|
|
1177
1191
|
|
|
1192
|
+
// src/priority.ts
|
|
1193
|
+
var DEFAULT_PRIORITY_SIGNALS = {
|
|
1194
|
+
type: "",
|
|
1195
|
+
tags: [],
|
|
1196
|
+
requiresHumanApproval: false,
|
|
1197
|
+
directAnchor: false,
|
|
1198
|
+
directSymbol: false,
|
|
1199
|
+
exactTaskMatch: false,
|
|
1200
|
+
strongSemantic: false,
|
|
1201
|
+
usefulSemantic: false,
|
|
1202
|
+
moduleOrDomainMatch: false,
|
|
1203
|
+
tagTaskMatch: false
|
|
1204
|
+
};
|
|
1205
|
+
function prioritySignals(partial) {
|
|
1206
|
+
return { ...DEFAULT_PRIORITY_SIGNALS, ...partial };
|
|
1207
|
+
}
|
|
1208
|
+
function classifyMemoryPriority(signals) {
|
|
1209
|
+
const isNegative = signals.type === "attempt";
|
|
1210
|
+
const isSkill2 = signals.type === "skill";
|
|
1211
|
+
if (signals.requiresHumanApproval || signals.directAnchor || signals.directSymbol || isNegative && (signals.exactTaskMatch || signals.strongSemantic) || isSkill2 && (signals.exactTaskMatch || signals.strongSemantic)) {
|
|
1212
|
+
return "must_read";
|
|
1213
|
+
}
|
|
1214
|
+
if (isStackPackSeed({ tags: signals.tags }) || isEnvWorkaroundMemory({ tags: signals.tags })) {
|
|
1215
|
+
if (isStackPackSeed({ tags: signals.tags }) && (signals.exactTaskMatch || signals.strongSemantic)) {
|
|
1216
|
+
return "useful";
|
|
1217
|
+
}
|
|
1218
|
+
return "background";
|
|
1219
|
+
}
|
|
1220
|
+
if (isSkill2 || signals.moduleOrDomainMatch || signals.exactTaskMatch || signals.usefulSemantic || signals.tagTaskMatch) {
|
|
1221
|
+
return "useful";
|
|
1222
|
+
}
|
|
1223
|
+
return "background";
|
|
1224
|
+
}
|
|
1225
|
+
function priorityRank(priority) {
|
|
1226
|
+
return priority === "must_read" ? 3 : priority === "useful" ? 2 : 1;
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1178
1229
|
// src/eval.ts
|
|
1179
1230
|
function round32(n) {
|
|
1180
1231
|
return Math.round(n * 1e3) / 1e3;
|
|
@@ -1302,6 +1353,69 @@ function synthesizeSelfEvalCases(memories, options = {}) {
|
|
|
1302
1353
|
}
|
|
1303
1354
|
return cases;
|
|
1304
1355
|
}
|
|
1356
|
+
function appendProposedRetrievalCases(specRaw, cases) {
|
|
1357
|
+
let spec = {};
|
|
1358
|
+
if (specRaw?.trim()) {
|
|
1359
|
+
try {
|
|
1360
|
+
spec = JSON.parse(specRaw);
|
|
1361
|
+
} catch {
|
|
1362
|
+
spec = {};
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1365
|
+
const existingNames = /* @__PURE__ */ new Set([
|
|
1366
|
+
...(spec.retrieval ?? []).map((c) => c.name),
|
|
1367
|
+
...(spec.proposed_retrieval ?? []).map((c) => c.name)
|
|
1368
|
+
]);
|
|
1369
|
+
const fresh = cases.filter((c) => !existingNames.has(c.name));
|
|
1370
|
+
if (fresh.length > 0) spec.proposed_retrieval = [...spec.proposed_retrieval ?? [], ...fresh];
|
|
1371
|
+
return JSON.stringify(spec, null, 2) + "\n";
|
|
1372
|
+
}
|
|
1373
|
+
function approveProposedCases(specRaw) {
|
|
1374
|
+
let spec;
|
|
1375
|
+
try {
|
|
1376
|
+
spec = JSON.parse(specRaw);
|
|
1377
|
+
} catch {
|
|
1378
|
+
return { raw: specRaw, approved: 0 };
|
|
1379
|
+
}
|
|
1380
|
+
const proposed = spec.proposed_retrieval ?? [];
|
|
1381
|
+
if (proposed.length === 0) return { raw: specRaw, approved: 0 };
|
|
1382
|
+
spec.retrieval = [...spec.retrieval ?? [], ...proposed];
|
|
1383
|
+
delete spec.proposed_retrieval;
|
|
1384
|
+
return { raw: JSON.stringify(spec, null, 2) + "\n", approved: proposed.length };
|
|
1385
|
+
}
|
|
1386
|
+
function runTierContract() {
|
|
1387
|
+
const cases = [
|
|
1388
|
+
{
|
|
1389
|
+
name: "stack-pack seed + strong task evidence \u2192 useful (rescue path stays alive)",
|
|
1390
|
+
expected: "useful",
|
|
1391
|
+
signals: prioritySignals({ type: "convention", tags: ["stack-pack"], strongSemantic: true, usefulSemantic: true })
|
|
1392
|
+
},
|
|
1393
|
+
{
|
|
1394
|
+
name: "stack-pack seed + weak evidence \u2192 background (crowding guard stays)",
|
|
1395
|
+
expected: "background",
|
|
1396
|
+
signals: prioritySignals({ type: "convention", tags: ["stack-pack"], usefulSemantic: true, tagTaskMatch: true })
|
|
1397
|
+
},
|
|
1398
|
+
{
|
|
1399
|
+
name: "env workaround + strong evidence \u2192 background (hard cap stays)",
|
|
1400
|
+
expected: "background",
|
|
1401
|
+
signals: prioritySignals({ type: "gotcha", tags: ["dev-env"], strongSemantic: true, exactTaskMatch: true })
|
|
1402
|
+
},
|
|
1403
|
+
{
|
|
1404
|
+
name: "direct anchor \u2192 must_read (anchors always win)",
|
|
1405
|
+
expected: "must_read",
|
|
1406
|
+
signals: prioritySignals({ type: "convention", directAnchor: true })
|
|
1407
|
+
},
|
|
1408
|
+
{
|
|
1409
|
+
name: "attempt + exact task match \u2192 must_read (negative knowledge first)",
|
|
1410
|
+
expected: "must_read",
|
|
1411
|
+
signals: prioritySignals({ type: "attempt", exactTaskMatch: true })
|
|
1412
|
+
}
|
|
1413
|
+
];
|
|
1414
|
+
return cases.map(({ name, expected, signals }) => {
|
|
1415
|
+
const actual = classifyMemoryPriority(signals);
|
|
1416
|
+
return { name, expected, actual, pass: actual === expected };
|
|
1417
|
+
});
|
|
1418
|
+
}
|
|
1305
1419
|
|
|
1306
1420
|
// src/confidence.ts
|
|
1307
1421
|
var DEFAULT_CONFIDENCE_THRESHOLDS = {
|
|
@@ -3958,6 +4072,34 @@ function runSensors(memories, targets) {
|
|
|
3958
4072
|
}
|
|
3959
4073
|
return hits;
|
|
3960
4074
|
}
|
|
4075
|
+
var COMMAND_ENV_EXACT = /* @__PURE__ */ new Set([
|
|
4076
|
+
"PATH",
|
|
4077
|
+
"HOME",
|
|
4078
|
+
"LANG",
|
|
4079
|
+
"LANGUAGE",
|
|
4080
|
+
"TMPDIR",
|
|
4081
|
+
"TMP",
|
|
4082
|
+
"TEMP",
|
|
4083
|
+
"TERM",
|
|
4084
|
+
"SHELL",
|
|
4085
|
+
"USER",
|
|
4086
|
+
"LOGNAME",
|
|
4087
|
+
"PWD",
|
|
4088
|
+
"CI",
|
|
4089
|
+
"COLORTERM",
|
|
4090
|
+
"TZ"
|
|
4091
|
+
]);
|
|
4092
|
+
var COMMAND_ENV_PREFIXES = ["LC_", "NODE_", "NVM_", "npm_", "HIVELORE_", "HAIVE_"];
|
|
4093
|
+
function scrubbedCommandEnv(env) {
|
|
4094
|
+
const out = {};
|
|
4095
|
+
for (const [key, value] of Object.entries(env)) {
|
|
4096
|
+
if (value === void 0) continue;
|
|
4097
|
+
if (COMMAND_ENV_EXACT.has(key) || COMMAND_ENV_PREFIXES.some((p) => key.startsWith(p))) {
|
|
4098
|
+
out[key] = value;
|
|
4099
|
+
}
|
|
4100
|
+
}
|
|
4101
|
+
return out;
|
|
4102
|
+
}
|
|
3961
4103
|
function incidentSuffix(incident) {
|
|
3962
4104
|
const ref = incident?.trim();
|
|
3963
4105
|
return ref ? ` \u21A9 guards incident: ${ref}` : "";
|
|
@@ -3986,6 +4128,34 @@ function selectCommandSensors(memories, changedPaths) {
|
|
|
3986
4128
|
}
|
|
3987
4129
|
return specs;
|
|
3988
4130
|
}
|
|
4131
|
+
function addedLineNumbersFromDiff(diff) {
|
|
4132
|
+
const result = /* @__PURE__ */ new Map();
|
|
4133
|
+
let currentPath = null;
|
|
4134
|
+
let newLine = 0;
|
|
4135
|
+
for (const line of diff.split("\n")) {
|
|
4136
|
+
if (line.startsWith("+++ ")) {
|
|
4137
|
+
const raw = line.slice(4).trim();
|
|
4138
|
+
currentPath = raw === "/dev/null" ? null : normalizeProjectPath(raw);
|
|
4139
|
+
continue;
|
|
4140
|
+
}
|
|
4141
|
+
const hunk = line.match(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
|
|
4142
|
+
if (hunk) {
|
|
4143
|
+
newLine = Number(hunk[1]);
|
|
4144
|
+
continue;
|
|
4145
|
+
}
|
|
4146
|
+
if (!currentPath) continue;
|
|
4147
|
+
if (line.startsWith("+") && !line.startsWith("+++")) {
|
|
4148
|
+
let set = result.get(currentPath);
|
|
4149
|
+
if (!set) result.set(currentPath, set = /* @__PURE__ */ new Set());
|
|
4150
|
+
set.add(newLine);
|
|
4151
|
+
newLine++;
|
|
4152
|
+
} else if (line.startsWith("-") && !line.startsWith("---")) {
|
|
4153
|
+
} else if (!line.startsWith("\\")) {
|
|
4154
|
+
newLine++;
|
|
4155
|
+
}
|
|
4156
|
+
}
|
|
4157
|
+
return result;
|
|
4158
|
+
}
|
|
3989
4159
|
function sensorTargetsFromDiff(diff) {
|
|
3990
4160
|
const targets = [];
|
|
3991
4161
|
let currentPath = null;
|
|
@@ -5407,6 +5577,33 @@ function findUncapturedFailures(failures, captureTimes, options = {}) {
|
|
|
5407
5577
|
out.sort((a, b) => a.ts.localeCompare(b.ts));
|
|
5408
5578
|
return out;
|
|
5409
5579
|
}
|
|
5580
|
+
var EXPLORATORY_RE = /^(ls|find|grep|rg|cat|head|tail|which|stat)\b/i;
|
|
5581
|
+
function distillFailureObservations(failures, options = {}) {
|
|
5582
|
+
const max = options.max ?? 3;
|
|
5583
|
+
const clusters = /* @__PURE__ */ new Map();
|
|
5584
|
+
for (const f of failures) {
|
|
5585
|
+
const summary = f.summary.trim();
|
|
5586
|
+
if (!summary || EXPLORATORY_RE.test(summary.replace(/^Bash:\s*/i, ""))) continue;
|
|
5587
|
+
const key = normalizeSummary(summary);
|
|
5588
|
+
const existing = clusters.get(key);
|
|
5589
|
+
if (existing) {
|
|
5590
|
+
existing.count++;
|
|
5591
|
+
for (const file of f.files ?? []) existing.files.add(file);
|
|
5592
|
+
} else {
|
|
5593
|
+
clusters.set(key, { first: f, count: 1, files: new Set(f.files ?? []) });
|
|
5594
|
+
}
|
|
5595
|
+
}
|
|
5596
|
+
return [...clusters.values()].sort((a, b) => b.count - a.count || b.first.ts.localeCompare(a.first.ts)).slice(0, max).map(({ first, count, files }) => {
|
|
5597
|
+
const summary = first.summary.trim();
|
|
5598
|
+
const firstLine = summary.split("\n")[0].slice(0, 120);
|
|
5599
|
+
return {
|
|
5600
|
+
what: `${first.tool} failed: ${firstLine}`,
|
|
5601
|
+
why_failed: summary.slice(0, 400),
|
|
5602
|
+
paths: [...files].slice(0, 6),
|
|
5603
|
+
occurrences: count
|
|
5604
|
+
};
|
|
5605
|
+
});
|
|
5606
|
+
}
|
|
5410
5607
|
|
|
5411
5608
|
// src/coverage.ts
|
|
5412
5609
|
var DEFAULT_COVERING_TYPES = ["decision", "convention", "gotcha", "architecture"];
|
|
@@ -5906,43 +6103,6 @@ async function handoffAgeMs(root, now = /* @__PURE__ */ new Date()) {
|
|
|
5906
6103
|
}
|
|
5907
6104
|
}
|
|
5908
6105
|
|
|
5909
|
-
// src/priority.ts
|
|
5910
|
-
var DEFAULT_PRIORITY_SIGNALS = {
|
|
5911
|
-
type: "",
|
|
5912
|
-
tags: [],
|
|
5913
|
-
requiresHumanApproval: false,
|
|
5914
|
-
directAnchor: false,
|
|
5915
|
-
directSymbol: false,
|
|
5916
|
-
exactTaskMatch: false,
|
|
5917
|
-
strongSemantic: false,
|
|
5918
|
-
usefulSemantic: false,
|
|
5919
|
-
moduleOrDomainMatch: false,
|
|
5920
|
-
tagTaskMatch: false
|
|
5921
|
-
};
|
|
5922
|
-
function prioritySignals(partial) {
|
|
5923
|
-
return { ...DEFAULT_PRIORITY_SIGNALS, ...partial };
|
|
5924
|
-
}
|
|
5925
|
-
function classifyMemoryPriority(signals) {
|
|
5926
|
-
const isNegative = signals.type === "attempt";
|
|
5927
|
-
const isSkill2 = signals.type === "skill";
|
|
5928
|
-
if (signals.requiresHumanApproval || signals.directAnchor || signals.directSymbol || isNegative && (signals.exactTaskMatch || signals.strongSemantic) || isSkill2 && (signals.exactTaskMatch || signals.strongSemantic)) {
|
|
5929
|
-
return "must_read";
|
|
5930
|
-
}
|
|
5931
|
-
if (isStackPackSeed({ tags: signals.tags }) || isEnvWorkaroundMemory({ tags: signals.tags })) {
|
|
5932
|
-
if (isStackPackSeed({ tags: signals.tags }) && (signals.exactTaskMatch || signals.strongSemantic)) {
|
|
5933
|
-
return "useful";
|
|
5934
|
-
}
|
|
5935
|
-
return "background";
|
|
5936
|
-
}
|
|
5937
|
-
if (isSkill2 || signals.moduleOrDomainMatch || signals.exactTaskMatch || signals.usefulSemantic || signals.tagTaskMatch) {
|
|
5938
|
-
return "useful";
|
|
5939
|
-
}
|
|
5940
|
-
return "background";
|
|
5941
|
-
}
|
|
5942
|
-
function priorityRank(priority) {
|
|
5943
|
-
return priority === "must_read" ? 3 : priority === "useful" ? 2 : 1;
|
|
5944
|
-
}
|
|
5945
|
-
|
|
5946
6106
|
// src/agent-context.ts
|
|
5947
6107
|
var AGENT_ENV_SIGNALS = [
|
|
5948
6108
|
{ name: "HAIVE_SESSION_ID", label: "hivelore-run-wrapper" },
|
|
@@ -5960,6 +6120,92 @@ function detectAgentContext(env = typeof process !== "undefined" ? process.env :
|
|
|
5960
6120
|
const signals = AGENT_ENV_SIGNALS.filter(({ name }) => (env[name] ?? "").trim().length > 0).map(({ name, label }) => `${label} (${name})`);
|
|
5961
6121
|
return { agent: signals.length > 0, signals: [...new Set(signals)] };
|
|
5962
6122
|
}
|
|
6123
|
+
|
|
6124
|
+
// src/pr-review-ingest.ts
|
|
6125
|
+
var REVIEW_LEARNING_MARKER = /(^|\s)\/?hivelore[:,]?\s+remember\b|(^|\s)hivelore:/i;
|
|
6126
|
+
var INSTRUCTION_RE = /\b(never|always|don'?t|do not|must(?: not)?|should(?: not|n'?t)?|avoid|prefer|instead of|use\s+\S+\s+instead)\b/i;
|
|
6127
|
+
var MAX_INSTRUCTION_CHARS = 500;
|
|
6128
|
+
var MIN_INSTRUCTION_CHARS = 12;
|
|
6129
|
+
function prNumberFrom(comment) {
|
|
6130
|
+
const m = comment.pull_request_url?.match(/\/pulls\/(\d+)$/);
|
|
6131
|
+
return m ? Number(m[1]) : void 0;
|
|
6132
|
+
}
|
|
6133
|
+
function extractReviewLearnings(payload) {
|
|
6134
|
+
if (!Array.isArray(payload)) return [];
|
|
6135
|
+
const comments = payload;
|
|
6136
|
+
const learnings = [];
|
|
6137
|
+
for (const comment of comments) {
|
|
6138
|
+
if (typeof comment?.id !== "number") continue;
|
|
6139
|
+
if ((comment.user?.type ?? "").toLowerCase() === "bot") continue;
|
|
6140
|
+
const body = (comment.body ?? "").trim();
|
|
6141
|
+
if (body.length < MIN_INSTRUCTION_CHARS) continue;
|
|
6142
|
+
const marked = REVIEW_LEARNING_MARKER.test(body);
|
|
6143
|
+
if (!marked && !INSTRUCTION_RE.test(body)) continue;
|
|
6144
|
+
const instruction = body.replace(REVIEW_LEARNING_MARKER, " ").replace(/\s+/g, " ").trim().slice(0, MAX_INSTRUCTION_CHARS);
|
|
6145
|
+
if (instruction.length < MIN_INSTRUCTION_CHARS) continue;
|
|
6146
|
+
learnings.push({
|
|
6147
|
+
thread_id: comment.in_reply_to_id ?? comment.id,
|
|
6148
|
+
comment_id: comment.id,
|
|
6149
|
+
...comment.path ? { path: comment.path } : {},
|
|
6150
|
+
...typeof (comment.line ?? comment.original_line) === "number" ? { line: comment.line ?? comment.original_line } : {},
|
|
6151
|
+
author: comment.user?.login ?? "reviewer",
|
|
6152
|
+
instruction,
|
|
6153
|
+
...comment.html_url ? { url: comment.html_url } : {},
|
|
6154
|
+
...prNumberFrom(comment) !== void 0 ? { pr_number: prNumberFrom(comment) } : {}
|
|
6155
|
+
});
|
|
6156
|
+
}
|
|
6157
|
+
return learnings;
|
|
6158
|
+
}
|
|
6159
|
+
function reviewLearningsToDrafts(learnings, options = {}) {
|
|
6160
|
+
const limit = options.limit ?? 20;
|
|
6161
|
+
const drafts = [];
|
|
6162
|
+
const seenThreads = /* @__PURE__ */ new Set();
|
|
6163
|
+
for (const learning of learnings) {
|
|
6164
|
+
if (drafts.length >= limit) break;
|
|
6165
|
+
if (seenThreads.has(learning.thread_id)) {
|
|
6166
|
+
const idx = drafts.findIndex((d) => d.key === `github-pr:${learning.thread_id}`);
|
|
6167
|
+
if (idx >= 0) drafts.splice(idx, 1);
|
|
6168
|
+
}
|
|
6169
|
+
seenThreads.add(learning.thread_id);
|
|
6170
|
+
const key = `github-pr:${learning.thread_id}`;
|
|
6171
|
+
const slugSource = learning.instruction.toLowerCase().replace(/[^a-z0-9\s]/g, " ");
|
|
6172
|
+
const slug = slugSource.trim().split(/\s+/).slice(0, 6).join("-") || "review-learning";
|
|
6173
|
+
const finding = {
|
|
6174
|
+
tool: "github-pr",
|
|
6175
|
+
ruleId: "review-learning",
|
|
6176
|
+
severity: "major",
|
|
6177
|
+
message: learning.instruction,
|
|
6178
|
+
path: learning.path ?? "",
|
|
6179
|
+
...learning.line !== void 0 ? { line: learning.line } : {},
|
|
6180
|
+
key
|
|
6181
|
+
};
|
|
6182
|
+
const baseFm = buildFrontmatter({
|
|
6183
|
+
type: "convention",
|
|
6184
|
+
slug,
|
|
6185
|
+
scope: options.scope ?? "team",
|
|
6186
|
+
module: options.module,
|
|
6187
|
+
tags: ["review-learning"],
|
|
6188
|
+
paths: learning.path ? [learning.path] : [],
|
|
6189
|
+
author: options.author ?? learning.author
|
|
6190
|
+
});
|
|
6191
|
+
const frontmatter = { ...baseFm, status: "proposed", topic: `ingest:${key}` };
|
|
6192
|
+
const provenance = [
|
|
6193
|
+
learning.pr_number !== void 0 ? `PR #${learning.pr_number}` : null,
|
|
6194
|
+
`@${learning.author}`,
|
|
6195
|
+
learning.url ?? null
|
|
6196
|
+
].filter(Boolean).join(" \xB7 ");
|
|
6197
|
+
const body = `# Review learning: ${learning.instruction.slice(0, 80)}
|
|
6198
|
+
|
|
6199
|
+
${learning.instruction}
|
|
6200
|
+
|
|
6201
|
+
` + (learning.path ? `Applies to: \`${learning.path}\`${learning.line !== void 0 ? ` (line ${learning.line} at review time)` : ""}
|
|
6202
|
+
|
|
6203
|
+
` : "") + `_From a review thread (${provenance}). Review: approve, refine, or reject \u2014 then consider \`hivelore sensors propose\` to make it a deterministic gate._
|
|
6204
|
+
`;
|
|
6205
|
+
drafts.push({ key, topic: `ingest:${key}`, frontmatter, body, finding, has_sensor: false });
|
|
6206
|
+
}
|
|
6207
|
+
return drafts;
|
|
6208
|
+
}
|
|
5963
6209
|
export {
|
|
5964
6210
|
AUTOPILOT_DEFAULTS,
|
|
5965
6211
|
ActivationSchema,
|
|
@@ -5998,6 +6244,7 @@ export {
|
|
|
5998
6244
|
PREVENTION_DEBOUNCE_MS,
|
|
5999
6245
|
PROJECT_CONTEXT_FILE,
|
|
6000
6246
|
PROJECT_CONTEXT_THROTTLE_MS,
|
|
6247
|
+
REVIEW_LEARNING_MARKER,
|
|
6001
6248
|
RUNTIME_JOURNAL_FILENAME,
|
|
6002
6249
|
SCAFFOLD_MARKER_RE,
|
|
6003
6250
|
SEED_QUALITY_FLOOR,
|
|
@@ -6010,6 +6257,7 @@ export {
|
|
|
6010
6257
|
USAGE_FILE,
|
|
6011
6258
|
USAGE_LOG_DIR,
|
|
6012
6259
|
USAGE_LOG_FILE,
|
|
6260
|
+
addedLineNumbersFromDiff,
|
|
6013
6261
|
addedLinesFromDiff,
|
|
6014
6262
|
aggregateRetrieval,
|
|
6015
6263
|
aggregateSensors,
|
|
@@ -6018,11 +6266,13 @@ export {
|
|
|
6018
6266
|
antiPatternGateParams,
|
|
6019
6267
|
appendEvalHistory,
|
|
6020
6268
|
appendPreventionEvent,
|
|
6269
|
+
appendProposedRetrievalCases,
|
|
6021
6270
|
appendRuntimeJournalEntry,
|
|
6022
6271
|
appendSensorEvaluations,
|
|
6023
6272
|
appendUsageEvent,
|
|
6024
6273
|
applyConflictResolution,
|
|
6025
6274
|
applyFeedbackAdjustment,
|
|
6275
|
+
approveProposedCases,
|
|
6026
6276
|
assessBootstrapState,
|
|
6027
6277
|
assessScaffoldLoop,
|
|
6028
6278
|
assessSensorHealth,
|
|
@@ -6064,6 +6314,7 @@ export {
|
|
|
6064
6314
|
detectStacksFromManifests,
|
|
6065
6315
|
diffContract,
|
|
6066
6316
|
diffHasDistinctiveOverlap,
|
|
6317
|
+
distillFailureObservations,
|
|
6067
6318
|
distinctiveCap,
|
|
6068
6319
|
draftsFromFindings,
|
|
6069
6320
|
emptyUsage,
|
|
@@ -6075,6 +6326,7 @@ export {
|
|
|
6075
6326
|
existingGateMissShas,
|
|
6076
6327
|
extractActionsBriefBody,
|
|
6077
6328
|
extractReferencedPaths,
|
|
6329
|
+
extractReviewLearnings,
|
|
6078
6330
|
extractSensorExamples,
|
|
6079
6331
|
extractSnippet,
|
|
6080
6332
|
extractTestFilePathsFromCommand,
|
|
@@ -6189,8 +6441,10 @@ export {
|
|
|
6189
6441
|
resolveProjectInfo,
|
|
6190
6442
|
retirementSignal,
|
|
6191
6443
|
revertedShaFromCommit,
|
|
6444
|
+
reviewLearningsToDrafts,
|
|
6192
6445
|
runRegexSensor,
|
|
6193
6446
|
runSensors,
|
|
6447
|
+
runTierContract,
|
|
6194
6448
|
runtimeJournalPath,
|
|
6195
6449
|
saveCodeMap,
|
|
6196
6450
|
saveConfig,
|
|
@@ -6199,6 +6453,7 @@ export {
|
|
|
6199
6453
|
scannableSensorTargets,
|
|
6200
6454
|
scoreRetrievalCase,
|
|
6201
6455
|
scoreSensorCase,
|
|
6456
|
+
scrubbedCommandEnv,
|
|
6202
6457
|
selectCommandSensors,
|
|
6203
6458
|
sensorAppliesToPath,
|
|
6204
6459
|
sensorLedgerPath,
|