File size: 11,930 Bytes
06671aa
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
import { describe, it, expect } from "vitest";
import { buildWorkflowSummary, buildWorkflowSummaryFromRows, type WorkflowFinding, type WorkflowFindingRow } from "./chatTools";

describe("buildWorkflowSummary", () => {
    it("produces an intro with rewrite + comment counts", () => {
        const findings: WorkflowFinding[] = [
            {
                comment_key: "sec-8-3",
                block_id: "BMK_P_abc",
                op_type: "REWRITE_PARAGRAPH",
                status: "FAIL",
                section_heading: "Section 8.3",
                applied: true,
                comment: "Corrects cross-reference",
                target_text: "as per Section 14",
                new_text: "as per Section 20",
                reasoning: null,
            },
            {
                comment_key: "sec-12",
                block_id: "BMK_P_def",
                op_type: "COMMENT",
                status: "RISK",
                section_heading: "Section 12",
                applied: true,
                comment: "Prevents testing scope creep",
                target_text: "verified in accordance to...",
                new_text: undefined,
                reasoning: null,
            },
        ];
        const result = buildWorkflowSummary(findings);
        expect(result).toContain("redlined 1 issue");
        expect(result).toContain("flagged 1 concern");
        expect(result).toContain("| Section 8.3 | FAIL | Rewrite |");
        expect(result).toContain("| Section 12 | RISK | Comment |");
        expect(result).toContain("as per Section 14");
        expect(result).toContain("as per Section 20");
        expect(result).toContain("Corrects cross-reference");
        expect(result).toContain("Prevents testing scope creep");
    });

    it("shows — for comment amended/original columns", () => {
        const findings: WorkflowFinding[] = [
            {
                comment_key: "c1",
                op_type: "COMMENT",
                status: "REVIEW",
                section_heading: "Section 5",
                applied: true,
                comment: "Note this clause",
                reasoning: null,
            },
        ];
        const result = buildWorkflowSummary(findings);
        expect(result).toContain("| Section 5 | REVIEW | Comment | — | — |");
    });

    it("handles insert and delete op types", () => {
        const findings: WorkflowFinding[] = [
            {
                op_type: "INSERT_PARAGRAPH",
                status: "MISSING",
                section_heading: "Section 9",
                applied: true,
                comment: "Add indemnity clause",
                new_text: "New indemnity text",
                reasoning: null,
            },
            {
                op_type: "DELETE_PARAGRAPH",
                status: "FAIL",
                section_heading: "Section 10",
                applied: true,
                comment: "Remove conflicting clause",
                target_text: "Conflicting text",
                reasoning: null,
            },
        ];
        const result = buildWorkflowSummary(findings);
        expect(result).toContain("inserted 1 clause");
        expect(result).toContain("deleted 1 provision");
        expect(result).toContain("| Insert |");
        expect(result).toContain("| Delete |");
    });

    it("handles no applied findings", () => {
        const findings: WorkflowFinding[] = [];
        const result = buildWorkflowSummary(findings);
        expect(result).toContain("No changes were applied");
    });

    it("escapes pipe characters in content", () => {
        const findings: WorkflowFinding[] = [
            {
                op_type: "REWRITE_PARAGRAPH",
                status: "FAIL",
                section_heading: "Section 1",
                applied: true,
                comment: "A | B | C",
                target_text: "X | Y",
                new_text: "Z | W",
                reasoning: null,
            },
        ];
        const result = buildWorkflowSummary(findings);
        expect(result).toContain("A \\| B \\| C");
        expect(result).toContain("X \\| Y");
        expect(result).toContain("Z \\| W");
    });

    it("never exposes an internal block id when section_heading is missing", () => {
        const findings: WorkflowFinding[] = [
            {
                block_id: "BMK_P_xyz",
                op_type: "COMMENT",
                status: "RISK",
                applied: true,
                comment: "Some concern",
                reasoning: null,
            },
        ];
        const result = buildWorkflowSummary(findings);
        expect(result).toContain("| Relevant section | RISK | Comment |");
        expect(result).not.toContain("BMK_P_xyz");
    });
});

describe("buildWorkflowSummaryFromRows", () => {
    function makeRow(overrides: Partial<WorkflowFindingRow>): WorkflowFindingRow {
        return {
            row_id: overrides.row_id ?? "doc-0:test",
            doc_id: overrides.doc_id ?? "doc-0",
            comment_key: overrides.comment_key ?? "test",
            block_id: overrides.block_id ?? "P_0001",
            finding_status: overrides.finding_status ?? "FAIL",
            apply_status: overrides.apply_status ?? "applied",
            action: overrides.action ?? "REWRITE",
            issue: overrides.issue ?? "Incorrect cross-reference",
            source: overrides.source ?? "plan_comments_on_document",
            ...overrides,
        };
    }

    it("status column shows FAIL, not Flagged", () => {
        const rows = [makeRow({ finding_status: "FAIL", apply_status: "applied" })];
        const result = buildWorkflowSummaryFromRows(rows);
        expect(result.markdown).toContain("| FAIL |");
        expect(result.markdown).not.toContain("| Flagged |");
    });

    it("result column can show Applied", () => {
        const rows = [makeRow({ apply_status: "applied" })];
        const result = buildWorkflowSummaryFromRows(rows);
        expect(result.metrics.applied).toBe(1);
    });

    it("INSERT and INSERT_PARAGRAPH both render as Insert via action field", () => {
        const rows = [makeRow({ action: "INSERT", apply_status: "applied" })];
        const result = buildWorkflowSummaryFromRows(rows);
        expect(result.markdown).toContain("| Insert |");
    });

    it("XML entities in section headings decode", () => {
        const rows = [makeRow({ section_heading: "Section 5 &amp; 6", apply_status: "applied" })];
        const result = buildWorkflowSummaryFromRows(rows);
        expect(result.markdown).toContain("Section 5 & 6");
    });

    it("Issue, Zamil Impact, and Recommended Action parse into separate columns", () => {
        const rows = [makeRow({
            issue: "Incorrect cross-reference",
            zamil_impact: "Payment may point to wrong section",
            recommended_action: "Correct the cross-reference",
            apply_status: "applied",
        })];
        const result = buildWorkflowSummaryFromRows(rows);
        expect(result.markdown).toContain("Incorrect cross-reference");
        expect(result.markdown).toContain("Payment may point to wrong section");
        expect(result.markdown).toContain("Correct the cross-reference");
    });

    it("empty rows render no-action intro", () => {
        const result = buildWorkflowSummaryFromRows([]);
        expect(result.markdown).toContain("No actionable changes were applied");
    });

    it("failed rows render under Not applied", () => {
        const rows = [makeRow({
            apply_status: "failed_validation",
            finding_status: "RISK",
            action: "COMMENT",
            failure_code: "anchor_not_found",
            failure_message: "Block not found",
        })];
        const result = buildWorkflowSummaryFromRows(rows);
        expect(result.markdown).toContain("### Not applied");
        expect(result.markdown).toContain("Block not found");
    });

    it("document_order controls sorting", () => {
        const rows = [
            makeRow({ row_id: "doc-0:b", comment_key: "b", section_heading: "Section 5", document_order: 5, apply_status: "applied" }),
            makeRow({ row_id: "doc-0:a", comment_key: "a", section_heading: "Section 2", document_order: 2, apply_status: "applied" }),
        ];
        const result = buildWorkflowSummaryFromRows(rows);
        const lines = result.markdown.split("\n").filter((l) => l.startsWith("| 1"));
        expect(lines.length).toBe(1);
        expect(lines[0]).toContain("Section 2");
        const lines2 = result.markdown.split("\n").filter((l) => l.startsWith("| 2"));
        expect(lines2.length).toBe(1);
        expect(lines2[0]).toContain("Section 5");
    });

    it("ignored_pass rows do not appear in the applied table", () => {
        const rows = [
            makeRow({ apply_status: "applied", finding_status: "FAIL", comment_key: "real" }),
            makeRow({ apply_status: "ignored_pass", finding_status: "REVIEW", comment_key: "pass1" }),
        ];
        const result = buildWorkflowSummaryFromRows(rows);
        expect(result.markdown).not.toContain("pass1");
        expect(result.metrics.ignoredPass).toBe(1);
        expect(result.metrics.applied).toBe(1);
    });

    it("if a report omits section_heading, internal IDs never appear in the summary", () => {
        const rows = [makeRow({
            section_heading: null,
            apply_status: "applied",
            finding_status: "FAIL",
            block_id: "P_0042",
        })];
        const result = buildWorkflowSummaryFromRows(rows);
        expect(result.markdown).toContain("Relevant section");
        expect(result.markdown).not.toContain("P_0042");
    });

    it("uses reasoning clause_section when section_heading is missing", () => {
        const rows = [makeRow({
            section_heading: null,
            block_id: "BMK_P_9ll3g",
            reasoning: {
                clause_section: "Section 1: Building Description",
                violated_standard: "Delivery protection",
                zamil_exposure: "Delay exposure",
            },
        })];
        const result = buildWorkflowSummaryFromRows(rows);
        expect(result.markdown).toContain("Section 1: Building Description");
        expect(result.markdown).not.toContain("BMK_P_9ll3g");
    });

    it("strips internal IDs from every user-facing summary field", () => {
        const rows = [makeRow({
            section_heading: "Section 1 BMK_P_9ll3g",
            block_id: "BMK_P_9ll3g",
            issue: "Missing protection near BMK_P_9ll3g",
            zamil_impact: "See P_0042 for exposure",
            recommended_action: "Insert after BMK_P_9ll3g",
            original_wording: "Text at P_0042",
        })];
        const result = buildWorkflowSummaryFromRows(rows);
        expect(result.markdown).toContain("Section 1");
        expect(result.markdown).not.toMatch(/BMK_[PT]_/);
        expect(result.markdown).not.toMatch(/\bP_\d{4,}\b/);
    });

    it("metrics include correct counts", () => {
        const rows = [
            makeRow({ apply_status: "applied", action: "REWRITE", finding_status: "FAIL" }),
            makeRow({ apply_status: "applied", action: "COMMENT", finding_status: "RISK" }),
            makeRow({ apply_status: "failed_validation", action: "INSERT", finding_status: "MISSING" }),
            makeRow({ apply_status: "ignored_pass", action: "COMMENT", finding_status: "REVIEW" }),
        ];
        const result = buildWorkflowSummaryFromRows(rows);
        expect(result.metrics.total).toBe(4);
        expect(result.metrics.applied).toBe(2);
        expect(result.metrics.failed).toBe(1);
        expect(result.metrics.ignoredPass).toBe(1);
        expect(result.metrics.actionable).toBe(3);
        expect(result.metrics.rewrites).toBe(1);
        expect(result.metrics.comments).toBe(1);
    });
});