@adeu/core 1.17.0 → 1.17.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.
@@ -25,6 +25,47 @@ describe('Search and Targeted Write Engine', () => {
25
25
  expect(() => engine.process_batch(edits)).toThrowError(BatchValidationError);
26
26
  });
27
27
 
28
+ it('strict rejection carries match_mode guidance, and the suggested re-call applies (Turn Loop Trap)', async () => {
29
+ // The DPA scenario: one placeholder repeated across clauses.
30
+ const doc = await createTestDocument();
31
+ addParagraph(doc, "PROVIDER: [official company name] shall process the data.");
32
+ addParagraph(doc, "PROVIDER: [official company name] is the data processor.");
33
+
34
+ const engine = new RedlineEngine(doc);
35
+ const strictEdit: any = {
36
+ type: 'modify',
37
+ target_text: "PROVIDER: [official company name]",
38
+ new_text: "PROVIDER: Acme Corp",
39
+ };
40
+
41
+ // 1. The strict edit is rejected — and the error MUST name the escape hatch
42
+ // so the agent re-calls instead of looping on context/regex refinement.
43
+ let caught: BatchValidationError | null = null;
44
+ try {
45
+ engine.process_batch([strictEdit]);
46
+ } catch (e) {
47
+ caught = e as BatchValidationError;
48
+ }
49
+ expect(caught).toBeInstanceOf(BatchValidationError);
50
+ const msg = caught!.errors.join("\n");
51
+ expect(msg).toContain("Ambiguous match");
52
+ expect(msg).toContain('"match_mode": "all"');
53
+ expect(msg).toContain('"match_mode": "first"');
54
+
55
+ // 2. Follow the guidance verbatim — the same target_text with match_mode="all"
56
+ // applies cleanly and mutates BOTH clauses on the saved document.
57
+ const doc2 = await createTestDocument();
58
+ addParagraph(doc2, "PROVIDER: [official company name] shall process the data.");
59
+ addParagraph(doc2, "PROVIDER: [official company name] is the data processor.");
60
+ const engine2 = new RedlineEngine(doc2);
61
+ const stats = engine2.process_batch([{ ...strictEdit, match_mode: 'all' }]);
62
+ expect(stats.edits[0].occurrences_modified).toBe(2);
63
+
64
+ const text = await extractTextFromBuffer(await doc2.save(), true);
65
+ expect(text.match(/Acme Corp/g)?.length).toBe(2);
66
+ expect(text).not.toContain("PROVIDER: [official company name]");
67
+ });
68
+
28
69
  it('match_mode="first" modifies only the first occurrence', async () => {
29
70
  const doc = await createTestDocument();
30
71
  addParagraph(doc, "This is a repetitive clause.");