canon 0.1.3 → 0.1.5

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 (102) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +9 -1
  3. data/.rubocop_todo.yml +276 -7
  4. data/README.adoc +203 -138
  5. data/_config.yml +116 -0
  6. data/docs/ADVANCED_TOPICS.adoc +20 -0
  7. data/docs/BASIC_USAGE.adoc +16 -0
  8. data/docs/CHARACTER_VISUALIZATION.adoc +567 -0
  9. data/docs/CLI.adoc +493 -0
  10. data/docs/CUSTOMIZING_BEHAVIOR.adoc +19 -0
  11. data/docs/DIFF_ARCHITECTURE.adoc +435 -0
  12. data/docs/DIFF_FORMATTING.adoc +540 -0
  13. data/docs/FORMATS.adoc +447 -0
  14. data/docs/INDEX.adoc +222 -0
  15. data/docs/INPUT_VALIDATION.adoc +477 -0
  16. data/docs/MATCH_ARCHITECTURE.adoc +463 -0
  17. data/docs/MATCH_OPTIONS.adoc +719 -0
  18. data/docs/MODES.adoc +432 -0
  19. data/docs/NORMATIVE_INFORMATIVE_DIFFS.adoc +219 -0
  20. data/docs/OPTIONS.adoc +1387 -0
  21. data/docs/PREPROCESSING.adoc +491 -0
  22. data/docs/RSPEC.adoc +605 -0
  23. data/docs/RUBY_API.adoc +478 -0
  24. data/docs/SEMANTIC_DIFF_REPORT.adoc +528 -0
  25. data/docs/UNDERSTANDING_CANON.adoc +17 -0
  26. data/docs/VERBOSE.adoc +482 -0
  27. data/exe/canon +7 -0
  28. data/lib/canon/cli.rb +179 -0
  29. data/lib/canon/commands/diff_command.rb +195 -0
  30. data/lib/canon/commands/format_command.rb +113 -0
  31. data/lib/canon/comparison/base_comparator.rb +39 -0
  32. data/lib/canon/comparison/comparison_result.rb +79 -0
  33. data/lib/canon/comparison/html_comparator.rb +410 -0
  34. data/lib/canon/comparison/json_comparator.rb +212 -0
  35. data/lib/canon/comparison/match_options.rb +616 -0
  36. data/lib/canon/comparison/xml_comparator.rb +566 -0
  37. data/lib/canon/comparison/yaml_comparator.rb +93 -0
  38. data/lib/canon/comparison.rb +239 -0
  39. data/lib/canon/config.rb +172 -0
  40. data/lib/canon/diff/diff_block.rb +71 -0
  41. data/lib/canon/diff/diff_block_builder.rb +105 -0
  42. data/lib/canon/diff/diff_classifier.rb +46 -0
  43. data/lib/canon/diff/diff_context.rb +85 -0
  44. data/lib/canon/diff/diff_context_builder.rb +107 -0
  45. data/lib/canon/diff/diff_line.rb +77 -0
  46. data/lib/canon/diff/diff_node.rb +56 -0
  47. data/lib/canon/diff/diff_node_mapper.rb +148 -0
  48. data/lib/canon/diff/diff_report.rb +133 -0
  49. data/lib/canon/diff/diff_report_builder.rb +62 -0
  50. data/lib/canon/diff_formatter/by_line/base_formatter.rb +407 -0
  51. data/lib/canon/diff_formatter/by_line/html_formatter.rb +672 -0
  52. data/lib/canon/diff_formatter/by_line/json_formatter.rb +284 -0
  53. data/lib/canon/diff_formatter/by_line/simple_formatter.rb +190 -0
  54. data/lib/canon/diff_formatter/by_line/xml_formatter.rb +860 -0
  55. data/lib/canon/diff_formatter/by_line/yaml_formatter.rb +292 -0
  56. data/lib/canon/diff_formatter/by_object/base_formatter.rb +199 -0
  57. data/lib/canon/diff_formatter/by_object/json_formatter.rb +305 -0
  58. data/lib/canon/diff_formatter/by_object/xml_formatter.rb +248 -0
  59. data/lib/canon/diff_formatter/by_object/yaml_formatter.rb +17 -0
  60. data/lib/canon/diff_formatter/character_map.yml +197 -0
  61. data/lib/canon/diff_formatter/debug_output.rb +431 -0
  62. data/lib/canon/diff_formatter/diff_detail_formatter.rb +551 -0
  63. data/lib/canon/diff_formatter/legend.rb +141 -0
  64. data/lib/canon/diff_formatter.rb +520 -0
  65. data/lib/canon/errors.rb +56 -0
  66. data/lib/canon/formatters/html4_formatter.rb +17 -0
  67. data/lib/canon/formatters/html5_formatter.rb +17 -0
  68. data/lib/canon/formatters/html_formatter.rb +37 -0
  69. data/lib/canon/formatters/html_formatter_base.rb +163 -0
  70. data/lib/canon/formatters/json_formatter.rb +3 -0
  71. data/lib/canon/formatters/xml_formatter.rb +20 -55
  72. data/lib/canon/formatters/yaml_formatter.rb +4 -1
  73. data/lib/canon/pretty_printer/html.rb +57 -0
  74. data/lib/canon/pretty_printer/json.rb +25 -0
  75. data/lib/canon/pretty_printer/xml.rb +29 -0
  76. data/lib/canon/rspec_matchers.rb +222 -80
  77. data/lib/canon/validators/base_validator.rb +49 -0
  78. data/lib/canon/validators/html_validator.rb +138 -0
  79. data/lib/canon/validators/json_validator.rb +89 -0
  80. data/lib/canon/validators/xml_validator.rb +53 -0
  81. data/lib/canon/validators/yaml_validator.rb +73 -0
  82. data/lib/canon/version.rb +1 -1
  83. data/lib/canon/xml/attribute_handler.rb +80 -0
  84. data/lib/canon/xml/c14n.rb +36 -0
  85. data/lib/canon/xml/character_encoder.rb +38 -0
  86. data/lib/canon/xml/data_model.rb +225 -0
  87. data/lib/canon/xml/element_matcher.rb +196 -0
  88. data/lib/canon/xml/line_range_mapper.rb +158 -0
  89. data/lib/canon/xml/namespace_handler.rb +86 -0
  90. data/lib/canon/xml/node.rb +32 -0
  91. data/lib/canon/xml/nodes/attribute_node.rb +54 -0
  92. data/lib/canon/xml/nodes/comment_node.rb +23 -0
  93. data/lib/canon/xml/nodes/element_node.rb +56 -0
  94. data/lib/canon/xml/nodes/namespace_node.rb +38 -0
  95. data/lib/canon/xml/nodes/processing_instruction_node.rb +24 -0
  96. data/lib/canon/xml/nodes/root_node.rb +16 -0
  97. data/lib/canon/xml/nodes/text_node.rb +23 -0
  98. data/lib/canon/xml/processor.rb +151 -0
  99. data/lib/canon/xml/whitespace_normalizer.rb +72 -0
  100. data/lib/canon/xml/xml_base_handler.rb +188 -0
  101. data/lib/canon.rb +14 -3
  102. metadata +116 -21
@@ -0,0 +1,528 @@
1
+ ---
2
+ layout: default
3
+ title: Semantic Diff Report
4
+ nav_order: 41
5
+ parent: Advanced Topics
6
+ ---
7
+ = Canon semantic diff report
8
+ :toc:
9
+ :toclevels: 3
10
+
11
+ == General
12
+
13
+ The Semantic Diff Report provides dimension-specific, actionable details for
14
+ each difference found during comparison. Unlike the detailed line-by-line or
15
+ object tree diffs, which show every changed line, the Semantic Diff Report
16
+ focuses on WHAT changed and WHY it matters.
17
+
18
+ The report is always shown in verbose mode when differences exist, providing
19
+ a high-level summary before the detailed diff output.
20
+
21
+ Key features:
22
+
23
+ * XPath locations for XML/HTML elements
24
+ * JSON path locations for JSON/YAML data
25
+ * Dimension-specific formatting optimized for each type of difference
26
+ * Colorized output for easy visual scanning
27
+ * Whitespace preservation detection for `<pre>`, `<code>`, etc.
28
+ * Actionable change summaries (e.g., "Added: +xmlns:v, +xmlns:o")
29
+
30
+ == Architecture
31
+
32
+ The Semantic Diff Report is generated by the `DiffDetailFormatter` module
33
+ and integrated into the main diff output flow:
34
+
35
+ [source]
36
+ ----
37
+ ╔═════════════════════════════════════════════════════════════════════╗
38
+ ║ SEMANTIC DIFF REPORT ARCHITECTURE ║
39
+ ╚═════════════════════════════════════════════════════════════════════╝
40
+
41
+ Input: ComparisonResult with differences array
42
+
43
+ ┌─────────────────────────────────────────────────────────────────────┐
44
+ │ DiffDetailFormatter.format_report(differences) │
45
+ │ │
46
+ │ For each difference: │
47
+ │ 1. Detect type (DiffNode for XML/HTML, Hash for JSON/YAML) │
48
+ │ 2. Extract location (XPath or JSON path) │
49
+ │ 3. Dispatch to dimension-specific formatter │
50
+ │ 4. Format as vertical section with colors │
51
+ └─────────────────────────────────────────────────────────────────────┘
52
+
53
+
54
+ ┌─────────────────────────────────────────────────────────────────────┐
55
+ │ Dimension-Specific Formatters │
56
+ │ │
57
+ │ XML/HTML: JSON/YAML: │
58
+ │ • attribute_presence • hash_diff │
59
+ │ • attribute_values (unified handling) │
60
+ │ • text_content │
61
+ │ • structural_whitespace │
62
+ │ • comments │
63
+ │ │
64
+ │ Each formatter returns: │
65
+ │ [detail1, detail2, changes_summary] │
66
+ └─────────────────────────────────────────────────────────────────────┘
67
+
68
+
69
+ Output: Formatted semantic diff report with:
70
+ • Header with difference count
71
+ • Individual difference sections
72
+ • Color-coded for easy scanning
73
+ ----
74
+
75
+ === Integration with DiffFormatter
76
+
77
+ The Semantic Diff Report is integrated at the `format_comparison_result()`
78
+ level:
79
+
80
+ [source,ruby]
81
+ ----
82
+ # In Canon::DiffFormatter
83
+ def format_comparison_result(comparison_result, expected, actual)
84
+ output = []
85
+
86
+ # 1. CANON VERBOSE tables (optional)
87
+ output << DebugOutput.verbose_tables_only(...)
88
+
89
+ # 2. Semantic Diff Report (always if diffs exist)
90
+ if comparison_result.differences.any?
91
+ output << DiffDetailFormatter.format_report(differences)
92
+ end
93
+
94
+ # 3. Detailed diff (always)
95
+ output << format(...)
96
+
97
+ output.compact.join("\n")
98
+ end
99
+ ----
100
+
101
+ This ensures the Semantic Diff Report is part of the main output, not debug
102
+ information.
103
+
104
+ == Output format
105
+
106
+ === General structure
107
+
108
+ Each difference is displayed as a vertical section with these components:
109
+
110
+ [example]
111
+ ====
112
+ [source]
113
+ ----
114
+ 🔍 DIFFERENCE #1/3 [STATUS]
115
+ ──────────────────────────────────────────────────────────────────────
116
+ Dimension: dimension_name
117
+ Location: /xpath/or/json.path
118
+
119
+ ⊖ Expected (File 1):
120
+ Details...
121
+
122
+ ⊕ Actual (File 2):
123
+ Details...
124
+
125
+ ✨ Changes:
126
+ Summary of what changed
127
+ ----
128
+ ====
129
+
130
+ Where:
131
+
132
+ `STATUS`:: Either `[NORMATIVE]` (green) or `[INFORMATIVE]` (yellow)
133
+ `Dimension`:: The match dimension that detected this difference (magenta)
134
+ `Location`:: XPath for XML/HTML, JSON path for JSON/YAML (blue)
135
+ `Expected`:: What was in File 1 (red heading)
136
+ `Actual`:: What was in File 2 (green heading)
137
+ `Changes`:: Actionable summary (yellow)
138
+
139
+ === Color scheme
140
+
141
+ The report uses colors to make scanning easy:
142
+
143
+ * **Dimension name**: Magenta
144
+ * **XPath/JSON path**: Blue
145
+ * **Expected heading**: Red (bold)
146
+ * **Actual heading**: Green (bold)
147
+ * **Changes heading**: Yellow (bold)
148
+ * **Status [NORMATIVE]**: Green (bold)
149
+ * **Status [INFORMATIVE]**: Yellow (bold)
150
+ * **Added items**: Green (with `+` prefix)
151
+ * **Removed items**: Red (with `-` prefix)
152
+ * **Element names**: Magenta
153
+ * **Attribute names**: Cyan
154
+
155
+ === Vertical layout
156
+
157
+ The vertical layout ensures no width constraints, making it easy to read
158
+ even with long attribute lists or deeply nested paths.
159
+
160
+ == XML/HTML dimensions
161
+
162
+ === General
163
+
164
+ XML and HTML comparisons use the same set of dimensions, classified based
165
+ on what aspect of the document differs.
166
+
167
+ === Attribute presence differences
168
+
169
+ Reports when attributes are missing or extra.
170
+
171
+ [example]
172
+ ====
173
+ [source]
174
+ ----
175
+ 🔍 DIFFERENCE #1/1 [NORMATIVE]
176
+ ──────────────────────────────────────────────────────────────────────
177
+ Dimension: attribute_presence
178
+ Location: /html/body/p
179
+
180
+ ⊖ Expected (File 1):
181
+ <p> with 2 attributes: id, lang
182
+
183
+ ⊕ Actual (File 2):
184
+ <p> with 5 attributes: id, lang, xmlns:o, xmlns:v, xmlns:w
185
+
186
+ ✨ Changes:
187
+ Added: +xmlns:o, +xmlns:v, +xmlns:w
188
+ ----
189
+ ====
190
+
191
+ The report shows:
192
+
193
+ * Element name
194
+ * Total attribute count in each document
195
+ * Complete list of attributes
196
+ * Which were added (green `+` prefix) or removed (red `-` prefix)
197
+
198
+ This makes it immediately clear what needs to be added or removed to fix
199
+ the test.
200
+
201
+ === Attribute value differences
202
+
203
+ Reports when an attribute value differs.
204
+
205
+ [example]
206
+ ====
207
+ [source]
208
+ ----
209
+ 🔍 DIFFERENCE #1/1 [NORMATIVE]
210
+ ──────────────────────────────────────────────────────────────────────
211
+ Dimension: attribute_values
212
+ Location: /html/body/div[@id="main"]
213
+
214
+ ⊖ Expected (File 1):
215
+ <div> class=" container fluid "
216
+
217
+ ⊕ Actual (File 2):
218
+ <div> class="container fluid"
219
+
220
+ ✨ Changes:
221
+ Whitespace normalization difference
222
+ ----
223
+ ====
224
+
225
+ The report shows:
226
+
227
+ * Which specific attribute has different value (cyan highlighting)
228
+ * Exact values on both sides (with quotes)
229
+ * Analysis of the difference type:
230
+ ** "Whitespace difference only" - Only leading/trailing whitespace differs
231
+ ** "Whitespace normalization difference" - Whitespace runs differ
232
+ ** "Value changed" - Actual content differs
233
+
234
+ === Text content differences
235
+
236
+ Reports when element text content differs.
237
+
238
+ [example]
239
+ ====
240
+ [source]
241
+ ----
242
+ 🔍 DIFFERENCE #1/1 [NORMATIVE]
243
+ ──────────────────────────────────────────────────────────────────────
244
+ Dimension: text_content
245
+ Location: /html/body/div/table/tbody/tr/td/pre/text
246
+
247
+ ⊖ Expected (File 1):
248
+ <text> "
249
+ puts \"Hello, world.\"
250
+ "
251
+
252
+ ⊕ Actual (File 2):
253
+ <text> "puts \"Hello, world.\" "
254
+
255
+ ✨ Changes:
256
+ ⚠️ Whitespace preserved (inside <pre>, <code>, etc. - whitespace
257
+ is significant)
258
+ ----
259
+ ====
260
+
261
+ The report shows:
262
+
263
+ * Text preview (truncated at 100 characters if long)
264
+ * Element containing the text
265
+ * Special warning if the text is inside whitespace-preserving elements
266
+ (`<pre>`, `<code>`, `<textarea>`, `<script>`, `<style>`)
267
+
268
+ The whitespace warning is important because Canon automatically switches
269
+ from `text_content: normalize` to `:strict` mode inside these elements.
270
+
271
+ === Structural whitespace differences
272
+
273
+ Reports whitespace-only text differences (usually informative).
274
+
275
+ [example]
276
+ ====
277
+ [source]
278
+ ----
279
+ 🔍 DIFFERENCE #1/1 [INFORMATIVE]
280
+ ──────────────────────────────────────────────────────────────────────
281
+ Dimension: structural_whitespace
282
+ Location: /root/section/p
283
+
284
+ ⊖ Expected (File 1):
285
+ <p> "hello␣␣world"
286
+
287
+ ⊕ Actual (File 2):
288
+ <p> "hello␣world"
289
+
290
+ ✨ Changes:
291
+ Whitespace-only difference (informative)
292
+ ----
293
+ ====
294
+
295
+ The report shows:
296
+
297
+ * Whitespace visualized using Unicode symbols:
298
+ ** `␣` - Space (U+0020)
299
+ ** `→` - Tab
300
+ ** `↵` - Newline
301
+ * Marked as `[INFORMATIVE]` (yellow) when `structural_whitespace: ignore`
302
+
303
+ === Comment differences
304
+
305
+ Reports when HTML/XML comment content differs.
306
+
307
+ [example]
308
+ ====
309
+ [source]
310
+ ----
311
+ 🔍 DIFFERENCE #1/1 [INFORMATIVE]
312
+ ──────────────────────────────────────────────────────────────────────
313
+ Dimension: comments
314
+ Location: /html/head
315
+
316
+ ⊖ Expected (File 1):
317
+ <!-- Original comment text -->
318
+
319
+ ⊕ Actual (File 2):
320
+ <!-- Modified comment text -->
321
+
322
+ ✨ Changes:
323
+ Comment content differs
324
+ ----
325
+ ====
326
+
327
+ == JSON/YAML dimensions
328
+
329
+ === General
330
+
331
+ JSON and YAML comparisons use path-based difference reporting with Hash
332
+ objects containing:
333
+
334
+ * `:path` - The JSON path to the difference (e.g., `user.profile.email`)
335
+ * `:value1` - Expected value
336
+ * `:value2` - Actual value
337
+ * `:diff_code` - Type of difference (MISSING_HASH_KEY,
338
+ UNEQUAL_PRIMITIVES, etc.)
339
+
340
+ === Hash key differences
341
+
342
+ Reports when a key is missing or has different value.
343
+
344
+ [example]
345
+ ====
346
+ [source]
347
+ ----
348
+ 🔍 DIFFERENCE #1/1 [NORMATIVE]
349
+ ──────────────────────────────────────────────────────────────────────
350
+ Dimension: 2
351
+ Location: user.email
352
+
353
+ ⊖ Expected (File 1):
354
+ user.email = "alice@example.com"
355
+
356
+ ⊕ Actual (File 2):
357
+ user.email = nil
358
+
359
+ ✨ Changes:
360
+ Key missing
361
+ ----
362
+ ====
363
+
364
+ === Primitive value differences
365
+
366
+ Reports when primitive values (strings, numbers, booleans) differ.
367
+
368
+ [example]
369
+ ====
370
+ [source]
371
+ ----
372
+ 🔍 DIFFERENCE #1/1 [NORMATIVE]
373
+ ──────────────────────────────────────────────────────────────────────
374
+ Dimension: 15
375
+ Location: users[0].age
376
+
377
+ ⊖ Expected (File 1):
378
+ users[0].age = 25
379
+
380
+ ⊕ Actual (File 2):
381
+ users[0].age = 30
382
+
383
+ ✨ Changes:
384
+ Value changed
385
+ ----
386
+ ====
387
+
388
+ === Array differences
389
+
390
+ Reports when arrays have different lengths or elements.
391
+
392
+ [example]
393
+ ====
394
+ [source]
395
+ ----
396
+ 🔍 DIFFERENCE #1/1 [NORMATIVE]
397
+ ──────────────────────────────────────────────────────────────────────
398
+ Dimension: 12
399
+ Location: items
400
+
401
+ ⊖ Expected (File 1):
402
+ items = [...] (5 items)
403
+
404
+ ⊕ Actual (File 2):
405
+ items = [...] (3 items)
406
+
407
+ ✨ Changes:
408
+ Array length differs
409
+ ----
410
+ ====
411
+
412
+ Complex values (hashes, arrays) are shown as `{...} (N keys)` or
413
+ `[...] (N items)` to keep output concise.
414
+
415
+ == Special features
416
+
417
+ === XPath location extraction
418
+
419
+ For XML/HTML differences, the report extracts XPath with:
420
+
421
+ * Full path from root: `/html/body/div/section/p`
422
+ * Position predicates when multiple siblings: `/p[2]`, `/div[3]`
423
+ * Safe traversal with depth limits to prevent infinite loops
424
+ * Graceful error handling for circular references
425
+ * Document node detection to stop at appropriate boundaries
426
+
427
+ === Whitespace preservation detection
428
+
429
+ The report detects when text is inside whitespace-preserving HTML elements
430
+ and shows a special warning:
431
+
432
+ [source]
433
+ ----
434
+ ✨ Changes: ⚠️ Whitespace preserved (inside <pre>, <code>, etc. -
435
+ whitespace is significant)
436
+ ----
437
+
438
+ This is important because Canon automatically switches to `:strict` mode
439
+ for text content inside these elements:
440
+
441
+ * `<pre>` - Preformatted text
442
+ * `<code>` - Code blocks
443
+ * `<textarea>` - Text input areas
444
+ * `<script>` - JavaScript code
445
+ * `<style>` - CSS style sheets
446
+
447
+ The warning helps developers understand why a seemingly minor whitespace
448
+ difference is causing a test failure.
449
+
450
+ === Comprehensive error handling
451
+
452
+ The formatter includes multiple layers of error handling:
453
+
454
+ * Top-level rescue in `format_single_diff()` - Catches any formatting
455
+ errors
456
+ * Safe XPath extraction with depth limits and circular reference detection
457
+ * Safe parent traversal with document node checks
458
+ * Graceful fallbacks when node types are unexpected
459
+
460
+ This ensures the Semantic Diff Report never crashes, even with unusual DOM
461
+ structures.
462
+
463
+ == Implementation
464
+
465
+ === DiffDetailFormatter class
466
+
467
+ Module: `Canon::DiffFormatter::DiffDetailFormatter`
468
+
469
+ Location: `lib/canon/diff_formatter/diff_detail_formatter.rb`
470
+
471
+ Main entry point:
472
+
473
+ [source,ruby]
474
+ ----
475
+ # Format all differences as semantic diff report
476
+ def self.format_report(differences, use_color: true)
477
+ # Returns formatted string with all difference sections
478
+ end
479
+ ----
480
+
481
+ === Dimension dispatch mechanism
482
+
483
+ The formatter uses dimension-based dispatch:
484
+
485
+ [source,ruby]
486
+ ----
487
+ def format_dimension_details(diff, use_color)
488
+ # Handle Hash diffs (JSON/YAML)
489
+ return format_hash_diff_details(diff) if diff.is_a?(Hash)
490
+
491
+ # Handle DiffNode (XML/HTML) based on dimension
492
+ case diff.dimension
493
+ when :attribute_presence
494
+ format_attribute_presence_details(diff)
495
+ when :attribute_values
496
+ format_attribute_values_details(diff)
497
+ when :text_content
498
+ format_text_content_details(diff)
499
+ # ... other dimensions
500
+ end
501
+ end
502
+ ----
503
+
504
+ This ensures each difference type is optimally formatted.
505
+
506
+ === Helper methods
507
+
508
+ Key helper methods:
509
+
510
+ `extract_xpath(node)`:: Extracts XPath from XML/HTML nodes with safety
511
+ limits and error handling
512
+
513
+ `extract_location(diff)`:: Dispatches to XPath extraction for XML/HTML or
514
+ returns JSON path for JSON/YAML
515
+
516
+ `inside_preserve_element?(node)`:: Detects if node is inside `<pre>`,
517
+ `<code>`, etc. with safe parent traversal
518
+
519
+ `get_attribute_names(node)`:: Extracts sorted attribute names from elements
520
+
521
+ `find_differing_attribute(node1, node2)`:: Finds which attribute has
522
+ different value
523
+
524
+ `format_json_value(value)`:: Formats JSON values concisely ({...},
525
+ [...], primitives)
526
+
527
+ All helpers include comprehensive error handling to ensure the report never
528
+ crashes.
@@ -0,0 +1,17 @@
1
+ ---
2
+ layout: default
3
+ title: Understanding Canon
4
+ nav_order: 3
5
+ has_children: true
6
+ ---
7
+ = Understanding Canon
8
+
9
+ Learn how Canon works internally:
10
+
11
+ * **link:FORMATS[Format support]** - XML, HTML, JSON, YAML
12
+ canonicalization
13
+ * **link:MODES[Diff modes]** - By-line vs by-object comparison modes
14
+ * **link:MATCH_ARCHITECTURE[Match architecture]** - Three-phase
15
+ comparison flow
16
+
17
+ These documents explain Canon's core concepts and architecture.