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,136 @@
1
+ ---
2
+ title: Markdown Composer Compose Include
3
+ type: guide
4
+ status: current
5
+ updated: 2026-06-01
6
+ description: Public guide to Include syntax and examples in Markdown Composer compose rows.
7
+ ---
8
+
9
+ # Markdown Composer Compose Include
10
+
11
+ ### References
12
+
13
+ | What | Where |
14
+ |------|-------|
15
+ | Compose anatomy | [./md_composer_compose_anatomy.md](./md_composer_compose_anatomy.md) |
16
+ | Select | [./md_composer_compose_select.md](./md_composer_compose_select.md) |
17
+ | Unit tokens | [../reference/md_composer_unit_tokens.md](../reference/md_composer_unit_tokens.md) |
18
+ | Take modifiers | [../reference/md_composer_take.md](../reference/md_composer_take.md) |
19
+ | Where conditions | [../reference/md_composer_where.md](../reference/md_composer_where.md) |
20
+ | Nested syntax | [../reference/md_composer_nested.md](../reference/md_composer_nested.md) |
21
+ | Structured data example | [../examples/md_composer_example_structured_data.md](../examples/md_composer_example_structured_data.md) |
22
+
23
+ ---
24
+
25
+ ## 1. Purpose
26
+
27
+ `include` chooses what to keep from each selected unit.
28
+
29
+ ```ruby
30
+ {
31
+ "select" => "heading_2_section",
32
+ "include" => "heading_title, paragraph[first:1]"
33
+ }
34
+ ```
35
+
36
+ If omitted, `include` defaults to `all`.
37
+
38
+ ## 2. Syntax Anatomy
39
+
40
+ `include` is an include expression. It runs inside each selected unit.
41
+
42
+ ```text
43
+ include = all | content | include_list
44
+
45
+ include_list = include_item, include_item, ...
46
+
47
+ include_item = include_unit [take] [where condition] [except exclude_unit]
48
+ | data_path("path.expression")
49
+ | nested_include
50
+
51
+ nested_include = unit_token { include_item; include_item; ... }
52
+ ```
53
+
54
+ For every include-capable token and alias, see [Unit Tokens](../reference/md_composer_unit_tokens.md).
55
+ For shared bracketed take syntax inside include items, see [Take Modifiers](../reference/md_composer_take.md).
56
+ For shared `where` syntax inside include items, see [Where Conditions](../reference/md_composer_where.md).
57
+ For nested include projection, recursion, and separators, see [Nested Syntax](../reference/md_composer_nested.md).
58
+
59
+ Include parts:
60
+
61
+ | Part | Example | Meaning |
62
+ |------|---------|---------|
63
+ | `all` | `all` | Keep the whole selected unit. |
64
+ | `content` | `content` | Keep the selected unit body without its heading/title wrapper. |
65
+ | `include_unit` | `paragraph`, `heading_title`, `code_block` | Keep only matching child units. |
66
+ | `[take]` | `paragraph[first:2]` | Keep positions inside each selected unit. |
67
+ | `where condition` | `paragraph where text:contains("Note")` | Filter child units. |
68
+ | `except` | `paragraph except link` | Keep the include unit while excluding nested matches. |
69
+ | `data_path(...)` | `data_path("metadata.title")` | Extract structured YAML/JSON values from `data_block`. |
70
+
71
+ Examples:
72
+
73
+ | Goal | Include |
74
+ |------|---------|
75
+ | Whole selected unit | `all` |
76
+ | Body without section heading | `content` |
77
+ | Only heading title | `heading_title` |
78
+ | First paragraph inside each selected section | `paragraph[first:1]` |
79
+ | Keep paragraphs except links | `paragraph except link` |
80
+ | Extract data value | `data_path("title")` |
81
+
82
+ ## 3. Include Runs Per Selected Unit
83
+
84
+ This row selects two H2 sections and keeps the first paragraph from each section:
85
+
86
+ ```ruby
87
+ {
88
+ "select" => "heading_2_section[position:1,2]",
89
+ "include" => "paragraph[first:1]",
90
+ "action" => "set"
91
+ }
92
+ ```
93
+
94
+ It does not keep the first paragraph across the whole document. It keeps the first paragraph inside each selected section.
95
+
96
+ ## 4. Nested Include
97
+
98
+ Nested include is useful when a selection should keep a specific shape.
99
+
100
+ ```ruby
101
+ {
102
+ "select" => "heading_2_section { heading_title; paragraph[first:1] }",
103
+ "action" => "set"
104
+ }
105
+ ```
106
+
107
+ The nested include after `{ ... }` is part of the selector string and is normalized as include rules for each selected section.
108
+
109
+ ## 5. Data Path Include
110
+
111
+ `data_path(...)` is only valid when the selected unit is `data_block`.
112
+
113
+ ```ruby
114
+ {
115
+ "select" => "data_block where format:equals(\"yaml\")",
116
+ "include" => "data_path(\"metadata.title\")",
117
+ "action" => "set"
118
+ }
119
+ ```
120
+
121
+ `data_path(...)` creates derived nodes:
122
+
123
+ | Derived type | Produced when |
124
+ |--------------|---------------|
125
+ | `data_record` | The extracted value is an object/hash. |
126
+ | `data_value` | The extracted value is a scalar or array value. |
127
+
128
+ These derived node types matter for metadata and future structured-output targeting. In normal Markdown output, they are serialized back into Markdown text.
129
+
130
+ ## 6. Common Mistakes
131
+
132
+ | Mistake | Fix |
133
+ |---------|-----|
134
+ | Using `data_path(...)` outside `data_block`. | Select `data_block` first. |
135
+ | Expecting `include` to choose source sections. | Use `select` for source units; use `include` for parts inside those units. |
136
+ | Forgetting `heading_title` when rebuilding summaries. | Include it explicitly with body parts. |
@@ -0,0 +1,198 @@
1
+ ---
2
+ title: Markdown Composer Compose Select
3
+ type: guide
4
+ status: current
5
+ updated: 2026-06-01
6
+ description: Public guide to Select syntax, unit tokens, take operators, and where conditions.
7
+ ---
8
+
9
+ # Markdown Composer Compose Select
10
+
11
+ ### References
12
+
13
+ | What | Where |
14
+ |------|-------|
15
+ | Compose anatomy | [./md_composer_compose_anatomy.md](./md_composer_compose_anatomy.md) |
16
+ | Include | [./md_composer_compose_include.md](./md_composer_compose_include.md) |
17
+ | Targets | [./md_composer_compose_targets.md](./md_composer_compose_targets.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
+ | Nested syntax | [../reference/md_composer_nested.md](../reference/md_composer_nested.md) |
22
+
23
+ ---
24
+
25
+ ## 1. Purpose
26
+
27
+ `select` chooses source units before `include` decides what to keep from each unit.
28
+
29
+ ```ruby
30
+ { "select" => "heading_2_section[first:1]" }
31
+ ```
32
+
33
+ If omitted, `select` defaults to `all`.
34
+
35
+ ## 2. Syntax Anatomy
36
+
37
+ `select` is a selector expression. It can be plain, filtered, narrowed by take, or paired with a nested include block.
38
+
39
+ Readable shape:
40
+
41
+ ```text
42
+ heading_2_section[position:2,3] where title:contains("API") { heading_title; paragraph[first:1] }
43
+ | | | | | | |
44
+ | | | | | └── nested include rules
45
+ | | | | └──── include block for each selected unit
46
+ | | | └────────────────────────── where condition
47
+ | | └──────────────────────────────── take modifier
48
+ └─────────────────────────────────────────────────────────────── unit token
49
+ ```
50
+
51
+ Read it as:
52
+
53
+ ```text
54
+ Select H2 sections, keep positions 2 and 3 after filtering, require the title to contain "API", then keep only the heading title and first paragraph from each selected section.
55
+ ```
56
+
57
+ Simple selectors can stop at any earlier part:
58
+
59
+ ```text
60
+ paragraph
61
+ | |
62
+ └───────└── unit token only
63
+
64
+ paragraph[first:2]
65
+ | | |
66
+ | └───────└── take modifier
67
+ └────────────────── unit token
68
+
69
+ paragraph where text:contains("warning")
70
+ | | |
71
+ | | └── where condition
72
+ | └──────── where keyword
73
+ └────────────────── unit token
74
+ ```
75
+
76
+ ### 2.1. Formal Grammar
77
+
78
+ ```text
79
+ select = all | selector
80
+
81
+ selector = unit_token [take] [where condition] [nested_include]
82
+
83
+ unit_token = heading_2_section | paragraph | table | code_block | ...
84
+
85
+ take = [first:n] | [last:n] | [position:n,n] | [range:n..n] | [odd] | [even] | [every:n]
86
+
87
+ nested_include = { include_item; include_item; ... }
88
+ ```
89
+
90
+ For every supported unit token and alias, see [Unit Tokens](../reference/md_composer_unit_tokens.md).
91
+ For every take operator, see [Take Modifiers](../reference/md_composer_take.md).
92
+ For every where field, predicate, and group, see [Where Conditions](../reference/md_composer_where.md).
93
+ For nested include projection, see [Nested Syntax](../reference/md_composer_nested.md).
94
+
95
+ The order matters:
96
+
97
+ ```text
98
+ unit token first, take second, where third, nested include last
99
+ ```
100
+
101
+ ### 2.2. Selector Parts
102
+
103
+ | Part | Example | Purpose |
104
+ |------|---------|---------|
105
+ | [`unit_token`](../reference/md_composer_unit_tokens.md) | `heading_2_section` | Chooses the kind of source unit. |
106
+ | [`[take]`](../reference/md_composer_take.md) | `[position:2,3]` | Narrows by position after unit matching. |
107
+ | [`where condition`](../reference/md_composer_where.md) | `where title:contains("API")` | Filters matches by metadata or text. |
108
+ | [`{ nested include }`](../reference/md_composer_nested.md) | `{ heading_title; paragraph[first:1] }` | Keeps a shaped subset from each selected unit. |
109
+
110
+ ### 2.3. Examples
111
+
112
+ | Goal | Select | Anatomy used |
113
+ |------|--------|--------------|
114
+ | All content | `all` | special selector |
115
+ | All paragraphs | `paragraph` | unit token |
116
+ | First H2 section | `heading_2_section[first:1]` | unit token + take |
117
+ | H2 section with title | `heading_2_section where title:equals("API")` | unit token + where |
118
+ | Paragraphs containing text | `paragraph where text:contains("warning")` | unit token + where |
119
+ | H2 sections 2 through 4 | `heading_2_section[range:2..4]` | unit token + take |
120
+ | H2 sections with nested include | `heading_2_section { heading_title; paragraph[first:1] }` | unit token + nested include |
121
+ | H2 sections with take, where, and include | `heading_2_section[position:2,3] where title:contains("API") { heading_title; paragraph[first:1] }` | full selector |
122
+
123
+ ## 3. Section Versus Node Tokens
124
+
125
+ | Token | Selects |
126
+ |-------|---------|
127
+ | `heading_2` / `h2` | Only H2 heading nodes. |
128
+ | `heading_2_section` / `h2_section` | H2 heading plus content until the next H2 or higher heading. |
129
+ | `paragraph` / `p` | Paragraph nodes. |
130
+ | `section` | Heading-defined sections at any level. |
131
+ | `heading` | Heading nodes at any level. |
132
+
133
+ If you want a heading and its body, use a section token.
134
+
135
+ ## 4. Take Operators
136
+
137
+ Take operators live inside brackets.
138
+
139
+ For full syntax anatomy, advanced operators, target/scope take, and field-take, see [Take Modifiers](../reference/md_composer_take.md).
140
+
141
+ | Goal | Select |
142
+ |------|--------|
143
+ | First two matches | `paragraph[first:2]` |
144
+ | Last match | `heading_2_section[last:1]` |
145
+ | Positions | `heading_2_section[position:2,3]` |
146
+ | Range | `heading_2_section[range:2..4]` |
147
+ | Odd positions | `list_item[odd]` |
148
+ | Every third paragraph | `paragraph[every:3]` |
149
+ | Exclude positions | `heading_2_section[except:1,3]` |
150
+
151
+ Positions are 1-based. Negative positions count from the end in structured take values.
152
+
153
+ ## 5. Where Conditions
154
+
155
+ Where conditions are shared selector syntax. They work in `select`, `include`, `target`, and transform `scope`.
156
+
157
+ In `select`, where conditions filter matches after the unit token is selected and before take is applied.
158
+
159
+ ```ruby
160
+ "heading_2_section where title:contains(\"Install\")"
161
+ ```
162
+
163
+ For the full syntax anatomy, field list, predicates, groups, field-take, and diagnostics, see [Where Conditions](../reference/md_composer_where.md).
164
+
165
+ Common fields:
166
+
167
+ | Field | Example |
168
+ |-------|---------|
169
+ | `title` | `heading_2_section where title:equals("API")` |
170
+ | `text` | `paragraph where text:contains("warning")` |
171
+ | `position` | `section where position:range(2..4)` |
172
+ | `language` | `code_block where language:equals("ruby")` |
173
+ | `links` | `section where links:exists` |
174
+ | `empty` | `paragraph where empty:equals(false)` |
175
+
176
+ Groups combine conditions:
177
+
178
+ ```ruby
179
+ "heading_2_section where any(title:contains(\"API\"); title:contains(\"CLI\"))"
180
+ ```
181
+
182
+ ## 6. Data Blocks
183
+
184
+ Markdown Composer can select structured YAML or JSON blocks:
185
+
186
+ ```ruby
187
+ "data_block where format:equals(\"json\")"
188
+ ```
189
+
190
+ Use `include: 'data_path("...")'` to extract values from selected data blocks. `data_record` and `data_value` are produced by `data_path(...)`; they are not normal `select` tokens.
191
+
192
+ ## 7. Common Mistakes
193
+
194
+ | Mistake | Fix |
195
+ |---------|-----|
196
+ | Using `h2` when you want body content. | Use `h2_section`. |
197
+ | Putting `where` before take. | Use `unit[take] where condition`. |
198
+ | Treating `data_value` as a normal selector. | Select `data_block`, then include `data_path(...)`. |
@@ -0,0 +1,161 @@
1
+ ---
2
+ title: Markdown Composer Compose Sources
3
+ type: guide
4
+ status: current
5
+ updated: 2026-06-01
6
+ description: Public guide to Source values in Markdown Composer compose rows.
7
+ ---
8
+
9
+ # Markdown Composer Compose Sources
10
+
11
+ ### References
12
+
13
+ | What | Where |
14
+ |------|-------|
15
+ | Compose anatomy | [./md_composer_compose_anatomy.md](./md_composer_compose_anatomy.md) |
16
+ | Source examples | [../examples/md_composer_example_multi_row_compose.md](../examples/md_composer_example_multi_row_compose.md) |
17
+ | Capabilities | [../reference/md_composer_reference_capabilities.md](../reference/md_composer_reference_capabilities.md) |
18
+
19
+ ---
20
+
21
+ ## 1. Purpose
22
+
23
+ `source` tells a compose row where to read content from.
24
+
25
+ ```ruby
26
+ { "source" => "current", "select" => "heading_2_section[first:1]" }
27
+ ```
28
+
29
+ If omitted, `source` defaults to `current`.
30
+
31
+ ## 2. Syntax Anatomy
32
+
33
+ `source` can be omitted, a source token string, a structured source reference, or an inline source object.
34
+
35
+ ```text
36
+ source = omitted
37
+ | source_token
38
+ | source_reference
39
+ | inline_source
40
+
41
+ source_token = current | explicit | inherited | previous | buffer | inline
42
+
43
+ source_reference = {
44
+ type: source_token,
45
+ key: source_key
46
+ }
47
+
48
+ inline_source = {
49
+ type: inline,
50
+ key: source_key,
51
+ markdown: markdown_text
52
+ }
53
+ ```
54
+
55
+ Common forms:
56
+
57
+ | Form | Example | Meaning |
58
+ |------|---------|---------|
59
+ | omitted | no `source` field | Use `current`. |
60
+ | string token | `"source" => "buffer"` | Use a named source behavior. |
61
+ | structured reference | `"source" => { "type" => "explicit", "key" => "guide" }` | Resolve a keyed source. |
62
+ | inline object | `"source" => { "type" => "inline", "markdown" => "..." }` | Embed small source content in the plan. |
63
+
64
+ ## 3. Source Tokens
65
+
66
+ | Source | Meaning | When to use |
67
+ |--------|---------|-------------|
68
+ | `current` | The main source supplied by the caller. | Most one-document recipes. |
69
+ | `explicit` | A keyed source selected by the plan or caller. | Multi-source composition. |
70
+ | `inherited` | A caller-resolved inherited source. | Applications that provide inheritance. |
71
+ | `previous` | The effective source from the previous row. | Repeating the same source without restating it. |
72
+ | `buffer` | The current composition buffer. | Moving, modifying, removing, or transforming content already composed. |
73
+ | `inline` | Content embedded in the config. | Small generated snippets or tests. |
74
+
75
+ ## 4. Current Source
76
+
77
+ ```ruby
78
+ sources = [
79
+ { "key" => "current", "type" => "current", "markdown" => "# Guide\n\n## Intro\n\nHello.\n" }
80
+ ]
81
+
82
+ config = {
83
+ "compose" => [
84
+ { "source" => "current", "select" => "heading_2_section[first:1]", "action" => "set" }
85
+ ]
86
+ }
87
+ ```
88
+
89
+ If there is exactly one current source, you can omit `source`.
90
+
91
+ ## 5. Explicit Source
92
+
93
+ Use explicit source references when a plan reads from more than one input.
94
+
95
+ ```ruby
96
+ sources = [
97
+ { "key" => "guide", "type" => "explicit", "markdown" => "# Guide\n\n## API\n\nUse it.\n" },
98
+ { "key" => "faq", "type" => "explicit", "markdown" => "# FAQ\n\n## Install\n\nBundle it.\n" }
99
+ ]
100
+
101
+ config = {
102
+ "compose" => [
103
+ { "source" => { "type" => "explicit", "key" => "guide" }, "select" => "heading_2_section", "action" => "set" },
104
+ { "source" => { "type" => "explicit", "key" => "faq" }, "select" => "heading_2_section", "action" => "append" }
105
+ ]
106
+ }
107
+ ```
108
+
109
+ ## 6. Previous Source
110
+
111
+ `previous` uses the effective source reference from the prior row. On the first row, it falls back to `current`.
112
+
113
+ ```ruby
114
+ {
115
+ "compose" => [
116
+ { "source" => "current", "select" => "heading_2_section[first:1]", "action" => "set" },
117
+ { "source" => "previous", "select" => "heading_2_section[last:1]", "action" => "append" }
118
+ ]
119
+ }
120
+ ```
121
+
122
+ ## 7. Buffer Source
123
+
124
+ Use `buffer` when the row should read from composed output rather than the original source.
125
+
126
+ ```ruby
127
+ {
128
+ "source" => "buffer",
129
+ "select" => "heading_2_section where title:contains(\"Draft\")",
130
+ "action" => "remove_buffer_target",
131
+ "target" => "heading_2_section where title:contains(\"Draft\")"
132
+ }
133
+ ```
134
+
135
+ `buffer` is only useful after a previous row has created buffer content, unless the caller passes an initial buffer option.
136
+
137
+ ## 8. Inline Source
138
+
139
+ Inline source embeds content directly in the plan.
140
+
141
+ ```ruby
142
+ {
143
+ "source" => {
144
+ "type" => "inline",
145
+ "key" => "notice",
146
+ "markdown" => "## Notice\n\nGenerated content.\n"
147
+ },
148
+ "select" => "heading_2_section",
149
+ "action" => "append"
150
+ }
151
+ ```
152
+
153
+ Use inline sources for small generated snippets, not large documents.
154
+
155
+ ## 9. Common Mistakes
156
+
157
+ | Mistake | Fix |
158
+ |---------|-----|
159
+ | Using `buffer` in the first row. | Use `current`, or provide `initial_buffer`. |
160
+ | Using `previous` when the prior row used `buffer`. | Be explicit if the distinction matters. |
161
+ | Forgetting a key for multi-source explicit plans. | Pass `key`, `id`, or `slug` in the source reference. |
@@ -0,0 +1,194 @@
1
+ ---
2
+ title: Markdown Composer Compose Targets
3
+ type: guide
4
+ status: current
5
+ updated: 2026-06-01
6
+ description: Public guide to target syntax, placement, selector targets, and structured-output target caveats.
7
+ ---
8
+
9
+ # Markdown Composer Compose Targets
10
+
11
+ ### References
12
+
13
+ | What | Where |
14
+ |------|-------|
15
+ | Compose anatomy | [./md_composer_compose_anatomy.md](./md_composer_compose_anatomy.md) |
16
+ | Actions | [./md_composer_compose_actions.md](./md_composer_compose_actions.md) |
17
+ | Buffer | [./md_composer_compose_buffer.md](./md_composer_compose_buffer.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
+ `target` addresses the current composition buffer. It does not address the original source unless the row explicitly uses `source: "buffer"`.
27
+
28
+ ```ruby
29
+ {
30
+ "action" => "replace",
31
+ "target" => "heading_2_section where title:equals(\"Old\")"
32
+ }
33
+ ```
34
+
35
+ ## 2. Syntax Anatomy
36
+
37
+ `target` has three layers: a special buffer position, a selector that matches the current buffer, or a placement expression built from selectors.
38
+
39
+ ```text
40
+ target = special_target | target_selector | placement_target
41
+
42
+ special_target = whole output | output | start | end | in_place
43
+
44
+ target_selector = unit_token [take] [where condition]
45
+
46
+ placement_target = before target_selector
47
+ | after target_selector
48
+ | between target_selector and target_selector
49
+ ```
50
+
51
+ For every target-capable token and alias, see [Unit Tokens](../reference/md_composer_unit_tokens.md).
52
+ For shared target-selector take syntax, see [Take Modifiers](../reference/md_composer_take.md).
53
+ For shared target-selector `where` syntax, see [Where Conditions](../reference/md_composer_where.md).
54
+
55
+ | Layer | Example | Meaning |
56
+ |-------|---------|---------|
57
+ | `special_target` | `end` | A built-in buffer location or whole-buffer operation. |
58
+ | `target_selector` | `heading_2_section[first:1]` | A selector matched against the current composition buffer. |
59
+ | `placement_target` | `before heading_2 where text:contains("API")` | A placement instruction anchored to one or two buffer selectors. |
60
+
61
+ Target selector anatomy:
62
+
63
+ ```text
64
+ unit_token[take] where condition
65
+ ```
66
+
67
+ | Part | Example | Notes |
68
+ |------|---------|-------|
69
+ | `unit_token` | `heading_2_section`, `paragraph`, `table` | Uses the same token registry as `select` and transform `scope`. |
70
+ | `[take]` | `[first:1]`, `[position:2,3]`, `[range:2..4]` | Optional. Positions are 1-based. |
71
+ | `where condition` | `where title:equals("API")` | Optional. Filters buffer matches. |
72
+
73
+ Common target forms:
74
+
75
+ | Form | Example | Common actions |
76
+ |------|---------|----------------|
77
+ | `whole output` | `target: "whole output"` | `set`, `replace`, `modify` |
78
+ | `start` | `target: "start"` | `copy`, `move`, `modify` |
79
+ | `end` | `target: "end"` | `copy`, `move`, `modify` |
80
+ | selector | `target: "heading_2_section[first:1]"` | `replace`, `remove_buffer_target`, `transform_buffer_target` |
81
+ | before | `target: "before heading_2 where text:contains(\"API\")"` | `insert_before`, `copy`, `move`, `modify` |
82
+ | after | `target: "after heading_2 where text:contains(\"API\")"` | `insert_after`, `copy`, `move`, `modify` |
83
+ | between | `target: "between heading_2[first:1] and heading_2[last:1]"` | `insert_between`, `modify` |
84
+ | `in_place` | `target: "in_place"` | `modify` with `source: "buffer"` only |
85
+
86
+ ## 3. Selector Targets
87
+
88
+ Selector targets reuse select syntax, but they match against the current buffer.
89
+
90
+ ```ruby
91
+ {
92
+ "action" => "replace",
93
+ "target" => "paragraph where text:contains(\"TODO\")"
94
+ }
95
+ ```
96
+
97
+ The target can use take and where:
98
+
99
+ ```ruby
100
+ "heading_2_section[position:2] where title:contains(\"Draft\")"
101
+ ```
102
+
103
+ That means this row replaces the first matching section already in the buffer, not the first matching section in the original source:
104
+
105
+ ```ruby
106
+ {
107
+ "select" => "heading_2_section where title:equals(\"New API\")",
108
+ "action" => "replace",
109
+ "target" => "heading_2_section where title:equals(\"Old API\")"
110
+ }
111
+ ```
112
+
113
+ ## 4. Between Targets
114
+
115
+ Between targets need a start anchor and an end anchor.
116
+
117
+ ```ruby
118
+ {
119
+ "action" => "insert_between",
120
+ "target" => "between heading_2 where text:contains(\"Start\") and heading_2 where text:contains(\"End\")"
121
+ }
122
+ ```
123
+
124
+ The start anchor must resolve before the end anchor. Otherwise validation/execution reports `target.order_invalid`.
125
+
126
+ ## 5. In Place Target
127
+
128
+ `in_place` rewrites selected buffer content inline.
129
+
130
+ ```ruby
131
+ {
132
+ "source" => "buffer",
133
+ "select" => "paragraph where text:contains(\"Draft\")",
134
+ "action" => "modify",
135
+ "target" => "in_place",
136
+ "transforms" => [
137
+ { "transform" => "replace_text", "mode" => "literal", "options" => "from:Draft; to:Final" }
138
+ ]
139
+ }
140
+ ```
141
+
142
+ Rules:
143
+
144
+ | Rule | Meaning |
145
+ |------|---------|
146
+ | Source must be `buffer`. | The row must select from the existing buffer. |
147
+ | Action must be `modify`. | Other actions reject `in_place`. |
148
+ | Selected content must be line-addressable. | Markdown Composer maps transformed fragments back to original buffer line ranges. |
149
+
150
+ ## 6. Target-Supported Unit Tokens
151
+
152
+ Capabilities mark some unit tokens as valid for target matching. The common practical target tokens are the same node/section tokens used by selection:
153
+
154
+ | Token family | Example target |
155
+ |--------------|----------------|
156
+ | Sections | `heading_2_section where title:equals("API")` |
157
+ | Headings | `heading_2 where text:contains("API")` |
158
+ | Paragraphs | `paragraph where text:contains("TODO")` |
159
+ | Lists | `list`, `list_item`, `ordered_list`, `unordered_list` |
160
+ | Tables | `table`, `table_row`, `table_cell` |
161
+ | Code and diagrams | `code_block`, `mermaid`, `math_block` |
162
+ | Data blocks | `data_block where format:equals("yaml")` |
163
+
164
+ `data_record` and `data_value` are different. Capabilities mark them as advanced targets and output-only scopes because they are derived structured-output node types produced by `data_path(...)`. In the standalone Markdown buffer, `data_path(...)` values are serialized back to Markdown text, and normal target matching reparses that Markdown. Treat `data_record` and `data_value` as metadata/deferred structured-output target tokens unless a host adapter preserves them as matchable buffer nodes.
165
+
166
+ Practical structured-data recipe:
167
+
168
+ ```ruby
169
+ {
170
+ "select" => "data_block where format:equals(\"yaml\")",
171
+ "include" => "data_path(\"fields[type=dropdown].label\")",
172
+ "action" => "set"
173
+ }
174
+ ```
175
+
176
+ Then target the serialized Markdown text if needed:
177
+
178
+ ```ruby
179
+ {
180
+ "source" => "buffer",
181
+ "select" => "all",
182
+ "action" => "remove_buffer_target",
183
+ "target" => "paragraph where text:contains(\"Old label\")"
184
+ }
185
+ ```
186
+
187
+ ## 7. Common Mistakes
188
+
189
+ | Mistake | Fix |
190
+ |---------|-----|
191
+ | Expecting target to match the original source. | Target matches the current buffer. |
192
+ | Using `data_record` directly as a normal target in standalone Markdown output. | Select `data_block`, include `data_path(...)`, then target the serialized output or use a host adapter that preserves structured nodes. |
193
+ | Using `start` or `end` with `remove_buffer_target`. | Use a selector target. |
194
+ | Using `in_place` without `source: "buffer"`. | Select from the buffer explicitly. |