@dzhechkov/harness-core 0.3.128 → 0.3.130

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.
@@ -50,6 +50,7 @@ import {
50
50
  loadStoreRecords,
51
51
  readMemoryLearningConfig,
52
52
  readReinforcementState,
53
+ readQuarantineState,
53
54
  removePatternsByIds,
54
55
  snapshotStore,
55
56
  updateReinforcementState,
@@ -155,6 +156,8 @@ export interface HybridHit {
155
156
  readonly backend: RecallHit['backend'];
156
157
  /** Reciprocal-rank-fusion score (ranking only — NOT the pattern's reward). */
157
158
  readonly score: number;
159
+ /** lesson-quarantine: set only for a quarantined hit — display marks ⚠q, ranking was damped. */
160
+ readonly quarantined?: boolean;
158
161
  }
159
162
 
160
163
  /** Outcome of {@link recallHybrid}. With no engine this is content-identical to `recallPatterns`. */
@@ -336,7 +339,7 @@ export function isVectorNoise(text: string): boolean {
336
339
  * ACL: taught {@link PatternRecord} → {@link VectorEntry}. Returns `undefined` for noise (the
337
340
  * ingest gate — I-6). Score is the record's REAL reward, never a fabricated 1.0.
338
341
  */
339
- export function patternVectorEntry(p: PatternRecord, source = 'dz-teach'): VectorEntry | undefined {
342
+ export function patternVectorEntry(p: PatternRecord, source = 'dz-teach', opts: { quarantined?: boolean } = {}): VectorEntry | undefined {
340
343
  if (isVectorNoise(p.pattern)) return undefined;
341
344
  const dzId = patternRecordId(p);
342
345
  return {
@@ -345,7 +348,9 @@ export function patternVectorEntry(p: PatternRecord, source = 'dz-teach'): Vecto
345
348
  score: p.reward,
346
349
  taskType: 'dz-teach',
347
350
  tags: ['dz-teach', p.type],
348
- metadata: { dzId, source, ts: p.ts, domain: p.domain },
351
+ // FR-8: quarantine rides into the mirror so the HOOK DAEMON (which reads only the mirror's
352
+ // sqlite metadata) can exclude unproven lessons from auto-injection.
353
+ metadata: { dzId, source, ts: p.ts, domain: p.domain, ...(opts.quarantined === true ? { qStatus: 'quarantined' } : {}) },
349
354
  };
350
355
  }
351
356
 
@@ -790,7 +795,20 @@ export async function recallHybrid(
790
795
  const learning = resolveLearningBackend(projectRoot);
791
796
  // Phase 3: opt-in SAFLA-delta re-rank. The map is built ONCE per recall (off ⇒ undefined ⇒ the
792
797
  // reinforce-only path, byte-identical to today).
793
- const deltaMap = readMemoryLearningConfig(projectRoot).deltaRerank ? lessonDeltaMap(projectRoot) : undefined;
798
+ const memCfg = readMemoryLearningConfig(projectRoot);
799
+ const deltaMap = memCfg.deltaRerank ? lessonDeltaMap(projectRoot) : undefined;
800
+ // lesson-quarantine: damp read from the AUTHORITATIVE store records (idToRecord), never from
801
+ // mirror metadata — the mirror may lag a promotion; the store cannot.
802
+ const dampQuarantined = (hits: readonly HybridHit[]): HybridHit[] => {
803
+ if (!memCfg.quarantine) return [...hits];
804
+ return hits
805
+ .map((h) => {
806
+ const rec = idToRecord.get(idOf(h.pattern));
807
+ const q = rec !== undefined && readQuarantineState(rec).quarantined;
808
+ return q ? { ...h, score: h.score * memCfg.quarantineDamp, quarantined: true as const } : h;
809
+ })
810
+ .sort((a, b) => b.score - a.score);
811
+ };
794
812
  const enhance = (hits: readonly HybridHit[]): HybridHit[] => {
795
813
  const candidates = hits.map((h) => {
796
814
  const dzId = idOf(h.pattern);
@@ -799,9 +817,9 @@ export async function recallHybrid(
799
817
  });
800
818
  if (deltaMap !== undefined) {
801
819
  const deltaByIndex = candidates.map((c) => deltaMap.get(c.dzId) ?? 0);
802
- return applyLearningSignalsWithDelta(hits, learning, candidates, REINFORCE_RRF_CAP, deltaByIndex, REINFORCE_RRF_CAP);
820
+ return dampQuarantined(applyLearningSignalsWithDelta(hits, learning, candidates, REINFORCE_RRF_CAP, deltaByIndex, REINFORCE_RRF_CAP));
803
821
  }
804
- return applyLearningSignals(hits, learning, candidates, REINFORCE_RRF_CAP);
822
+ return dampQuarantined(applyLearningSignals(hits, learning, candidates, REINFORCE_RRF_CAP));
805
823
  };
806
824
  const lexicalOnly = (extra: Partial<Pick<HybridRecall, 'vectorEngine' | 'vectorReason' | 'vectorError'>>): HybridRecall => ({
807
825
  hits: enhance(lexical.map((h, rank) => ({ pattern: h.pattern, backend: h.backend, score: 1 / (RRF_K + rank + 1) }))),