@hivelore/core 0.39.2 → 0.43.2

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.js CHANGED
@@ -28,9 +28,18 @@ 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
- /** Regex source (for kind=regex), matched against added diff lines / file content. */
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(),
39
+ /** kind=ast: full ast-grep Rule object (`kind`, `inside`, `has`, `not`, `all`, `any`, …). */
40
+ rule: z.record(z.unknown()).optional(),
41
+ /** kind=ast: explicit language name for extensionless/non-standard files (e.g. python, go). */
42
+ language: z.string().optional(),
34
43
  /**
35
44
  * Optional "correct-usage" regex (kind=regex). When `pattern` (the risky call) matches but this
36
45
  * regex ALSO appears within a small window around the match, the catch is SUPPRESSED — the diff
@@ -56,6 +65,13 @@ var SensorSchema = z.object({
56
65
  * the incident the test exists to prevent". Surfaced in the block message and the prevention receipt.
57
66
  */
58
67
  incident: z.string().optional(),
68
+ /**
69
+ * kind=shell|test only: the oracle was PROVEN to fail (RED) on the incident state at arming time
70
+ * (`sensors propose --red-ref`): the command passed on the presumed-correct tree AND failed on
71
+ * the replayed incident tree. Distinguishes "a test is routed" from "the test demonstrably
72
+ * catches the incident". Surfaced in the prevention receipt.
73
+ */
74
+ red_proven: z.boolean().optional(),
59
75
  /** `warn` surfaces in review; `block` can hard-block the commit (only when the gate opts in). */
60
76
  severity: z.enum(["warn", "block"]).default("warn"),
61
77
  /** True when Hivelore generated this sensor automatically (vs. hand-authored). */
@@ -951,10 +967,11 @@ function buildPreventionReceipt(events, memories, usage, options) {
951
967
  stage: event.stage ?? null,
952
968
  exit_code: event.exit_code ?? null,
953
969
  message: sensor?.message ?? null,
954
- incident: sensor?.incident ?? null
970
+ incident: sensor?.incident ?? null,
971
+ red_proven: sensor?.red_proven === true
955
972
  };
956
973
  }).sort((a, b) => b.at.localeCompare(a.at));
957
- const preventedCountTotal = Object.values(usage.by_id).reduce((sum, item) => sum + item.prevented_count, 0);
974
+ const preventedCountTotal = Object.values(usage.by_id).reduce((sum, item) => sum + (item.prevented_count ?? 0), 0);
958
975
  return {
959
976
  generated_at: now.toISOString(),
960
977
  since: options.since.toISOString(),
@@ -976,7 +993,8 @@ function renderPreventionReceipt(receipt) {
976
993
  const exit = row.exit_code === null ? "" : `, exit ${row.exit_code}`;
977
994
  const stage = row.stage ? ` \u2014 caught at ${row.stage}` : "";
978
995
  const incident = row.incident ? ` \u21A9 incident: ${row.incident}` : "";
979
- lines.push(` \u2717\u2192\u2713 ${row.at.slice(0, 10)} ${row.id.padEnd(32)} (${kind}${exit}${stage})${incident}`);
996
+ const red = row.red_proven ? " \u2713 RED-proven" : "";
997
+ lines.push(` \u2717\u2192\u2713 ${row.at.slice(0, 10)} ${row.id.padEnd(32)} (${kind}${exit}${stage})${incident}${red}`);
980
998
  }
981
999
  lines.push(
982
1000
  ` Trend: ${receipt.total} this window vs ${receipt.previous_total} previous window (${receipt.total <= receipt.previous_total ? "recurrences declining" : "recurrences rising"}).`
@@ -1175,6 +1193,43 @@ async function recordProjectContextEmission(paths, hash, now = Date.now()) {
1175
1193
  });
1176
1194
  }
1177
1195
 
1196
+ // src/priority.ts
1197
+ var DEFAULT_PRIORITY_SIGNALS = {
1198
+ type: "",
1199
+ tags: [],
1200
+ requiresHumanApproval: false,
1201
+ directAnchor: false,
1202
+ directSymbol: false,
1203
+ exactTaskMatch: false,
1204
+ strongSemantic: false,
1205
+ usefulSemantic: false,
1206
+ moduleOrDomainMatch: false,
1207
+ tagTaskMatch: false
1208
+ };
1209
+ function prioritySignals(partial) {
1210
+ return { ...DEFAULT_PRIORITY_SIGNALS, ...partial };
1211
+ }
1212
+ function classifyMemoryPriority(signals) {
1213
+ const isNegative = signals.type === "attempt";
1214
+ const isSkill2 = signals.type === "skill";
1215
+ if (signals.requiresHumanApproval || signals.directAnchor || signals.directSymbol || isNegative && (signals.exactTaskMatch || signals.strongSemantic) || isSkill2 && (signals.exactTaskMatch || signals.strongSemantic)) {
1216
+ return "must_read";
1217
+ }
1218
+ if (isStackPackSeed({ tags: signals.tags }) || isEnvWorkaroundMemory({ tags: signals.tags })) {
1219
+ if (isStackPackSeed({ tags: signals.tags }) && (signals.exactTaskMatch || signals.strongSemantic)) {
1220
+ return "useful";
1221
+ }
1222
+ return "background";
1223
+ }
1224
+ if (isSkill2 || signals.moduleOrDomainMatch || signals.exactTaskMatch || signals.usefulSemantic || signals.tagTaskMatch) {
1225
+ return "useful";
1226
+ }
1227
+ return "background";
1228
+ }
1229
+ function priorityRank(priority) {
1230
+ return priority === "must_read" ? 3 : priority === "useful" ? 2 : 1;
1231
+ }
1232
+
1178
1233
  // src/eval.ts
1179
1234
  function round32(n) {
1180
1235
  return Math.round(n * 1e3) / 1e3;
@@ -1302,6 +1357,69 @@ function synthesizeSelfEvalCases(memories, options = {}) {
1302
1357
  }
1303
1358
  return cases;
1304
1359
  }
1360
+ function appendProposedRetrievalCases(specRaw, cases) {
1361
+ let spec = {};
1362
+ if (specRaw?.trim()) {
1363
+ try {
1364
+ spec = JSON.parse(specRaw);
1365
+ } catch {
1366
+ spec = {};
1367
+ }
1368
+ }
1369
+ const existingNames = /* @__PURE__ */ new Set([
1370
+ ...(spec.retrieval ?? []).map((c) => c.name),
1371
+ ...(spec.proposed_retrieval ?? []).map((c) => c.name)
1372
+ ]);
1373
+ const fresh = cases.filter((c) => !existingNames.has(c.name));
1374
+ if (fresh.length > 0) spec.proposed_retrieval = [...spec.proposed_retrieval ?? [], ...fresh];
1375
+ return JSON.stringify(spec, null, 2) + "\n";
1376
+ }
1377
+ function approveProposedCases(specRaw) {
1378
+ let spec;
1379
+ try {
1380
+ spec = JSON.parse(specRaw);
1381
+ } catch {
1382
+ return { raw: specRaw, approved: 0 };
1383
+ }
1384
+ const proposed = spec.proposed_retrieval ?? [];
1385
+ if (proposed.length === 0) return { raw: specRaw, approved: 0 };
1386
+ spec.retrieval = [...spec.retrieval ?? [], ...proposed];
1387
+ delete spec.proposed_retrieval;
1388
+ return { raw: JSON.stringify(spec, null, 2) + "\n", approved: proposed.length };
1389
+ }
1390
+ function runTierContract() {
1391
+ const cases = [
1392
+ {
1393
+ name: "stack-pack seed + strong task evidence \u2192 useful (rescue path stays alive)",
1394
+ expected: "useful",
1395
+ signals: prioritySignals({ type: "convention", tags: ["stack-pack"], strongSemantic: true, usefulSemantic: true })
1396
+ },
1397
+ {
1398
+ name: "stack-pack seed + weak evidence \u2192 background (crowding guard stays)",
1399
+ expected: "background",
1400
+ signals: prioritySignals({ type: "convention", tags: ["stack-pack"], usefulSemantic: true, tagTaskMatch: true })
1401
+ },
1402
+ {
1403
+ name: "env workaround + strong evidence \u2192 background (hard cap stays)",
1404
+ expected: "background",
1405
+ signals: prioritySignals({ type: "gotcha", tags: ["dev-env"], strongSemantic: true, exactTaskMatch: true })
1406
+ },
1407
+ {
1408
+ name: "direct anchor \u2192 must_read (anchors always win)",
1409
+ expected: "must_read",
1410
+ signals: prioritySignals({ type: "convention", directAnchor: true })
1411
+ },
1412
+ {
1413
+ name: "attempt + exact task match \u2192 must_read (negative knowledge first)",
1414
+ expected: "must_read",
1415
+ signals: prioritySignals({ type: "attempt", exactTaskMatch: true })
1416
+ }
1417
+ ];
1418
+ return cases.map(({ name, expected, signals }) => {
1419
+ const actual = classifyMemoryPriority(signals);
1420
+ return { name, expected, actual, pass: actual === expected };
1421
+ });
1422
+ }
1305
1423
 
1306
1424
  // src/confidence.ts
1307
1425
  var DEFAULT_CONFIDENCE_THRESHOLDS = {
@@ -2445,6 +2563,8 @@ var DEFAULT_CONFIG = {
2445
2563
  antiPatternGate: "anchored",
2446
2564
  bootstrapGate: "block",
2447
2565
  humanCommits: "relaxed",
2566
+ commandSensorUnrunnable: "warn",
2567
+ sensorWeakeningGate: "warn",
2448
2568
  scoreThreshold: 80,
2449
2569
  cleanupGeneratedArtifacts: true,
2450
2570
  toolProfile: "enforcement",
@@ -2479,6 +2599,8 @@ var AUTOPILOT_DEFAULTS = {
2479
2599
  antiPatternGate: "anchored",
2480
2600
  bootstrapGate: "block",
2481
2601
  humanCommits: "relaxed",
2602
+ commandSensorUnrunnable: "warn",
2603
+ sensorWeakeningGate: "warn",
2482
2604
  scoreThreshold: 85,
2483
2605
  cleanupGeneratedArtifacts: true,
2484
2606
  toolProfile: "enforcement",
@@ -3958,6 +4080,34 @@ function runSensors(memories, targets) {
3958
4080
  }
3959
4081
  return hits;
3960
4082
  }
4083
+ var COMMAND_ENV_EXACT = /* @__PURE__ */ new Set([
4084
+ "PATH",
4085
+ "HOME",
4086
+ "LANG",
4087
+ "LANGUAGE",
4088
+ "TMPDIR",
4089
+ "TMP",
4090
+ "TEMP",
4091
+ "TERM",
4092
+ "SHELL",
4093
+ "USER",
4094
+ "LOGNAME",
4095
+ "PWD",
4096
+ "CI",
4097
+ "COLORTERM",
4098
+ "TZ"
4099
+ ]);
4100
+ var COMMAND_ENV_PREFIXES = ["LC_", "NODE_", "NVM_", "npm_", "HIVELORE_", "HAIVE_"];
4101
+ function scrubbedCommandEnv(env) {
4102
+ const out = {};
4103
+ for (const [key, value] of Object.entries(env)) {
4104
+ if (value === void 0) continue;
4105
+ if (COMMAND_ENV_EXACT.has(key) || COMMAND_ENV_PREFIXES.some((p) => key.startsWith(p))) {
4106
+ out[key] = value;
4107
+ }
4108
+ }
4109
+ return out;
4110
+ }
3961
4111
  function incidentSuffix(incident) {
3962
4112
  const ref = incident?.trim();
3963
4113
  return ref ? ` \u21A9 guards incident: ${ref}` : "";
@@ -3986,6 +4136,34 @@ function selectCommandSensors(memories, changedPaths) {
3986
4136
  }
3987
4137
  return specs;
3988
4138
  }
4139
+ function addedLineNumbersFromDiff(diff) {
4140
+ const result = /* @__PURE__ */ new Map();
4141
+ let currentPath = null;
4142
+ let newLine = 0;
4143
+ for (const line of diff.split("\n")) {
4144
+ if (line.startsWith("+++ ")) {
4145
+ const raw = line.slice(4).trim();
4146
+ currentPath = raw === "/dev/null" ? null : normalizeProjectPath(raw);
4147
+ continue;
4148
+ }
4149
+ const hunk = line.match(/^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/);
4150
+ if (hunk) {
4151
+ newLine = Number(hunk[1]);
4152
+ continue;
4153
+ }
4154
+ if (!currentPath) continue;
4155
+ if (line.startsWith("+") && !line.startsWith("+++")) {
4156
+ let set = result.get(currentPath);
4157
+ if (!set) result.set(currentPath, set = /* @__PURE__ */ new Set());
4158
+ set.add(newLine);
4159
+ newLine++;
4160
+ } else if (line.startsWith("-") && !line.startsWith("---")) {
4161
+ } else if (!line.startsWith("\\")) {
4162
+ newLine++;
4163
+ }
4164
+ }
4165
+ return result;
4166
+ }
3989
4167
  function sensorTargetsFromDiff(diff) {
3990
4168
  const targets = [];
3991
4169
  let currentPath = null;
@@ -4050,6 +4228,7 @@ function diffFileChanges(diff) {
4050
4228
  const newPath = raw === "/dev/null" ? null : normalizeProjectPath(raw);
4051
4229
  current = {
4052
4230
  path: newPath ?? oldPath ?? "",
4231
+ created: oldPath === null && newPath !== null,
4053
4232
  deleted: newPath === null,
4054
4233
  removed: [],
4055
4234
  added: []
@@ -4077,6 +4256,7 @@ function detectSensorWeakening(diff) {
4077
4256
  weakenings.push({ file: file.path, memory_id: memoryId, change, detail });
4078
4257
  };
4079
4258
  const removedBlockSeverity = file.removed.some((l) => SEVERITY_BLOCK_RE.test(l));
4259
+ if (file.created) continue;
4080
4260
  if (file.deleted) {
4081
4261
  if (file.removed.some((l) => SENSOR_BLOCK_START_RE.test(l)) && removedBlockSeverity) {
4082
4262
  flag("memory-deleted", "memory file with a block sensor deleted");
@@ -4099,7 +4279,10 @@ function detectSensorWeakening(diff) {
4099
4279
  }
4100
4280
  const removedAbsent = file.removed.find((l) => SENSOR_ABSENT_KEY_RE.test(l));
4101
4281
  const addedAbsent = file.added.find((l) => SENSOR_ABSENT_KEY_RE.test(l));
4102
- if (addedAbsent !== void 0 && addedAbsent.trim() !== removedAbsent?.trim()) {
4282
+ const addingNewSensorBlock = file.added.some((l) => SENSOR_BLOCK_START_RE.test(l)) && !file.removed.some(
4283
+ (l) => SENSOR_BLOCK_START_RE.test(l) || SENSOR_ORACLE_KEY_RE.test(l) || SEVERITY_BLOCK_RE.test(l) || SEVERITY_WARN_RE.test(l)
4284
+ );
4285
+ if (!addingNewSensorBlock && addedAbsent !== void 0 && addedAbsent.trim() !== removedAbsent?.trim()) {
4103
4286
  flag("suppression-broadened", removedAbsent === void 0 ? "absent marker added" : "absent marker changed");
4104
4287
  }
4105
4288
  if (file.removed.some((l) => SENSOR_BLOCK_START_RE.test(l)) && removedBlockSeverity && !file.added.some((l) => SENSOR_BLOCK_START_RE.test(l))) {
@@ -5407,6 +5590,33 @@ function findUncapturedFailures(failures, captureTimes, options = {}) {
5407
5590
  out.sort((a, b) => a.ts.localeCompare(b.ts));
5408
5591
  return out;
5409
5592
  }
5593
+ var EXPLORATORY_RE = /^(ls|find|grep|rg|cat|head|tail|which|stat)\b/i;
5594
+ function distillFailureObservations(failures, options = {}) {
5595
+ const max = options.max ?? 3;
5596
+ const clusters = /* @__PURE__ */ new Map();
5597
+ for (const f of failures) {
5598
+ const summary = f.summary.trim();
5599
+ if (!summary || EXPLORATORY_RE.test(summary.replace(/^Bash:\s*/i, ""))) continue;
5600
+ const key = normalizeSummary(summary);
5601
+ const existing = clusters.get(key);
5602
+ if (existing) {
5603
+ existing.count++;
5604
+ for (const file of f.files ?? []) existing.files.add(file);
5605
+ } else {
5606
+ clusters.set(key, { first: f, count: 1, files: new Set(f.files ?? []) });
5607
+ }
5608
+ }
5609
+ return [...clusters.values()].sort((a, b) => b.count - a.count || b.first.ts.localeCompare(a.first.ts)).slice(0, max).map(({ first, count, files }) => {
5610
+ const summary = first.summary.trim();
5611
+ const firstLine = summary.split("\n")[0].slice(0, 120);
5612
+ return {
5613
+ what: `${first.tool} failed: ${firstLine}`,
5614
+ why_failed: summary.slice(0, 400),
5615
+ paths: [...files].slice(0, 6),
5616
+ occurrences: count
5617
+ };
5618
+ });
5619
+ }
5410
5620
 
5411
5621
  // src/coverage.ts
5412
5622
  var DEFAULT_COVERING_TYPES = ["decision", "convention", "gotcha", "architecture"];
@@ -5906,43 +6116,6 @@ async function handoffAgeMs(root, now = /* @__PURE__ */ new Date()) {
5906
6116
  }
5907
6117
  }
5908
6118
 
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
6119
  // src/agent-context.ts
5947
6120
  var AGENT_ENV_SIGNALS = [
5948
6121
  { name: "HAIVE_SESSION_ID", label: "hivelore-run-wrapper" },
@@ -5960,6 +6133,92 @@ function detectAgentContext(env = typeof process !== "undefined" ? process.env :
5960
6133
  const signals = AGENT_ENV_SIGNALS.filter(({ name }) => (env[name] ?? "").trim().length > 0).map(({ name, label }) => `${label} (${name})`);
5961
6134
  return { agent: signals.length > 0, signals: [...new Set(signals)] };
5962
6135
  }
6136
+
6137
+ // src/pr-review-ingest.ts
6138
+ var REVIEW_LEARNING_MARKER = /(^|\s)\/?hivelore[:,]?\s+remember\b|(^|\s)hivelore:/i;
6139
+ 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;
6140
+ var MAX_INSTRUCTION_CHARS = 500;
6141
+ var MIN_INSTRUCTION_CHARS = 12;
6142
+ function prNumberFrom(comment) {
6143
+ const m = comment.pull_request_url?.match(/\/pulls\/(\d+)$/);
6144
+ return m ? Number(m[1]) : void 0;
6145
+ }
6146
+ function extractReviewLearnings(payload) {
6147
+ if (!Array.isArray(payload)) return [];
6148
+ const comments = payload;
6149
+ const learnings = [];
6150
+ for (const comment of comments) {
6151
+ if (typeof comment?.id !== "number") continue;
6152
+ if ((comment.user?.type ?? "").toLowerCase() === "bot") continue;
6153
+ const body = (comment.body ?? "").trim();
6154
+ if (body.length < MIN_INSTRUCTION_CHARS) continue;
6155
+ const marked = REVIEW_LEARNING_MARKER.test(body);
6156
+ if (!marked && !INSTRUCTION_RE.test(body)) continue;
6157
+ const instruction = body.replace(REVIEW_LEARNING_MARKER, " ").replace(/\s+/g, " ").trim().slice(0, MAX_INSTRUCTION_CHARS);
6158
+ if (instruction.length < MIN_INSTRUCTION_CHARS) continue;
6159
+ learnings.push({
6160
+ thread_id: comment.in_reply_to_id ?? comment.id,
6161
+ comment_id: comment.id,
6162
+ ...comment.path ? { path: comment.path } : {},
6163
+ ...typeof (comment.line ?? comment.original_line) === "number" ? { line: comment.line ?? comment.original_line } : {},
6164
+ author: comment.user?.login ?? "reviewer",
6165
+ instruction,
6166
+ ...comment.html_url ? { url: comment.html_url } : {},
6167
+ ...prNumberFrom(comment) !== void 0 ? { pr_number: prNumberFrom(comment) } : {}
6168
+ });
6169
+ }
6170
+ return learnings;
6171
+ }
6172
+ function reviewLearningsToDrafts(learnings, options = {}) {
6173
+ const limit = options.limit ?? 20;
6174
+ const drafts = [];
6175
+ const seenThreads = /* @__PURE__ */ new Set();
6176
+ for (const learning of learnings) {
6177
+ if (drafts.length >= limit) break;
6178
+ if (seenThreads.has(learning.thread_id)) {
6179
+ const idx = drafts.findIndex((d) => d.key === `github-pr:${learning.thread_id}`);
6180
+ if (idx >= 0) drafts.splice(idx, 1);
6181
+ }
6182
+ seenThreads.add(learning.thread_id);
6183
+ const key = `github-pr:${learning.thread_id}`;
6184
+ const slugSource = learning.instruction.toLowerCase().replace(/[^a-z0-9\s]/g, " ");
6185
+ const slug = slugSource.trim().split(/\s+/).slice(0, 6).join("-") || "review-learning";
6186
+ const finding = {
6187
+ tool: "github-pr",
6188
+ ruleId: "review-learning",
6189
+ severity: "major",
6190
+ message: learning.instruction,
6191
+ path: learning.path ?? "",
6192
+ ...learning.line !== void 0 ? { line: learning.line } : {},
6193
+ key
6194
+ };
6195
+ const baseFm = buildFrontmatter({
6196
+ type: "convention",
6197
+ slug,
6198
+ scope: options.scope ?? "team",
6199
+ module: options.module,
6200
+ tags: ["review-learning"],
6201
+ paths: learning.path ? [learning.path] : [],
6202
+ author: options.author ?? learning.author
6203
+ });
6204
+ const frontmatter = { ...baseFm, status: "proposed", topic: `ingest:${key}` };
6205
+ const provenance = [
6206
+ learning.pr_number !== void 0 ? `PR #${learning.pr_number}` : null,
6207
+ `@${learning.author}`,
6208
+ learning.url ?? null
6209
+ ].filter(Boolean).join(" \xB7 ");
6210
+ const body = `# Review learning: ${learning.instruction.slice(0, 80)}
6211
+
6212
+ ${learning.instruction}
6213
+
6214
+ ` + (learning.path ? `Applies to: \`${learning.path}\`${learning.line !== void 0 ? ` (line ${learning.line} at review time)` : ""}
6215
+
6216
+ ` : "") + `_From a review thread (${provenance}). Review: approve, refine, or reject \u2014 then consider \`hivelore sensors propose\` to make it a deterministic gate._
6217
+ `;
6218
+ drafts.push({ key, topic: `ingest:${key}`, frontmatter, body, finding, has_sensor: false });
6219
+ }
6220
+ return drafts;
6221
+ }
5963
6222
  export {
5964
6223
  AUTOPILOT_DEFAULTS,
5965
6224
  ActivationSchema,
@@ -5998,6 +6257,7 @@ export {
5998
6257
  PREVENTION_DEBOUNCE_MS,
5999
6258
  PROJECT_CONTEXT_FILE,
6000
6259
  PROJECT_CONTEXT_THROTTLE_MS,
6260
+ REVIEW_LEARNING_MARKER,
6001
6261
  RUNTIME_JOURNAL_FILENAME,
6002
6262
  SCAFFOLD_MARKER_RE,
6003
6263
  SEED_QUALITY_FLOOR,
@@ -6010,6 +6270,7 @@ export {
6010
6270
  USAGE_FILE,
6011
6271
  USAGE_LOG_DIR,
6012
6272
  USAGE_LOG_FILE,
6273
+ addedLineNumbersFromDiff,
6013
6274
  addedLinesFromDiff,
6014
6275
  aggregateRetrieval,
6015
6276
  aggregateSensors,
@@ -6018,11 +6279,13 @@ export {
6018
6279
  antiPatternGateParams,
6019
6280
  appendEvalHistory,
6020
6281
  appendPreventionEvent,
6282
+ appendProposedRetrievalCases,
6021
6283
  appendRuntimeJournalEntry,
6022
6284
  appendSensorEvaluations,
6023
6285
  appendUsageEvent,
6024
6286
  applyConflictResolution,
6025
6287
  applyFeedbackAdjustment,
6288
+ approveProposedCases,
6026
6289
  assessBootstrapState,
6027
6290
  assessScaffoldLoop,
6028
6291
  assessSensorHealth,
@@ -6064,6 +6327,7 @@ export {
6064
6327
  detectStacksFromManifests,
6065
6328
  diffContract,
6066
6329
  diffHasDistinctiveOverlap,
6330
+ distillFailureObservations,
6067
6331
  distinctiveCap,
6068
6332
  draftsFromFindings,
6069
6333
  emptyUsage,
@@ -6075,6 +6339,7 @@ export {
6075
6339
  existingGateMissShas,
6076
6340
  extractActionsBriefBody,
6077
6341
  extractReferencedPaths,
6342
+ extractReviewLearnings,
6078
6343
  extractSensorExamples,
6079
6344
  extractSnippet,
6080
6345
  extractTestFilePathsFromCommand,
@@ -6189,8 +6454,10 @@ export {
6189
6454
  resolveProjectInfo,
6190
6455
  retirementSignal,
6191
6456
  revertedShaFromCommit,
6457
+ reviewLearningsToDrafts,
6192
6458
  runRegexSensor,
6193
6459
  runSensors,
6460
+ runTierContract,
6194
6461
  runtimeJournalPath,
6195
6462
  saveCodeMap,
6196
6463
  saveConfig,
@@ -6199,6 +6466,7 @@ export {
6199
6466
  scannableSensorTargets,
6200
6467
  scoreRetrievalCase,
6201
6468
  scoreSensorCase,
6469
+ scrubbedCommandEnv,
6202
6470
  selectCommandSensors,
6203
6471
  sensorAppliesToPath,
6204
6472
  sensorLedgerPath,