hippo-memory 1.36.0 → 1.38.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.
@@ -13,6 +13,22 @@ export declare function captureError(exitCode: number, stderr: string, command:
13
13
  * Looks for fix:, revert:, bug:, error:, hotfix: commit messages.
14
14
  */
15
15
  export declare function extractLessons(gitLog: string, customPatterns?: string[]): string[];
16
+ /**
17
+ * Split parsed lessons into ones worth storing and low-information ones.
18
+ *
19
+ * `extractLessons` is a published API surface (exported from src/index.ts)
20
+ * and does one job: parse a git log into candidate lesson strings. Adding a
21
+ * quality check inside it would break that contract and silently change
22
+ * what external callers get back. So parsing and admission are separate
23
+ * steps: this function is the write-path gate, called by each caller that
24
+ * actually stores a lesson, not by the parser itself.
25
+ *
26
+ * Order is preserved in both output arrays.
27
+ */
28
+ export declare function partitionLessons(lessons: string[]): {
29
+ kept: string[];
30
+ dropped: string[];
31
+ };
16
32
  /**
17
33
  * Check if a substantially similar memory already exists.
18
34
  * Returns true if overlap > threshold (default 0.7).
@@ -1 +1 @@
1
- {"version":3,"file":"autolearn.d.ts","sourceRoot":"","sources":["../src/autolearn.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,WAAW,EAAuB,MAAM,aAAa,CAAC;AAI/D;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd,WAAW,CAoBb;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAsClF;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,kBAAkB,EAAE,MAAM,GAAG,WAAW,EAAE,EAC1C,MAAM,EAAE,MAAM,EACd,SAAS,SAAM,EACf,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAWT;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAwBzF;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAY9C;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAS7D"}
1
+ {"version":3,"file":"autolearn.d.ts","sourceRoot":"","sources":["../src/autolearn.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,WAAW,EAAuB,MAAM,aAAa,CAAC;AAK/D;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,MAAM,GACd,WAAW,CAoBb;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAsClF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG;IAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAWzF;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,kBAAkB,EAAE,MAAM,GAAG,WAAW,EAAE,EAC1C,MAAM,EAAE,MAAM,EACd,SAAS,SAAM,EACf,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAWT;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAwBzF;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAY9C;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAS7D"}
package/dist/autolearn.js CHANGED
@@ -6,6 +6,7 @@ import { execSync, execFileSync, spawn } from 'child_process';
6
6
  import { createMemory, Layer } from './memory.js';
7
7
  import { loadAllEntries } from './store.js';
8
8
  import { textOverlap } from './search.js';
9
+ import { isContentWorthStoring } from './audit.js';
9
10
  /**
10
11
  * Create a MemoryEntry capturing a command failure.
11
12
  * Content format: "Command '<cmd>' failed: <truncated stderr>"
@@ -68,6 +69,31 @@ export function extractLessons(gitLog, customPatterns) {
68
69
  // Deduplicate exact matches at extraction time
69
70
  return [...new Set(lessons)];
70
71
  }
72
+ /**
73
+ * Split parsed lessons into ones worth storing and low-information ones.
74
+ *
75
+ * `extractLessons` is a published API surface (exported from src/index.ts)
76
+ * and does one job: parse a git log into candidate lesson strings. Adding a
77
+ * quality check inside it would break that contract and silently change
78
+ * what external callers get back. So parsing and admission are separate
79
+ * steps: this function is the write-path gate, called by each caller that
80
+ * actually stores a lesson, not by the parser itself.
81
+ *
82
+ * Order is preserved in both output arrays.
83
+ */
84
+ export function partitionLessons(lessons) {
85
+ const kept = [];
86
+ const dropped = [];
87
+ for (const lesson of lessons) {
88
+ if (isContentWorthStoring(lesson)) {
89
+ kept.push(lesson);
90
+ }
91
+ else {
92
+ dropped.push(lesson);
93
+ }
94
+ }
95
+ return { kept, dropped };
96
+ }
71
97
  /**
72
98
  * Check if a substantially similar memory already exists.
73
99
  * Returns true if overlap > threshold (default 0.7).
@@ -1 +1 @@
1
- {"version":3,"file":"autolearn.js","sourceRoot":"","sources":["../src/autolearn.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"autolearn.js","sourceRoot":"","sources":["../src/autolearn.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AA2BH,OAAO,EASL,WAAW,EAEZ,MAAM,aAAa,CAAC;AA4GrB,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AA4ChC,kFAAkF;AAClF,wBAAgB,8BAA8B,IAAI,IAAI,CAErD;AA4ID,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,CAAC,CAAA;CAAE,CAwCjI;AAutED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,MAAqB,GAAG,MAAM,CAyF3F;AAyGD;;;GAGG;AACH,oHAAoH;AACpH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,GAAG,CAAC,WAAW,GAAG,IAAI,CAuD/D;AA2jGD,oHAAoH;AACpH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,EACtF,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,MAAkB,GAC1B,IAAI,CA6BN"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AA2BH,OAAO,EASL,WAAW,EAEZ,MAAM,aAAa,CAAC;AA6GrB,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AA4ChC,kFAAkF;AAClF,wBAAgB,8BAA8B,IAAI,IAAI,CAErD;AA4ID,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,EAAE,CAAC,CAAA;CAAE,CAwCjI;AA6xED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,MAAqB,GAAG,MAAM,CAyF3F;AAyGD;;;GAGG;AACH,oHAAoH;AACpH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,GAAG,CAAC,WAAW,GAAG,IAAI,CAuD/D;AA2jGD,oHAAoH;AACpH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,KAAK,CAAC;IAAE,KAAK,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAC,EACtF,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,MAAkB,GAC1B,IAAI,CA6BN"}
package/dist/cli.js CHANGED
@@ -54,7 +54,7 @@ import { loadConfig } from './config.js';
54
54
  import { openHippoDb, closeHippoDb } from './db.js';
55
55
  import { pushGoal, getActiveGoals, completeGoal, suspendGoal, resumeGoal, applyGoalStackBoost } from './goals.js';
56
56
  import { rowToGoal } from './goals.js';
57
- import { captureError, extractLessons, deduplicateLesson, runWatched, fetchGitLog, isGitRepo, } from './autolearn.js';
57
+ import { captureError, extractLessons, partitionLessons, deduplicateLesson, runWatched, fetchGitLog, isGitRepo, } from './autolearn.js';
58
58
  import { extractInvalidationTarget, invalidateMatching } from './invalidation.js';
59
59
  import { extractPathTags } from './path-context.js';
60
60
  import { detectScope } from './scope.js';
@@ -316,6 +316,11 @@ function cmdInitScan(scanDir, flags) {
316
316
  console.log(`Initialized global store at ${globalRoot}\n`);
317
317
  }
318
318
  let totalLessons = 0;
319
+ // Rolled up so the cross-repo summary reports the gate too. Each repo
320
+ // already prints its own count inside learnFromRepo, but the aggregate
321
+ // line showed only what was ADDED - and the point of this change is
322
+ // that a dropped subject is never invisible.
323
+ let totalLowInfo = 0;
319
324
  const seedDays = parseInt(String(flags['days'] ?? '365'), 10);
320
325
  for (const repo of repos) {
321
326
  const name = path.basename(repo);
@@ -331,12 +336,15 @@ function cmdInitScan(scanDir, flags) {
331
336
  const result = learnFromRepo(repoHippo, repo, seedDays, name);
332
337
  added = result.added;
333
338
  totalLessons += added;
339
+ totalLowInfo += result.lowInfo;
334
340
  }
335
341
  const status = alreadyExists ? 'existing' : 'new';
336
342
  const entries = loadAllEntries(repoHippo);
337
343
  console.log(` ${name.padEnd(25)} ${status.padEnd(10)} ${entries.length} memories${added > 0 ? ` (+${added} from git)` : ''}`);
338
344
  }
339
- console.log(`\n${repos.length} repositories, ${totalLessons} new lessons learned.`);
345
+ console.log(`\n${repos.length} repositories, ${totalLessons} new lessons learned` +
346
+ (totalLowInfo > 0 ? `, ${totalLowInfo} low-information subject(s) dropped` : '') +
347
+ '.');
340
348
  console.log(`Global store: ${globalRoot}`);
341
349
  if (!flags['no-schedule']) {
342
350
  setupDailySchedule(globalRoot);
@@ -781,9 +789,21 @@ async function cmdRecall(hippoRoot, query, flags) {
781
789
  let globalEntries = isInitialized(globalRoot) ? loadRecallSearchEntries(globalRoot, query, undefined, tenantId, requestedScopeForFilter, 'additive') : [];
782
790
  // v1.12.13 / C5 — WYSIATI counters. Track filter activity per the plan v3
783
791
  // Task 3 mapping table. dropped_pre_rank is the SUM of all non-budget
784
- // filter drops (pre-rank AND post-rank). Search-engine internal drops
785
- // (scored-to-zero rows that hybridSearch/physicsSearch returns fewer of)
786
- // are NOT counted in v1 they are part of the rank step, not a filter.
792
+ // filter drops (pre-rank AND post-rank); its meaning is unchanged by C5.
793
+ //
794
+ // C5 (2026-08-24): search-engine internal drops (scored-to-zero rows that
795
+ // hybridSearch/physicsSearch returns fewer of than they were given) now
796
+ // count toward droppedByBudget, not "not counted at all" as the old v1
797
+ // convention had it. That old convention is exactly why the `Cutoff:` line
798
+ // never printed: cmdRecall measured droppedByBudget from `results.length -
799
+ // limit` (cli.ts ~1547) AFTER the search call, but `results` had already
800
+ // been ranked and truncated by the search engine to a handful of rows, so
801
+ // `limit < results.length` was almost always false and the counter stayed
802
+ // 0 while hundreds of candidates silently vanished (see the plan's measured
803
+ // table: 397 of 400 candidates gone, every counter reading 0). droppedByBudget
804
+ // is now derived as "everything not attributed to a named pre-rank filter",
805
+ // computed after the final `--limit` slice — see the definition near
806
+ // line ~1545 for the exact formula and the double-count argument.
787
807
  // totalCandidates = post-SQL-predicate count (api.recall parity: measured
788
808
  // after loadRecallSearchEntries, before the JS scope filter). NOTE the
789
809
  // v1.12.13 accounting convention: SQL-excluded rows (quarantine + the
@@ -792,6 +812,9 @@ async function cmdRecall(hippoRoot, query, flags) {
792
812
  // defense-in-depth (LIKE/regex divergence, exact-mode mismatch).
793
813
  const totalCandidatesCountCmd = localEntries.length + globalEntries.length;
794
814
  let droppedPreRankCountCmd = 0;
815
+ // Graph expansion adds candidates AFTER totalCandidatesCountCmd is taken,
816
+ // so they are folded back in before the budget residual is derived.
817
+ let graphAddedCountCmd = 0;
795
818
  // v1.25.0: JS half of the recall scope rule (private-scope regex deny with
796
819
  // explicit-request unlock), via the canonical helper — do not inline a
797
820
  // fourth copy of this predicate.
@@ -979,6 +1002,19 @@ async function cmdRecall(hippoRoot, query, flags) {
979
1002
  }
980
1003
  }
981
1004
  if (hops > 0) {
1005
+ // graphExpandRecall can SURFACE rows that were never in the lexical
1006
+ // candidate pool (a graph neighbour reached by entity edge, not by
1007
+ // query match), and totalCandidatesCountCmd was snapshotted before the
1008
+ // search. Without this the derived budget count goes negative, clamps
1009
+ // to 0, and the accounting silently breaks: 1 candidate, 2 returned,
1010
+ // 0 drops. Found independently by two reviewers. Count the additions
1011
+ // so the invariant holds on graph-expanded recalls too.
1012
+ // GROSS, not net. graphExpandRecall both adds neighbours AND evicts weak
1013
+ // base rows in one call (graph-recall.ts:285), so a net delta of 0 hides
1014
+ // 3 added + 3 evicted: the additions escape the candidate total and the
1015
+ // evictions escape the drop count, and the Cutoff line goes silent again.
1016
+ // Compare ID sets so both directions are counted.
1017
+ const beforeGraphIds = new Set(results.map((r) => r.entry.id));
982
1018
  results = graphExpandRecall(results, {
983
1019
  hops,
984
1020
  maxNeighbors,
@@ -993,6 +1029,11 @@ async function cmdRecall(hippoRoot, query, flags) {
993
1029
  ? { requested: recallExplicitScope, additive: true }
994
1030
  : {},
995
1031
  });
1032
+ // Rows the graph surfaced that the lexical pool never held.
1033
+ for (const r of results) {
1034
+ if (!beforeGraphIds.has(r.entry.id))
1035
+ graphAddedCountCmd++;
1036
+ }
996
1037
  }
997
1038
  }
998
1039
  // ACC EVC-adaptive recall (RESEARCH.md §PFC.ACC). When the initial top-K is
@@ -1339,12 +1380,29 @@ async function cmdRecall(hippoRoot, query, flags) {
1339
1380
  results = results.filter((r) => r.entry.layer === layerFilter);
1340
1381
  droppedPreRankCountCmd += beforeLayerFilter - results.length;
1341
1382
  }
1342
- // v1.12.13 / C5 — WYSIATI dropped_by_budget counter (final limit cut).
1343
- let droppedByBudgetCountCmd = 0;
1383
+ // v1.12.13 / C5 — WYSIATI dropped_by_budget counter. Apply the final
1384
+ // `--limit` slice first, then derive the count ARITHMETICALLY as
1385
+ // "everything lost that droppedPreRank did not already claim":
1386
+ //
1387
+ // droppedByBudget = totalCandidates - droppedPreRank - returned
1388
+ //
1389
+ // This is the invariant the plan requires (totalCandidates == droppedPreRank
1390
+ // + droppedByBudget + returned) restated as an assignment, so it holds by
1391
+ // construction rather than by two counters happening to agree. It also
1392
+ // cannot double-count the post-search droppedPreRank sites (--filter-
1393
+ // conflicts, --outcome, --layer, ~1270/1528/1541): those are subtracted
1394
+ // once here, not re-counted, because this line does not re-walk any filter
1395
+ // — it only compares the two totals already tracked above. Everything left
1396
+ // over — search-engine internal rank-step drops AND the `--limit` slice
1397
+ // itself — lands in droppedByBudget, per the C5 accounting change in the
1398
+ // comment near line ~976. Clamped at 0 as a defensive floor: if a future
1399
+ // pipeline change ever returns MORE rows than totalCandidates minus
1400
+ // droppedPreRank (should not happen), report "nothing dropped" rather than
1401
+ // a negative count.
1344
1402
  if (limit < results.length) {
1345
- droppedByBudgetCountCmd = results.length - limit;
1346
1403
  results = results.slice(0, limit);
1347
1404
  }
1405
+ const droppedByBudgetCountCmd = Math.max(0, totalCandidatesCountCmd + graphAddedCountCmd - droppedPreRankCountCmd - results.length);
1348
1406
  // v0.33 / J1 — CLI per-pipeline anchoring detector. Each pipeline (api.recall,
1349
1407
  // cmdRecall, MCP) computes its own AnchoringHint via the shared detectAnchoring
1350
1408
  // helper against its own top-1 + its own per-(tenant, session) ring buffer.
@@ -1385,7 +1443,13 @@ async function cmdRecall(hippoRoot, query, flags) {
1385
1443
  // interference; query_repeat is a re-ask, not memory competition).
1386
1444
  const cmdSuppressedByInterference = cmdAnchoringHint?.reason === 'memory_dominance' ? 1 : 0;
1387
1445
  const cmdSuppressionSummary = api.buildSuppressionSummary({
1388
- totalCandidates: totalCandidatesCountCmd,
1446
+ // PUBLISHED total includes graph-surfaced rows. Folding graphAdded into
1447
+ // the derivation but not into the reported total made the invariant hold
1448
+ // internally and break externally by exactly that count: JSON consumers
1449
+ // saw total != preRank + byBudget + returned, and the text line could
1450
+ // read "showing 8 of 10" having actually considered 12. The number a
1451
+ // caller sees must be the number the arithmetic used.
1452
+ totalCandidates: totalCandidatesCountCmd + graphAddedCountCmd,
1389
1453
  droppedPreRank: droppedPreRankCountCmd,
1390
1454
  droppedByBudget: droppedByBudgetCountCmd,
1391
1455
  summarySubstitutionsAdded: 0,
@@ -1761,8 +1825,12 @@ async function cmdRecall(hippoRoot, query, flags) {
1761
1825
  if (showWhy) {
1762
1826
  const s = cmdSuppressionSummary;
1763
1827
  const clauses = [];
1828
+ // "dropped to fit limit" pointed at the wrong control: the residual covers
1829
+ // search-ranking and token-budget drops too, and fires even when --limit
1830
+ // was never passed. Measured: `recall --budget 20 --why` printed "39
1831
+ // dropped to fit limit" with no --limit flag in the command at all.
1764
1832
  if (s.droppedByBudget > 0)
1765
- clauses.push(`${s.droppedByBudget} dropped to fit limit`);
1833
+ clauses.push(`${s.droppedByBudget} not shown (rank, budget or limit)`);
1766
1834
  if (s.droppedPreRank > 0)
1767
1835
  clauses.push(`${s.droppedPreRank} filtered pre-rank`);
1768
1836
  if (s.summarySubstitutionsAdded > 0)
@@ -5770,18 +5838,49 @@ function learnFromRepo(hippoRoot, repoPath, days, label) {
5770
5838
  const prefix = label ? `[${label}] ` : '';
5771
5839
  if (!isGitRepo(repoPath)) {
5772
5840
  console.log(`${prefix}No git history found (or not a git repository).`);
5773
- return { added: 0, skipped: 0 };
5841
+ return { added: 0, skipped: 0, lowInfo: 0 };
5774
5842
  }
5775
5843
  const gitLog = fetchGitLog(repoPath, days);
5776
5844
  if (!gitLog.trim()) {
5777
5845
  console.log(`${prefix}No fix/revert/bug commits found in the specified period.`);
5778
- return { added: 0, skipped: 0 };
5846
+ return { added: 0, skipped: 0, lowInfo: 0 };
5779
5847
  }
5780
- const lessons = extractLessons(gitLog);
5781
- if (lessons.length === 0) {
5848
+ const parsedLessons = extractLessons(gitLog);
5849
+ if (parsedLessons.length === 0) {
5782
5850
  console.log(`${prefix}No fix/revert/bug commits found in the specified period.`);
5783
- return { added: 0, skipped: 0 };
5851
+ return { added: 0, skipped: 0, lowInfo: 0 };
5784
5852
  }
5853
+ // DF4: admission gate lives at the write path, not in extractLessons
5854
+ // (a published API surface that only parses). Bare subjects like "fixed
5855
+ // signals" are dropped here, before they ever become a memory.
5856
+ // The gate filters the loop INPUT, so a dropped lesson neither stores nor
5857
+ // invalidates. That is deliberate, and it was argued both ways.
5858
+ //
5859
+ // Round 1 of review called the lost invalidation a P1: a migration subject
5860
+ // too thin to store ("replace webpack with vite") would stop weakening
5861
+ // stale webpack memories. True. So the loop was widened to walk every
5862
+ // parsed lesson with the gate on the write alone.
5863
+ //
5864
+ // Round 2 then found the cure was worse. STORAGE is what makes invalidation
5865
+ // idempotent here: a stored lesson is recognised by deduplicateLesson on
5866
+ // the next scan and short-circuits before invalidating again. A lesson that
5867
+ // invalidates but is never stored has no such record, so every rescan
5868
+ // re-invalidates, and invalidateMatching halves half_life_days each time.
5869
+ // Measured: 7 -> 3 -> 1 over two runs. That is compounding data damage.
5870
+ //
5871
+ // Measured frequency decided it. Across 413 real auto-learn rows in 4
5872
+ // stores, 24 are gated and ZERO of those carry an invalidation target; the
5873
+ // 45 lessons that do carry targets all pass the gate and are unaffected
5874
+ // either way. Both failure modes are empty on real data, so the tie breaks
5875
+ // on which one is benign if it ever fires: not invalidating is a missed
5876
+ // improvement, re-invalidating forever is damage.
5877
+ //
5878
+ // Documented limitation, pinned by test: a migration subject too thin to
5879
+ // store also does not invalidate. Making invalidateMatching idempotent
5880
+ // would allow both, and is backlogged - it is a latent issue for the manual
5881
+ // `hippo invalidate` path too, not just this one.
5882
+ const { kept: lessons, dropped } = partitionLessons(parsedLessons);
5883
+ const lowInfo = dropped.length;
5785
5884
  let added = 0;
5786
5885
  let skipped = 0;
5787
5886
  // AT1 (plan §3 containment): per-lesson refusal must not abort the rest
@@ -5841,8 +5940,9 @@ function learnFromRepo(hippoRoot, repoPath, days, label) {
5841
5940
  }
5842
5941
  console.log(`${prefix}${added} new lessons added, ${skipped} duplicates skipped` +
5843
5942
  (rejected > 0 ? `, ${rejected} rejected value(s) skipped` : '') +
5943
+ (lowInfo > 0 ? `, ${lowInfo} low-information subject(s) dropped` : '') +
5844
5944
  '.');
5845
- return { added, skipped };
5945
+ return { added, skipped, lowInfo };
5846
5946
  }
5847
5947
  function cmdLearn(hippoRoot, flags) {
5848
5948
  requireInit(hippoRoot);