markdown_composer 0.7.0

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 (116) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +23 -0
  3. data/LICENSE.txt +21 -0
  4. data/README.md +278 -0
  5. data/ROADMAP.md +80 -0
  6. data/docs/_md_composer_architecture.md +50 -0
  7. data/docs/_md_composer_cheatsheet.md +72 -0
  8. data/docs/_md_composer_concepts.md +64 -0
  9. data/docs/_md_composer_dev_guide.md +55 -0
  10. data/docs/_md_composer_getting_started.md +114 -0
  11. data/docs/_md_composer_readme.md +93 -0
  12. data/docs/_md_composer_user_guide.md +65 -0
  13. data/docs/ai/md_composer_ai_audit.md +35 -0
  14. data/docs/ai/md_composer_ai_canonical_docs.md +44 -0
  15. data/docs/ai/md_composer_ai_source_map.md +39 -0
  16. data/docs/compose/md_composer_compose_actions.md +338 -0
  17. data/docs/compose/md_composer_compose_anatomy.md +156 -0
  18. data/docs/compose/md_composer_compose_buffer.md +81 -0
  19. data/docs/compose/md_composer_compose_examples.md +31 -0
  20. data/docs/compose/md_composer_compose_include.md +136 -0
  21. data/docs/compose/md_composer_compose_select.md +198 -0
  22. data/docs/compose/md_composer_compose_sources.md +161 -0
  23. data/docs/compose/md_composer_compose_targets.md +194 -0
  24. data/docs/examples/md_composer_example_basic_compose.md +57 -0
  25. data/docs/examples/md_composer_example_buffer_target_actions.md +83 -0
  26. data/docs/examples/md_composer_example_fixtures.md +62 -0
  27. data/docs/examples/md_composer_example_html_output.md +50 -0
  28. data/docs/examples/md_composer_example_modify.md +77 -0
  29. data/docs/examples/md_composer_example_multi_row_compose.md +67 -0
  30. data/docs/examples/md_composer_example_ruby_plans.md +62 -0
  31. data/docs/examples/md_composer_example_structured_data.md +68 -0
  32. data/docs/examples/md_composer_example_transforms.md +68 -0
  33. data/docs/examples/md_composer_example_yaml_json_rows.md +56 -0
  34. data/docs/examples/md_composer_examples_readme.md +45 -0
  35. data/docs/examples/md_composer_runnable_examples.md +374 -0
  36. data/docs/examples/md_composer_source_ruby_dsl.md +88 -0
  37. data/docs/reference/md_composer_nested.md +170 -0
  38. data/docs/reference/md_composer_reference_api.md +71 -0
  39. data/docs/reference/md_composer_reference_capabilities.md +63 -0
  40. data/docs/reference/md_composer_reference_diagnostics.md +54 -0
  41. data/docs/reference/md_composer_reference_plan_schema.md +75 -0
  42. data/docs/reference/md_composer_reference_registries.md +63 -0
  43. data/docs/reference/md_composer_take.md +221 -0
  44. data/docs/reference/md_composer_unit_tokens.md +228 -0
  45. data/docs/reference/md_composer_where.md +227 -0
  46. data/docs/transform/md_composer_transform_anatomy.md +112 -0
  47. data/docs/transform/md_composer_transform_examples.md +30 -0
  48. data/docs/transform/md_composer_transform_modes.md +83 -0
  49. data/docs/transform/md_composer_transform_options.md +142 -0
  50. data/docs/transform/md_composer_transform_scope.md +97 -0
  51. data/docs/transform/md_composer_transform_transforms.md +99 -0
  52. data/examples/README.md +20 -0
  53. data/examples/advanced_composer.rb +207 -0
  54. data/examples/basic_compose.rb +24 -0
  55. data/examples/complex_composer.rb +235 -0
  56. data/examples/example_support.rb +18 -0
  57. data/examples/fixtures/current.md +179 -0
  58. data/examples/fixtures/faq.md +58 -0
  59. data/examples/fixtures/guide.md +62 -0
  60. data/examples/fixtures/site_intro.md +29 -0
  61. data/examples/fixtures/source.html +22 -0
  62. data/examples/html_input.rb +26 -0
  63. data/examples/output/advanced_composer.md +76 -0
  64. data/examples/output/basic_compose.md +25 -0
  65. data/examples/output/complex_composer.md +85 -0
  66. data/examples/output/html_input.md +4 -0
  67. data/examples/output/source_list_dsl.md +126 -0
  68. data/examples/output/standard_composer.md +46 -0
  69. data/examples/output/standard_sources_buffer.md +31 -0
  70. data/examples/output/yaml_plan.md +43 -0
  71. data/examples/plans/basic.yml +20 -0
  72. data/examples/source_list_dsl.rb +41 -0
  73. data/examples/standard_composer.rb +42 -0
  74. data/examples/standard_sources_buffer.rb +62 -0
  75. data/examples/yaml_plan.rb +17 -0
  76. data/lib/markdown_composer/capabilities.rb +223 -0
  77. data/lib/markdown_composer/composition_buffer.rb +378 -0
  78. data/lib/markdown_composer/data_path.rb +313 -0
  79. data/lib/markdown_composer/diagnostics.rb +63 -0
  80. data/lib/markdown_composer/document_index/html_parser.rb +84 -0
  81. data/lib/markdown_composer/document_index/markdown_parser.rb +338 -0
  82. data/lib/markdown_composer/document_index.rb +94 -0
  83. data/lib/markdown_composer/executor.rb +284 -0
  84. data/lib/markdown_composer/markdown_renderer.rb +105 -0
  85. data/lib/markdown_composer/plan.rb +436 -0
  86. data/lib/markdown_composer/plan_builder.rb +111 -0
  87. data/lib/markdown_composer/registries/action_entries.rb +26 -0
  88. data/lib/markdown_composer/registries/condition_entries.rb +58 -0
  89. data/lib/markdown_composer/registries/registry.rb +69 -0
  90. data/lib/markdown_composer/registries/source_entries.rb +18 -0
  91. data/lib/markdown_composer/registries/support_values.rb +23 -0
  92. data/lib/markdown_composer/registries/take_entries.rb +31 -0
  93. data/lib/markdown_composer/registries/take_registry.rb +18 -0
  94. data/lib/markdown_composer/registries/target_entries.rb +40 -0
  95. data/lib/markdown_composer/registries/unit_token_entries.rb +62 -0
  96. data/lib/markdown_composer/registries/where_registry.rb +84 -0
  97. data/lib/markdown_composer/registries.rb +46 -0
  98. data/lib/markdown_composer/result.rb +34 -0
  99. data/lib/markdown_composer/selection_resolver.rb +181 -0
  100. data/lib/markdown_composer/source.rb +57 -0
  101. data/lib/markdown_composer/source_list_builder.rb +47 -0
  102. data/lib/markdown_composer/take.rb +129 -0
  103. data/lib/markdown_composer/transform_options.rb +66 -0
  104. data/lib/markdown_composer/transform_runner/content_placement.rb +63 -0
  105. data/lib/markdown_composer/transform_runner/field_interpolator.rb +213 -0
  106. data/lib/markdown_composer/transform_runner/heading_numbering.rb +106 -0
  107. data/lib/markdown_composer/transform_runner/scope_resolver.rb +87 -0
  108. data/lib/markdown_composer/transform_runner.rb +264 -0
  109. data/lib/markdown_composer/transforms/default_entries.rb +31 -0
  110. data/lib/markdown_composer/transforms/registry.rb +11 -0
  111. data/lib/markdown_composer/validator.rb +378 -0
  112. data/lib/markdown_composer/value_object.rb +15 -0
  113. data/lib/markdown_composer/version.rb +5 -0
  114. data/lib/markdown_composer/where.rb +313 -0
  115. data/lib/markdown_composer.rb +114 -0
  116. metadata +260 -0
@@ -0,0 +1,112 @@
1
+ ---
2
+ title: Markdown Composer Transform Anatomy
3
+ type: guide
4
+ status: current
5
+ updated: 2026-06-01
6
+ description: Public anatomy guide for Markdown Composer transform rows.
7
+ ---
8
+
9
+ # Markdown Composer Transform Anatomy
10
+
11
+ ### References
12
+
13
+ | What | Where |
14
+ |------|-------|
15
+ | Scope | [./md_composer_transform_scope.md](./md_composer_transform_scope.md) |
16
+ | Transforms | [./md_composer_transform_transforms.md](./md_composer_transform_transforms.md) |
17
+ | Modes | [./md_composer_transform_modes.md](./md_composer_transform_modes.md) |
18
+ | Options | [./md_composer_transform_options.md](./md_composer_transform_options.md) |
19
+ | Transform examples | [./md_composer_transform_examples.md](./md_composer_transform_examples.md) |
20
+
21
+ ---
22
+
23
+ ## 1. Row Shape
24
+
25
+ A transform row reshapes composed output.
26
+
27
+ ```text
28
+ Scope -> Transform -> Mode -> Options
29
+ ```
30
+
31
+ Full row anatomy:
32
+
33
+ ```text
34
+ transform_row = {
35
+ scope: scope_selector | output,
36
+ transform: transform_family,
37
+ mode: family_mode,
38
+ options: option_hash | option_string
39
+ }
40
+ ```
41
+
42
+ Read it as a sentence:
43
+
44
+ ```text
45
+ Inside this Scope, run this Transform in this Mode with these Options.
46
+ ```
47
+
48
+ Example:
49
+
50
+ ```ruby
51
+ {
52
+ "scope" => "output",
53
+ "transform" => "replace_text",
54
+ "mode" => "literal",
55
+ "options" => { "from" => "Draft", "to" => "Final" }
56
+ }
57
+ ```
58
+
59
+ ## 2. Top-Level Transforms
60
+
61
+ Top-level transforms run after all compose rows have finished building the buffer.
62
+
63
+ ```ruby
64
+ config = {
65
+ "compose" => [
66
+ { "select" => "heading_2_section", "action" => "set" }
67
+ ],
68
+ "transform" => [
69
+ { "scope" => "output", "transform" => "heading_numbers", "mode" => "rebuild", "options" => { "levels" => [2] } }
70
+ ]
71
+ }
72
+ ```
73
+
74
+ If `scope` is omitted, it defaults to `output`.
75
+
76
+ ## 3. Row-Level Transforms
77
+
78
+ Row-level transforms are used by actions that explicitly support nested transforms:
79
+
80
+ | Action | What nested transforms affect |
81
+ |--------|-------------------------------|
82
+ | `modify` | A temporary fragment made from selected source content. |
83
+ | `transform_buffer_target` | Existing buffer content matched by the action target, unless an explicit nested scope is supplied. |
84
+
85
+ Row-level transforms are not a separate global pipeline. They belong to the compose row that declares them.
86
+
87
+ ## 4. Field Responsibilities
88
+
89
+ | Field | Responsibility | Common mistake |
90
+ |-------|----------------|----------------|
91
+ | `scope` | Chooses which buffer units the transform changes. | Forgetting that top-level scope matches composed output, not original source. |
92
+ | `transform` | Chooses the transform family. | Picking a transform without checking its modes. |
93
+ | `mode` | Chooses behavior inside the transform family. | Assuming modes share the same required options. |
94
+ | `options` | Provides transform-specific settings. | Treating options as JSON only; hashes and semicolon strings both work. |
95
+
96
+ ## 5. Validation
97
+
98
+ Validation checks transform tokens, modes, required options, unknown option keys, option value types, and policy-gated transforms.
99
+
100
+ ```ruby
101
+ validation = MarkdownComposer.validate(config: config, sources: sources)
102
+ ```
103
+
104
+ Missing options produce `transform.option_missing`. Unknown options produce `transform.option_unknown`.
105
+
106
+ ## 6. Important Caveats
107
+
108
+ | Caveat | Meaning |
109
+ |--------|---------|
110
+ | Optional options are validation metadata. | Some accepted optional keys are ahead of standalone runner behavior. |
111
+ | HTML link attribute modes are special. | `links/nofollow` and `links/target_blank` apply during final HTML rendering for top-level transforms. |
112
+ | Policy-gated modes are not ordinary features. | `adapter`, `sanitise`, and `order/target_order` need host policy or are deferred. |
@@ -0,0 +1,30 @@
1
+ ---
2
+ title: Markdown Composer Transform Examples
3
+ type: examples
4
+ status: current
5
+ updated: 2026-06-01
6
+ description: Transform-focused example index for Markdown Composer.
7
+ ---
8
+
9
+ # Markdown Composer Transform Examples
10
+
11
+ ### References
12
+
13
+ | What | Where |
14
+ |------|-------|
15
+ | Transform anatomy | [./md_composer_transform_anatomy.md](./md_composer_transform_anatomy.md) |
16
+ | Transform examples | [../examples/md_composer_example_transforms.md](../examples/md_composer_example_transforms.md) |
17
+ | HTML output | [../examples/md_composer_example_html_output.md](../examples/md_composer_example_html_output.md) |
18
+
19
+ ---
20
+
21
+ ## 1. Example Map
22
+
23
+ | Need | Example |
24
+ |------|---------|
25
+ | Replace text after composing | [Transform examples](../examples/md_composer_example_transforms.md) |
26
+ | Use transform options | [Transform options](./md_composer_transform_options.md) |
27
+ | Add HTML link attributes | [HTML output](../examples/md_composer_example_html_output.md) |
28
+ | Transform during `modify` | [Modify](../examples/md_composer_example_modify.md) |
29
+ | Transform an existing buffer target | [Buffer target actions](../examples/md_composer_example_buffer_target_actions.md) |
30
+
@@ -0,0 +1,83 @@
1
+ ---
2
+ title: Markdown Composer Transform Modes
3
+ type: guide
4
+ status: current
5
+ updated: 2026-06-01
6
+ description: Public guide to transform modes and mode-specific behavior.
7
+ ---
8
+
9
+ # Markdown Composer Transform Modes
10
+
11
+ ### References
12
+
13
+ | What | Where |
14
+ |------|-------|
15
+ | Transform families | [./md_composer_transform_transforms.md](./md_composer_transform_transforms.md) |
16
+ | Options | [./md_composer_transform_options.md](./md_composer_transform_options.md) |
17
+ | Capabilities | [../reference/md_composer_reference_capabilities.md](../reference/md_composer_reference_capabilities.md) |
18
+
19
+ ---
20
+
21
+ ## 1. Purpose
22
+
23
+ `mode` selects behavior inside a transform family.
24
+
25
+ ```ruby
26
+ { "transform" => "heading_numbers", "mode" => "rebuild", "options" => { "levels" => [2] } }
27
+ ```
28
+
29
+ Modes are transform-specific. Do not assume options from one mode work in another mode.
30
+
31
+ ## 2. Syntax Anatomy
32
+
33
+ `mode` is not global. It is resolved inside the selected transform family.
34
+
35
+ ```text
36
+ mode_key = transform_family "/" mode
37
+
38
+ heading_numbers/rebuild
39
+ replace_text/literal
40
+ links/rewrite_url
41
+ ```
42
+
43
+ The mode key controls:
44
+
45
+ | Control | Example |
46
+ |---------|---------|
47
+ | Required options | `replace_text/literal` requires `from` and `to`. |
48
+ | Optional options | `heading_numbers/rebuild` accepts `start`, `format`, and related keys. |
49
+ | Output constraints | `links/nofollow` and `links/target_blank` are meaningful with HTML output. |
50
+ | Policy gates | `order/target_order` is not a normal ready-to-use mode. |
51
+
52
+ ## 3. Mode Matrix
53
+
54
+ | Transform | Modes |
55
+ |-----------|-------|
56
+ | `heading_numbers` | `keep`, `strip`, `add`, `rebuild` |
57
+ | `replace_text` | `literal`, `word`, `regex` |
58
+ | `links` | `keep`, `unwrap`, `remove`, `rewrite_url`, `nofollow`, `target_blank` |
59
+ | `heading_levels` | `promote`, `demote`, `normalise` |
60
+ | `remove_empty` | `remove` |
61
+ | `insert_before`, `insert_after`, `prepend_content`, `append_content` | `insert` |
62
+ | `replace_content` | `replace` |
63
+ | `remove_content` | `remove` |
64
+ | `dedupe` | `source_node_id`, `normalised_text` |
65
+ | `order` | `action_order`, `source_order`, `target_order` |
66
+ | `sanitise` | `block_safe`, `text_only`, `links_unwrapped`, `strict` |
67
+
68
+ ## 4. GUI Filtering Rule
69
+
70
+ If you build a UI, filter by mode-level `enabled`, not only transform-level `enabled`.
71
+
72
+ Example: `order` can be visible while `order/target_order` is gated. The mode metadata is the source of truth for whether a mode should be offered.
73
+
74
+ ## 5. HTML Link Attribute Modes
75
+
76
+ `links/nofollow` and `links/target_blank` are special:
77
+
78
+ | Rule | Meaning |
79
+ |------|---------|
80
+ | Require HTML output metadata | They are useful with `output: "html"`. |
81
+ | Top-level post-processing | Final HTML link attributes are applied after Markdown rendering. |
82
+ | Global external-link behavior | The renderer walks rendered links; it skips empty links and `#anchor` links. |
83
+ | Row-level caveat | Row-level link attribute transforms do not update final HTML attributes. |
@@ -0,0 +1,142 @@
1
+ ---
2
+ title: Markdown Composer Transform Options
3
+ type: guide
4
+ status: current
5
+ updated: 2026-06-01
6
+ description: Public guide to transform options, hash syntax, semicolon syntax, required keys, and validation errors.
7
+ ---
8
+
9
+ # Markdown Composer Transform Options
10
+
11
+ ### References
12
+
13
+ | What | Where |
14
+ |------|-------|
15
+ | Transform anatomy | [./md_composer_transform_anatomy.md](./md_composer_transform_anatomy.md) |
16
+ | Modes | [./md_composer_transform_modes.md](./md_composer_transform_modes.md) |
17
+ | Diagnostics | [../reference/md_composer_reference_diagnostics.md](../reference/md_composer_reference_diagnostics.md) |
18
+
19
+ ---
20
+
21
+ ## 1. Purpose
22
+
23
+ `options` provides settings for a transform mode.
24
+
25
+ ```ruby
26
+ { "transform" => "replace_text", "mode" => "literal", "options" => { "from" => "Draft", "to" => "Final" } }
27
+ ```
28
+
29
+ The field label in capabilities says `Options JSON`, but public authors can use either structured hashes or semicolon option strings.
30
+
31
+ ## 2. Syntax Anatomy
32
+
33
+ `options` can be a structured hash/object or a compact semicolon string. Both forms normalize into the same option map before validation.
34
+
35
+ ```text
36
+ options = option_hash | option_string
37
+
38
+ option_hash = { key => value, key => value }
39
+
40
+ option_string = key:value; key:value; key:value
41
+
42
+ value = string | number | boolean | nil | unit_token | unit_list | target_selector
43
+ ```
44
+
45
+ Option names and required keys depend on the selected transform/mode pair:
46
+
47
+ ```text
48
+ transform + mode -> allowed option keys
49
+ ```
50
+
51
+ Some option values have their own mini-grammar:
52
+
53
+ | Option kind | Example | Notes |
54
+ |-------------|---------|-------|
55
+ | Text value | `from:"Draft"` | Quote values that contain punctuation or semicolons. |
56
+ | Boolean | `case_sensitive:false` | Normalizes to `false`. |
57
+ | Unit token | `unit:p` | Aliases normalize, for example `p` to `paragraph`. |
58
+ | Unit list | `levels:h2,h3` | Comma-separated aliases normalize where supported. |
59
+ | Target selector | `target:"heading_2_section[first:1]"` | Used by target-order style metadata and gated/deferred modes. |
60
+
61
+ ## 3. Hash Syntax
62
+
63
+ Use hashes for Ruby code, JSON configs, generated plans, and GUI builders.
64
+
65
+ ```ruby
66
+ "options" => {
67
+ "from" => "Draft",
68
+ "to" => "Final",
69
+ "case_sensitive" => false
70
+ }
71
+ ```
72
+
73
+ ## 4. Semicolon String Syntax
74
+
75
+ Use semicolon strings for compact authoring.
76
+
77
+ ```ruby
78
+ "options" => "from:\"Draft\"; to:\"Final\"; case_sensitive:false"
79
+ ```
80
+
81
+ The parser splits only at top-level semicolons, so quoted semicolons and nested expressions can survive.
82
+
83
+ Values support:
84
+
85
+ | Value form | Example |
86
+ |------------|---------|
87
+ | String | `from:"Draft"` |
88
+ | Boolean | `case_sensitive:false` |
89
+ | Nil | `target:nil` |
90
+ | Integer | `limit:2` |
91
+ | Unit aliases | `unit:p`, `as:paragraph`, `levels:h2,h3` |
92
+
93
+ Option values normalize through unit token aliases where appropriate.
94
+
95
+ ## 5. Required Options
96
+
97
+ | Transform / mode | Required |
98
+ |------------------|----------|
99
+ | `replace_text/literal` | `from`, `to` |
100
+ | `replace_text/word` | `from`, `to` |
101
+ | `replace_text/regex` | `from`, `to` |
102
+ | `links/rewrite_url` | `from`, `to` |
103
+ | `heading_numbers/add` | `levels` |
104
+ | `heading_numbers/rebuild` | `levels` |
105
+ | `heading_levels/promote` | `by` |
106
+ | `heading_levels/demote` | `by` |
107
+ | `heading_levels/normalise` | `to` |
108
+ | `remove_empty/remove` | `unit` |
109
+ | content insertion/replacement | `content`, `as` |
110
+ | `order/target_order` | `target` |
111
+
112
+ ## 6. Optional Options
113
+
114
+ Examples:
115
+
116
+ | Transform / mode | Optional |
117
+ |------------------|----------|
118
+ | `heading_numbers/rebuild` | `start`, `format`, `exclude`, `restart_at` |
119
+ | `replace_text/literal` | `case_sensitive`, `limit`, `whole_node` |
120
+ | `links/rewrite_url` | `match`, `case_sensitive` |
121
+ | `heading_levels/promote` | `min_level`, `max_level` |
122
+ | `remove_empty/remove` | `trim`, `ignore_nbsp`, `ignore_comments` |
123
+ | `insert_before/insert` | `dedupe`, `parse_as` |
124
+
125
+ Some optional keys are validation metadata ahead of standalone runner behavior. Validate and test advanced options before promising them in a host UI.
126
+
127
+ ## 7. Validation Errors
128
+
129
+ | Diagnostic | Meaning |
130
+ |------------|---------|
131
+ | `transform.option_missing` | A required option is absent. |
132
+ | `transform.option_unknown` | The option key is not accepted for that transform/mode. |
133
+ | `transform.options_syntax` | The options value is not a readable string or object. |
134
+ | `transform.policy_required` | The transform requires host policy. |
135
+
136
+ ## 8. Common Mistakes
137
+
138
+ | Mistake | Fix |
139
+ |---------|-----|
140
+ | Treating options as JSON only. | Use either hashes or semicolon strings. |
141
+ | Copying options between modes. | Check the selected transform/mode pair. |
142
+ | Passing `levels:2,3` and expecting raw integers only. | Levels may normalize aliases like `h2,h3` to canonical tokens. |
@@ -0,0 +1,97 @@
1
+ ---
2
+ title: Markdown Composer Transform Scope
3
+ type: guide
4
+ status: current
5
+ updated: 2026-06-01
6
+ description: Public guide to transform scope syntax and behavior.
7
+ ---
8
+
9
+ # Markdown Composer Transform Scope
10
+
11
+ ### References
12
+
13
+ | What | Where |
14
+ |------|-------|
15
+ | Transform anatomy | [./md_composer_transform_anatomy.md](./md_composer_transform_anatomy.md) |
16
+ | Compose targets | [../compose/md_composer_compose_targets.md](../compose/md_composer_compose_targets.md) |
17
+ | Select syntax | [../compose/md_composer_compose_select.md](../compose/md_composer_compose_select.md) |
18
+ | Unit tokens | [../reference/md_composer_unit_tokens.md](../reference/md_composer_unit_tokens.md) |
19
+ | Take modifiers | [../reference/md_composer_take.md](../reference/md_composer_take.md) |
20
+ | Where conditions | [../reference/md_composer_where.md](../reference/md_composer_where.md) |
21
+
22
+ ---
23
+
24
+ ## 1. Purpose
25
+
26
+ `scope` chooses which units in the composed buffer a transform should affect.
27
+
28
+ ```ruby
29
+ { "scope" => "heading_2_section[first:1]", "transform" => "replace_text", "mode" => "literal", "options" => "from:Draft; to:Final" }
30
+ ```
31
+
32
+ If omitted, scope defaults to `output`.
33
+
34
+ ## 2. Syntax Anatomy
35
+
36
+ Scope reuses selector syntax:
37
+
38
+ ```text
39
+ scope = output | scope_selector
40
+
41
+ scope_selector = unit_token [take] [where condition]
42
+ ```
43
+
44
+ For every scope-capable token and alias, see [Unit Tokens](../reference/md_composer_unit_tokens.md).
45
+ For shared scope-selector take syntax, see [Take Modifiers](../reference/md_composer_take.md).
46
+ For shared scope-selector `where` syntax, see [Where Conditions](../reference/md_composer_where.md).
47
+
48
+ Scope selector parts:
49
+
50
+ | Part | Example | Meaning |
51
+ |------|---------|---------|
52
+ | `output` | `output` | The whole composed buffer or row fragment. |
53
+ | `unit_token` | `heading_2_section`, `paragraph`, `link` | A unit matched inside the current transform input. |
54
+ | `[take]` | `[first:1]`, `[position:2]` | Optional positional narrowing. |
55
+ | `where condition` | `where text:contains("Draft")` | Optional filtering. |
56
+
57
+ Examples:
58
+
59
+ | Goal | Scope |
60
+ |------|-------|
61
+ | Whole composed output | `output` |
62
+ | First H2 section in buffer | `heading_2_section[first:1]` |
63
+ | Paragraphs with text | `paragraph where text:contains("Draft")` |
64
+ | Code blocks | `code_block` |
65
+ | Data blocks | `data_block where format:equals("yaml")` |
66
+
67
+ ## 3. Top-Level Scope
68
+
69
+ Top-level transform scope matches the composed buffer after all compose rows have run. It does not search the original sources.
70
+
71
+ ```ruby
72
+ {
73
+ "transform" => [
74
+ { "scope" => "output", "transform" => "replace_text", "mode" => "literal", "options" => "from:Draft; to:Final" }
75
+ ]
76
+ }
77
+ ```
78
+
79
+ ## 4. Row-Level Scope
80
+
81
+ For `modify`, omitted nested scope means the temporary fragment output.
82
+
83
+ For `transform_buffer_target`, omitted nested scope means the action `target`. An explicit nested scope overrides the action target.
84
+
85
+ ## 5. Data Record And Data Value Scope
86
+
87
+ Capabilities mark `data_record` and `data_value` as output-only scopes. In the standalone Markdown buffer, they are transient derived nodes produced by `data_path(...)` and then serialized back to Markdown. Do not rely on `scope: "data_record"` or `scope: "data_value"` after ordinary composition unless a host adapter preserves structured nodes.
88
+
89
+ Use `data_block` scope for source-like structured blocks, or target the serialized Markdown text after extraction.
90
+
91
+ ## 6. Common Mistakes
92
+
93
+ | Mistake | Fix |
94
+ |---------|-----|
95
+ | Expecting scope to search source documents. | Scope searches the composed buffer or row fragment. |
96
+ | Omitting scope without understanding defaults. | Top-level transforms default to `output`; row-level defaults depend on action. |
97
+ | Using `data_value` as a reliable standalone scope. | Treat it as transient structured-output metadata unless preserved by a host. |
@@ -0,0 +1,99 @@
1
+ ---
2
+ title: Markdown Composer Transform Families
3
+ type: guide
4
+ status: current
5
+ updated: 2026-06-01
6
+ description: Public guide to Markdown Composer transform families.
7
+ ---
8
+
9
+ # Markdown Composer Transform Families
10
+
11
+ ### References
12
+
13
+ | What | Where |
14
+ |------|-------|
15
+ | Transform anatomy | [./md_composer_transform_anatomy.md](./md_composer_transform_anatomy.md) |
16
+ | Modes | [./md_composer_transform_modes.md](./md_composer_transform_modes.md) |
17
+ | Options | [./md_composer_transform_options.md](./md_composer_transform_options.md) |
18
+
19
+ ---
20
+
21
+ ## 1. Purpose
22
+
23
+ `transform` chooses the family of operation to run. `mode` chooses the behavior inside that family.
24
+
25
+ ```ruby
26
+ { "transform" => "replace_text", "mode" => "literal", "options" => { "from" => "Draft", "to" => "Final" } }
27
+ ```
28
+
29
+ ## 2. Syntax Anatomy
30
+
31
+ The `transform` field is only the family token. It is paired with `mode` and `options` to form an executable transform row.
32
+
33
+ ```text
34
+ transform_row = scope + transform_family + mode + options
35
+
36
+ transform_family = heading_numbers
37
+ | replace_text
38
+ | links
39
+ | heading_levels
40
+ | remove_empty
41
+ | insert_before
42
+ | insert_after
43
+ | prepend_content
44
+ | append_content
45
+ | replace_content
46
+ | remove_content
47
+ | dedupe
48
+ | order
49
+ | sanitise
50
+ | adapter
51
+
52
+ mode = one mode supported by that family
53
+
54
+ options = required and optional keys for that family/mode pair
55
+ ```
56
+
57
+ Example anatomy:
58
+
59
+ | Field | Value | Meaning |
60
+ |-------|-------|---------|
61
+ | `scope` | `paragraph where text:contains("Draft")` | Match paragraphs in the composed buffer. |
62
+ | `transform` | `replace_text` | Use the text replacement family. |
63
+ | `mode` | `literal` | Use literal text replacement. |
64
+ | `options` | `from:"Draft"; to:"Final"` | Provide required replacement values. |
65
+
66
+ ## 3. Transform Families
67
+
68
+ | Transform | Use for |
69
+ |-----------|---------|
70
+ | `heading_numbers` | Keep, strip, add, or rebuild heading numbering. |
71
+ | `replace_text` | Replace literal text, whole words, or regex matches. |
72
+ | `links` | Keep, unwrap, remove, rewrite URLs, or add HTML link attributes. |
73
+ | `heading_levels` | Promote, demote, or normalize heading levels. |
74
+ | `remove_empty` | Remove empty units. |
75
+ | `insert_before` / `insert_after` | Insert content near scoped units. |
76
+ | `prepend_content` / `append_content` | Add content inside or near scoped units. |
77
+ | `replace_content` | Replace scoped content. |
78
+ | `remove_content` | Remove scoped content. |
79
+ | `dedupe` | Remove duplicates by source node id or normalized text. |
80
+ | `order` | Preserve action/source order; target order is gated/deferred. |
81
+ | `sanitise` | Host-policy sanitizing profiles. |
82
+ | `adapter` | Host-provided transform behavior. |
83
+
84
+ ## 4. Common Choices
85
+
86
+ | Need | Transform / mode |
87
+ |------|------------------|
88
+ | Rename a term | `replace_text/literal` |
89
+ | Renumber headings | `heading_numbers/rebuild` |
90
+ | Promote headings | `heading_levels/promote` |
91
+ | Insert a warning before sections | `insert_before/insert` |
92
+ | Remove empty paragraphs | `remove_empty/remove` |
93
+ | Rewrite links | `links/rewrite_url` |
94
+
95
+ ## 5. Policy-Gated Families
96
+
97
+ `sanitise` and `adapter` are policy-gated. A host must explicitly enable and implement policy behavior before exposing them as ordinary user controls.
98
+
99
+ `order/target_order` is also gated/deferred in capabilities. Public docs should not present it as a normal ready-to-use transform.
@@ -0,0 +1,20 @@
1
+ # Markdown Composer Examples
2
+
3
+ These examples are runnable Ruby scripts for learning the gem from a normal Ruby project.
4
+
5
+ Run them from the gem root:
6
+
7
+ ```bash
8
+ ruby examples/basic_compose.rb
9
+ ruby examples/source_list_dsl.rb
10
+ ruby examples/yaml_plan.rb
11
+ ruby examples/html_input.rb
12
+ ruby examples/standard_composer.rb
13
+ ruby examples/standard_sources_buffer.rb
14
+ ruby examples/advanced_composer.rb
15
+ ruby examples/complex_composer.rb
16
+ ```
17
+
18
+ Each script prints Markdown and writes the same result to `examples/output/*.md`.
19
+
20
+ The examples use small Markdown and HTML fixtures in `examples/fixtures/`. They are user-facing sample inputs, separate from `test/fixtures/`, which are larger test-only fixtures used by the automated suite.