@dzhechkov/harness-core 0.7.11 → 0.8.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.
Files changed (137) hide show
  1. package/.dz-manifest.json +287 -87
  2. package/README.md +60 -1
  3. package/dist/amendment-trace.d.ts +12 -1
  4. package/dist/amendment-trace.d.ts.map +1 -1
  5. package/dist/amendment-trace.js +22 -4
  6. package/dist/amendment-trace.js.map +1 -1
  7. package/dist/book-kb.d.ts.map +1 -1
  8. package/dist/book-kb.js +17 -0
  9. package/dist/book-kb.js.map +1 -1
  10. package/dist/feature-adr-checkpoints.d.ts +61 -0
  11. package/dist/feature-adr-checkpoints.d.ts.map +1 -1
  12. package/dist/feature-adr-checkpoints.js +116 -2
  13. package/dist/feature-adr-checkpoints.js.map +1 -1
  14. package/dist/feature-adr-routing.d.ts +69 -12
  15. package/dist/feature-adr-routing.d.ts.map +1 -1
  16. package/dist/feature-adr-routing.js +117 -7
  17. package/dist/feature-adr-routing.js.map +1 -1
  18. package/dist/index.d.ts +23 -6
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +23 -4
  21. package/dist/index.js.map +1 -1
  22. package/dist/learning-backend.d.ts +39 -0
  23. package/dist/learning-backend.d.ts.map +1 -1
  24. package/dist/learning-backend.js +31 -11
  25. package/dist/learning-backend.js.map +1 -1
  26. package/dist/lesson-bandit.d.ts +116 -0
  27. package/dist/lesson-bandit.d.ts.map +1 -0
  28. package/dist/lesson-bandit.js +235 -0
  29. package/dist/lesson-bandit.js.map +1 -0
  30. package/dist/lesson-payoff.d.ts +260 -0
  31. package/dist/lesson-payoff.d.ts.map +1 -0
  32. package/dist/lesson-payoff.js +597 -0
  33. package/dist/lesson-payoff.js.map +1 -0
  34. package/dist/loop-blobs.generated.js +2 -2
  35. package/dist/loop-blobs.generated.js.map +1 -1
  36. package/dist/mutation-gate.d.ts +31 -8
  37. package/dist/mutation-gate.d.ts.map +1 -1
  38. package/dist/mutation-gate.js +57 -16
  39. package/dist/mutation-gate.js.map +1 -1
  40. package/dist/named-lock.d.ts.map +1 -1
  41. package/dist/named-lock.js +11 -9
  42. package/dist/named-lock.js.map +1 -1
  43. package/dist/native-dep-probe.d.ts +11 -0
  44. package/dist/native-dep-probe.d.ts.map +1 -0
  45. package/dist/native-dep-probe.js +100 -0
  46. package/dist/native-dep-probe.js.map +1 -0
  47. package/dist/operations.d.ts.map +1 -1
  48. package/dist/operations.js +82 -1
  49. package/dist/operations.js.map +1 -1
  50. package/dist/parser-safe-region.d.ts +47 -0
  51. package/dist/parser-safe-region.d.ts.map +1 -0
  52. package/dist/parser-safe-region.js +47 -0
  53. package/dist/parser-safe-region.js.map +1 -0
  54. package/dist/patterns.d.ts +21 -0
  55. package/dist/patterns.d.ts.map +1 -1
  56. package/dist/patterns.js +52 -4
  57. package/dist/patterns.js.map +1 -1
  58. package/dist/profile.d.ts +202 -0
  59. package/dist/profile.d.ts.map +1 -0
  60. package/dist/profile.js +536 -0
  61. package/dist/profile.js.map +1 -0
  62. package/dist/project-skills-root.d.ts +44 -0
  63. package/dist/project-skills-root.d.ts.map +1 -0
  64. package/dist/project-skills-root.js +62 -0
  65. package/dist/project-skills-root.js.map +1 -0
  66. package/dist/publish.d.ts +5 -3
  67. package/dist/publish.d.ts.map +1 -1
  68. package/dist/publish.js +46 -8
  69. package/dist/publish.js.map +1 -1
  70. package/dist/qe-rounds.d.ts +75 -0
  71. package/dist/qe-rounds.d.ts.map +1 -0
  72. package/dist/qe-rounds.js +174 -0
  73. package/dist/qe-rounds.js.map +1 -0
  74. package/dist/registry.d.ts.map +1 -1
  75. package/dist/registry.js +5 -1
  76. package/dist/registry.js.map +1 -1
  77. package/dist/run-records.d.ts.map +1 -1
  78. package/dist/run-records.js +8 -1
  79. package/dist/run-records.js.map +1 -1
  80. package/dist/setup.d.ts +35 -4
  81. package/dist/setup.d.ts.map +1 -1
  82. package/dist/setup.js +86 -23
  83. package/dist/setup.js.map +1 -1
  84. package/dist/sign.d.ts +29 -24
  85. package/dist/sign.d.ts.map +1 -1
  86. package/dist/sign.js +183 -61
  87. package/dist/sign.js.map +1 -1
  88. package/dist/store-location.d.ts +56 -0
  89. package/dist/store-location.d.ts.map +1 -0
  90. package/dist/store-location.js +56 -0
  91. package/dist/store-location.js.map +1 -0
  92. package/dist/store-lock.d.ts +1 -0
  93. package/dist/store-lock.d.ts.map +1 -1
  94. package/dist/store-lock.js +7 -1
  95. package/dist/store-lock.js.map +1 -1
  96. package/dist/store-merge.d.ts +55 -0
  97. package/dist/store-merge.d.ts.map +1 -0
  98. package/dist/store-merge.js +77 -0
  99. package/dist/store-merge.js.map +1 -0
  100. package/dist/teach-target.d.ts +61 -0
  101. package/dist/teach-target.d.ts.map +1 -0
  102. package/dist/teach-target.js +105 -0
  103. package/dist/teach-target.js.map +1 -0
  104. package/dist/vector-tier.d.ts +30 -0
  105. package/dist/vector-tier.d.ts.map +1 -1
  106. package/dist/vector-tier.js +178 -20
  107. package/dist/vector-tier.js.map +1 -1
  108. package/package.json +13 -13
  109. package/sbom.json +591 -87
  110. package/src/amendment-trace.ts +34 -4
  111. package/src/book-kb.ts +17 -0
  112. package/src/feature-adr-checkpoints.ts +111 -2
  113. package/src/feature-adr-routing.ts +146 -7
  114. package/src/index.ts +55 -3
  115. package/src/learning-backend.ts +62 -11
  116. package/src/lesson-bandit.ts +279 -0
  117. package/src/lesson-payoff.ts +728 -0
  118. package/src/loop-blobs.generated.ts +2 -2
  119. package/src/mutation-gate.ts +74 -17
  120. package/src/named-lock.ts +10 -8
  121. package/src/native-dep-probe.ts +118 -0
  122. package/src/operations.ts +77 -1
  123. package/src/parser-safe-region.ts +65 -0
  124. package/src/patterns.ts +76 -6
  125. package/src/profile.ts +593 -0
  126. package/src/project-skills-root.ts +63 -0
  127. package/src/publish.ts +43 -7
  128. package/src/qe-rounds.ts +226 -0
  129. package/src/registry.ts +5 -1
  130. package/src/run-records.ts +9 -1
  131. package/src/setup.ts +87 -23
  132. package/src/sign.ts +180 -60
  133. package/src/store-location.ts +76 -0
  134. package/src/store-lock.ts +8 -1
  135. package/src/store-merge.ts +91 -0
  136. package/src/teach-target.ts +126 -0
  137. package/src/vector-tier.ts +236 -21
package/src/patterns.ts CHANGED
@@ -31,6 +31,7 @@ import { resolveAgentdbPath } from './agentdb-index.js';
31
31
  import type { VectorEntry } from './vector-tier.js';
32
32
  import { rankLessonsByDelta, type LessonHistory } from './safla-delta.js';
33
33
  import { withStoreLock, withStoreLockSync, StoreLockTimeoutError, StoreLockCompromisedError } from './store-lock.js';
34
+ import { describeNativeDep, exerciseSqliteOpen, probeNativeDep } from './native-dep-probe.js';
34
35
 
35
36
  /** A learned pattern as written by `dz teach` to `.dz/patterns.jsonl`. */
36
37
  export interface PatternRecord {
@@ -106,6 +107,24 @@ export interface MemoryLearningConfig {
106
107
  readonly quarantineDamp: number;
107
108
  /** Days after which an unreinforced quarantined lesson is an EXPIRY CANDIDATE (informational). */
108
109
  readonly quarantineExpireDays: number;
110
+ /**
111
+ * Bandit payoff re-rank (feature lesson-bandit-rerank, ADR-001). When true, recall adds a BOUNDED
112
+ * term derived from each lesson's measured payoff posterior — "has this lesson ever actually
113
+ * helped", the axis similarity cannot answer. ADDITIVE alongside {@link deltaRerank}, never a
114
+ * replacement, and capped at the same `REINFORCE_RRF_CAP`: similarity still decides WHICH lessons
115
+ * are candidates; the bandit only reorders WITHIN them. Default FALSE: absent config is
116
+ * byte-identical to today — no file read, no file written, no term applied.
117
+ */
118
+ readonly banditRerank: boolean;
119
+ /**
120
+ * Bandit EXPLORATION (feature lesson-bandit-rerank, ADR-003). When true, an arm with no evidence
121
+ * may receive a bounded trial lift so it can accumulate some. This deliberately weakens the
122
+ * view-does-not-promote posture, so it SHIPS DISARMED and is a separate flag: enabling payoff
123
+ * re-ranking must never silently mean "start surfacing unproven lessons". Quarantined lessons are
124
+ * NEVER explored in any configuration. Default FALSE; `true` with `banditRerank: false` is a
125
+ * warned no-op.
126
+ */
127
+ readonly banditExploration: boolean;
109
128
  }
110
129
 
111
130
  /**
@@ -141,10 +160,10 @@ export function readLearningConfig(projectRoot: string): LearningConfig {
141
160
  }
142
161
 
143
162
  export function readMemoryLearningConfig(projectRoot: string): MemoryLearningConfig {
144
- const fallback: MemoryLearningConfig = { backend: 'native', onRecallHits: true, usesSat: 64, halfLifeDays: 30, reinforceThreshold: 0.95, deltaRerank: false, quarantine: false, quarantineDamp: 0.5, quarantineExpireDays: 30 };
163
+ const fallback: MemoryLearningConfig = { backend: 'native', onRecallHits: true, usesSat: 64, halfLifeDays: 30, reinforceThreshold: 0.95, deltaRerank: false, quarantine: false, quarantineDamp: 0.5, quarantineExpireDays: 30, banditRerank: false, banditExploration: false };
145
164
  try {
146
165
  const parsed = JSON.parse(readFileSync(join(projectRoot, '.dz', 'config.json'), 'utf-8')) as {
147
- memory?: { learning?: { backend?: string; onRecallHits?: boolean; usesSat?: number; halfLifeDays?: number; reinforceThreshold?: number; deltaRerank?: boolean; quarantine?: boolean; quarantineDamp?: number; quarantineExpireDays?: number } };
166
+ memory?: { learning?: { backend?: string; onRecallHits?: boolean; usesSat?: number; halfLifeDays?: number; reinforceThreshold?: number; deltaRerank?: boolean; quarantine?: boolean; quarantineDamp?: number; quarantineExpireDays?: number; banditRerank?: boolean; banditExploration?: boolean } };
148
167
  };
149
168
  const learning = parsed.memory?.learning ?? {};
150
169
  const backend = learning.backend === 'off' || learning.backend === 'ruvector-gnn' || learning.backend === 'native' ? learning.backend : 'native';
@@ -168,6 +187,11 @@ export function readMemoryLearningConfig(projectRoot: string): MemoryLearningCon
168
187
  typeof learning.quarantineExpireDays === 'number' && Number.isFinite(learning.quarantineExpireDays) && learning.quarantineExpireDays > 0
169
188
  ? Math.floor(learning.quarantineExpireDays)
170
189
  : fallback.quarantineExpireDays,
190
+ // Same `=== true` discipline as deltaRerank/quarantine: absent, null, "true" (a string), 1, or
191
+ // any legacy value ⇒ false. That is what makes "flag absent ⇒ byte-identical" true BY
192
+ // CONSTRUCTION rather than by care (ADR-001 R1).
193
+ banditRerank: learning.banditRerank === true,
194
+ banditExploration: learning.banditExploration === true,
171
195
  };
172
196
  } catch {
173
197
  return fallback;
@@ -574,16 +598,54 @@ export interface ReinforcePatternResult {
574
598
  readonly dzId?: string;
575
599
  readonly uses?: number;
576
600
  readonly error?: string;
601
+ /** The reward value actually observed for this reinforcement, clamped to [0,1]. */
602
+ readonly reward?: number;
577
603
  }
578
604
 
579
- export async function reinforcePattern(projectRoot: string, dzIdOrText: string, opts: { reward?: number; ts?: string; mergedFrom?: readonly string[]; exposure?: boolean } = {}): Promise<ReinforcePatternResult> {
605
+ export async function reinforcePattern(projectRoot: string, dzIdOrText: string, opts: { reward?: number; ts?: string; mergedFrom?: readonly string[]; exposure?: boolean; domain?: string } = {}): Promise<ReinforcePatternResult> {
580
606
  // The WHOLE read-modify-write holds the store lock (finding 5): two concurrent
581
607
  // reinforces would otherwise both read uses=N and both write back N+1.
608
+ let result: ReinforcePatternResult;
582
609
  try {
583
- return await withStoreLock(projectRoot, async () => reinforcePatternLocked(projectRoot, dzIdOrText, opts));
610
+ result = await withStoreLock(projectRoot, async () => reinforcePatternLocked(projectRoot, dzIdOrText, opts));
584
611
  } catch (err) {
585
612
  return { ok: false, error: lockErrorMessage(err) };
586
613
  }
614
+ // ── Bandit reward emission (feature lesson-bandit-rerank, ADR-001 D-5 / architecture §4) ──
615
+ //
616
+ // WHERE: here, in `reinforcePattern`, and NOT inside `reinforcePatternLocked` — that body runs
617
+ // under `withStoreLock`, and the bandit takes a DIFFERENT named lock. Lock nesting is AVOIDED,
618
+ // not managed: the bandit call is made after the store lock is released, from the result the
619
+ // locked section already returns. A crash between the two leaves the bandit one reward behind —
620
+ // a tolerable, self-correcting inconsistency for a ranking hint, and explicitly NOT tolerable for
621
+ // the store itself, which is why the store write is the one inside the lock.
622
+ //
623
+ // WHEN: only on the non-exposure branch — the SAME predicate that decides quarantine promotion
624
+ // eleven lines below. Hanging both on one predicate means the two can never disagree; the
625
+ // exposure≠reward distinction has ONE implementation, not two that must be kept in sync (INV-2).
626
+ //
627
+ // The store write is authoritative; the bandit is a derived index. Its failure is counted inside
628
+ // `recordReward` and can never fail a reinforce.
629
+ if (result.ok && result.dzId !== undefined && opts.exposure !== true) {
630
+ try {
631
+ if (readMemoryLearningConfig(projectRoot).banditRerank) {
632
+ // Dynamic import so the module is not even LOADED on the disarmed path (INV-1), and so
633
+ // patterns.ts keeps no static edge to a module that imports it.
634
+ const payoff = await import('./lesson-payoff.js');
635
+ const ev = payoff.makeRewardEvent(
636
+ result.dzId,
637
+ // HONEST LIMIT: a confirmation carries no memory of WHICH recall surfaced the lesson, so
638
+ // without an explicit `domain` the reward lands in the `general` bucket — the same bucket
639
+ // an undomained recall reads. A caller that knows the recall context should name it.
640
+ payoff.contextKeyFor(opts.domain),
641
+ result.reward ?? 1,
642
+ opts.ts ?? new Date().toISOString(),
643
+ );
644
+ if (ev !== null) payoff.recordReward(projectRoot, ev);
645
+ }
646
+ } catch { /* counted inside the payoff module; a derived index never fails a reinforce */ }
647
+ }
648
+ return result;
587
649
  }
588
650
 
589
651
  async function reinforcePatternLocked(projectRoot: string, dzIdOrText: string, opts: { reward?: number; ts?: string; mergedFrom?: readonly string[]; exposure?: boolean }): Promise<ReinforcePatternResult> {
@@ -619,7 +681,7 @@ async function reinforcePatternLocked(projectRoot: string, dzIdOrText: string, o
619
681
  try {
620
682
  appendFileSync(join(projectRoot, '.dz', 'sessions.jsonl'), JSON.stringify({ event: 'reinforce', ts, dzId: rec.id, uses: nextState.uses }) + '\n');
621
683
  } catch { /* best-effort */ }
622
- return { ok: true, dzId: rec.id, uses: nextState.uses };
684
+ return { ok: true, dzId: rec.id, uses: nextState.uses, reward: observed };
623
685
  }
624
686
 
625
687
  export async function updateReinforcementState(projectRoot: string, dzId: string, state: ReinforcementState): Promise<ReinforcePatternResult> {
@@ -1211,7 +1273,11 @@ function countNoiseVectors(projectRoot: string): { removed: number; error?: stri
1211
1273
  db.close();
1212
1274
  }
1213
1275
  } catch (err) {
1214
- return { removed: 0, error: `vector mirror: ${err instanceof Error ? err.message : String(err)}` };
1276
+ const verdict = probeNativeDep(projectRoot, 'better-sqlite3', exerciseSqliteOpen, 'sqlite-open');
1277
+ const message = verdict.state === 'unusable'
1278
+ ? describeNativeDep(verdict)
1279
+ : err instanceof Error ? err.message : String(err);
1280
+ return { removed: 0, error: `vector mirror: ${message}` };
1215
1281
  }
1216
1282
  }
1217
1283
 
@@ -1253,6 +1319,10 @@ function pruneNoiseVectors(projectRoot: string): { removed: number; error?: stri
1253
1319
  db.close();
1254
1320
  }
1255
1321
  } catch (err) {
1322
+ const verdict = probeNativeDep(projectRoot, 'better-sqlite3', exerciseSqliteOpen, 'sqlite-open');
1323
+ if (verdict.state === 'unusable') {
1324
+ return { removed: 0, error: `vector mirror: ${describeNativeDep(verdict)}` };
1325
+ }
1256
1326
  return { removed: 0, error: `vector prune failed: ${err instanceof Error ? err.message : String(err)}` };
1257
1327
  }
1258
1328
  }