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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1efddd31cae13aa57937ff72966d3d45553318b7f6e723153b09f818dc7fb554
4
+ data.tar.gz: 67639e63dc038422d7226bf54b3535aad398049c3bd4f08076af7383fbf192b4
5
+ SHA512:
6
+ metadata.gz: daaa025bb16b154e341938a4b357b85ab9dc4e23eba85a22d6f114996a3add3ee0bd855b307b9654a85b37df9e9b9609f17bd104ba56dceb4cf3fa25bc32f1c4
7
+ data.tar.gz: c37dc0643e032a97bd04d7b468941be08bc8d88ae0c348b45e0023f92406ce433a36a6dc7e03cc172fe6f3e27fad970e93862f40657e398a85efc56c0c709d0a
data/CHANGELOG.md ADDED
@@ -0,0 +1,23 @@
1
+ # Changelog
2
+
3
+ ## 0.7.0 - 2026-05-14
4
+
5
+ - Moved test fixtures into the gem so the public test suite is self-contained.
6
+ - Added `bundle exec rake test` as the standard test entry point.
7
+ - Added GitHub Actions CI for Ruby 3.1, 3.2, 3.3, and 3.4.
8
+ - Updated public gem metadata for the standalone `SocIt2Em/markdown_composer` repository.
9
+ - Raised declared Ruby support to `>= 3.1` so the public support claim matches the CI matrix.
10
+ - Documented the validation boundary between the core gem and host frameworks such as Rails.
11
+ - Documented regex policy expectations for hosts exposing Composer configs to untrusted users.
12
+ - Added focused edge-case tests that raise gem library line coverage above 95%.
13
+
14
+ ## 0.6.5.1 - 2026-05-14
15
+
16
+ - Added the standalone `markdown_composer` gem package.
17
+ - Added public APIs for composition, normalization, validation, capabilities, YAML/JSON import/export, row import/export, and the Ruby plan DSL.
18
+ - Added Markdown-first source indexing, selection, include, take, where, action, target, transform, data-path, and diagnostics support.
19
+ - Added HTML output support and best-effort HTML input conversion.
20
+ - Enabled regex matching, regex text replacement, deterministic random take, Mermaid/math/comment tokens, and HTML link attribute transforms.
21
+ - Kept raw HTML, sanitise transforms, host adapter transforms, and target-order behavior behind host policy or roadmap boundaries.
22
+ - Added fixture-backed UAT, release-readiness coverage, and coupling tests for app independence.
23
+ - Kept public capability metadata host-generic so GUI builders do not depend on application-specific document models.
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SocIt2Em
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,278 @@
1
+ # Markdown Composer
2
+
3
+ Markdown Composer is a headless Ruby gem for selecting, composing, validating, and transforming Markdown-first document fragments.
4
+
5
+ It is framework-independent. It does not depend on Rails, an ORM, an authorization system, a template engine, a component framework, or a UI. Host applications pass plain Markdown/HTML sources into the gem and own source lookup, permissions, sanitization, asset handling, and presentation.
6
+
7
+ ## Requirements
8
+
9
+ - Ruby `>= 3.1`
10
+ - `commonmarker >= 0.23.10, < 3`
11
+ - `nokogiri >= 1.13.10, < 2`
12
+
13
+ Development dependencies are limited to test and benchmark tooling.
14
+
15
+ ## Installation
16
+
17
+ From RubyGems:
18
+
19
+ ```ruby
20
+ # Gemfile
21
+ gem "markdown_composer"
22
+ ```
23
+
24
+ Then run:
25
+
26
+ ```bash
27
+ bundle install
28
+ ```
29
+
30
+ For a standalone Ruby script:
31
+
32
+ ```bash
33
+ gem install markdown_composer
34
+ ```
35
+
36
+ From a Git repository before a RubyGems release:
37
+
38
+ ```ruby
39
+ # Gemfile
40
+ gem "markdown_composer", git: "https://github.com/SocIt2Em/markdown_composer.git"
41
+ ```
42
+
43
+ If installing from source before a RubyGems release, point Bundler at the standalone repository:
44
+
45
+ ```ruby
46
+ # Gemfile
47
+ gem "markdown_composer", git: "https://github.com/SocIt2Em/markdown_composer.git"
48
+ ```
49
+
50
+ ## Quick Start
51
+
52
+ ```ruby
53
+ require "markdown_composer"
54
+
55
+ sources = MarkdownComposer.source_list do
56
+ current File.read("source_current.md")
57
+ explicit :guide, File.read("guide.md")
58
+ end
59
+
60
+ plan = MarkdownComposer.plan do
61
+ from :current
62
+ select "h2_section[first:1]"
63
+ include "all"
64
+ set
65
+
66
+ from({ type: "explicit", key: "guide" })
67
+ select "h2_section"
68
+ include "all"
69
+ append
70
+ end
71
+
72
+ result = MarkdownComposer.compose(sources: sources, plan: plan)
73
+
74
+ puts result.markdown
75
+ ```
76
+
77
+ `MarkdownComposer.compose` always receives source content through the `sources:` keyword. Plan-row `inline` sources are the exception: they embed small source content directly in the plan.
78
+
79
+ ## Sources
80
+
81
+ Runtime sources are plain hashes or values produced by `MarkdownComposer.source_list`.
82
+
83
+ ```ruby
84
+ sources = MarkdownComposer.source_list do
85
+ current current_markdown
86
+ explicit :guide, guide_markdown
87
+ explicit :faq, faq_markdown
88
+ inherited :site_intro, inherited_markdown
89
+ end
90
+ ```
91
+
92
+ This returns the same shape as:
93
+
94
+ ```ruby
95
+ [
96
+ { "key" => "current", "type" => "current", "markdown" => current_markdown },
97
+ { "key" => "guide", "type" => "explicit", "markdown" => guide_markdown },
98
+ { "key" => "faq", "type" => "explicit", "markdown" => faq_markdown },
99
+ { "key" => "site_intro", "type" => "inherited", "markdown" => inherited_markdown }
100
+ ]
101
+ ```
102
+
103
+ Supported runtime source types:
104
+
105
+ | Type | Purpose |
106
+ | --- | --- |
107
+ | `current` | Main document supplied by the caller. |
108
+ | `explicit` | Named source selected by key. |
109
+ | `inherited` | Named source resolved by the host application. |
110
+
111
+ Plan-only source references such as `previous`, `buffer`, and `inline` are not runtime source records.
112
+
113
+ ## Plans
114
+
115
+ Plans can be authored with the Ruby DSL:
116
+
117
+ ```ruby
118
+ plan = MarkdownComposer.plan do
119
+ output :markdown
120
+
121
+ from :current
122
+ select 'heading_2_section where none(title:equals("Table of Contents"))'
123
+ include "heading_title"
124
+ set
125
+
126
+ from :buffer
127
+ select 'heading_2 where text:starts_with("1.1")'
128
+ remove_buffer_target
129
+ end
130
+ ```
131
+
132
+ Structured config is also supported for storage, import, and export:
133
+
134
+ ```ruby
135
+ config = {
136
+ "output" => "markdown",
137
+ "compose" => [
138
+ {
139
+ "source" => "current",
140
+ "select" => "h2_section[first:1]",
141
+ "include" => "all",
142
+ "action" => "set"
143
+ }
144
+ ]
145
+ }
146
+
147
+ result = MarkdownComposer.compose(sources: sources, config: config)
148
+ ```
149
+
150
+ YAML and JSON helpers are available:
151
+
152
+ ```ruby
153
+ plan = MarkdownComposer.parse_yaml(yaml)
154
+ yaml = MarkdownComposer.to_yaml(plan)
155
+
156
+ plan = MarkdownComposer.parse_json(json)
157
+ json = MarkdownComposer.to_json(plan)
158
+ ```
159
+
160
+ Row-style hashes are accepted for spreadsheet or GUI workflows:
161
+
162
+ ```ruby
163
+ plan = MarkdownComposer.parse_rows([
164
+ { "Source" => "current", "Select" => "h2_section[first:1]", "Include" => "all", "Action" => "set" }
165
+ ])
166
+ ```
167
+
168
+ ## Public API
169
+
170
+ Main entry points:
171
+
172
+ ```ruby
173
+ MarkdownComposer.compose(sources:, config: nil, plan: nil, options: {})
174
+ MarkdownComposer.validate(config:, sources: [], options: {})
175
+ MarkdownComposer.normalise(config_or_rows)
176
+ MarkdownComposer.normalize(config_or_rows)
177
+ MarkdownComposer.capabilities(options: {})
178
+ MarkdownComposer.plan(&block)
179
+ MarkdownComposer.source_list(&block)
180
+ MarkdownComposer.parse_yaml(yaml_string)
181
+ MarkdownComposer.parse_json(json_string)
182
+ MarkdownComposer.parse_rows(row_hashes)
183
+ MarkdownComposer.to_yaml(plan)
184
+ MarkdownComposer.to_json(plan)
185
+ MarkdownComposer.to_rows(plan)
186
+ ```
187
+
188
+ `validate` returns `valid`, the normalized plan, diagnostics, and errors. `capabilities` returns JSON-compatible metadata for building host UI controls.
189
+
190
+ ## Actions And Transforms
191
+
192
+ Compose actions place selected content into the composition buffer:
193
+
194
+ - `set`, `append`, `prepend`
195
+ - `insert_before`, `insert_after`, `insert_between`
196
+ - `replace`, `copy`, `move`
197
+ - `modify`
198
+ - `remove_buffer_target`, `transform_buffer_target`
199
+
200
+ Transforms run after composition or inside `modify`/`transform_buffer_target` rows. Supported transform families include heading numbering, heading levels, text replacement, link handling, content insertion/removal, dedupe, ordering, sanitise, and adapter-owned transforms.
201
+
202
+ Use `MarkdownComposer.capabilities` as the source of truth for enabled actions, tokens, transform modes, required options, aliases, support status, and policy gates.
203
+
204
+ ## Framework Integration
205
+
206
+ Markdown Composer does not ship Rails, ViewComponent, React, Vue, or other framework-specific integrations.
207
+
208
+ Host applications are responsible for:
209
+
210
+ - resolving documents into plain Markdown/HTML source hashes;
211
+ - enforcing authorization, tenant, visibility, and workflow rules;
212
+ - preparing source Markdown when templates or variables are involved;
213
+ - sanitizing output and resolving assets;
214
+ - rendering previews and mapping diagnostics into host UI.
215
+
216
+ Rails, Hotwire, React, Vue, CLIs, and scripts should wrap the same headless API.
217
+
218
+ ## Validation And Diagnostics
219
+
220
+ Validation covers portable Composer configuration: source references, selectors, include rules, targets, actions, transforms, output formats, diagnostics, and policy-gated features.
221
+
222
+ Example diagnostic:
223
+
224
+ ```ruby
225
+ {
226
+ code: "transform.option_missing",
227
+ message: "Missing required option from",
228
+ path: "transform[0].options.from",
229
+ severity: :error,
230
+ details: nil
231
+ }
232
+ ```
233
+
234
+ Current diagnostic families include:
235
+
236
+ - `action.*`
237
+ - `json.*`
238
+ - `output.*`
239
+ - `source.*`
240
+ - `take.*`
241
+ - `target.*`
242
+ - `token.*`
243
+ - `transform.*`
244
+ - `where.*`
245
+ - `yaml.*`
246
+
247
+ ## Format Support
248
+
249
+ Markdown is the source-fidelity format. HTML input is accepted through a best-effort HTML-to-Markdown path and may lose fidelity for structures that do not map cleanly to Markdown.
250
+
251
+ | From | To | Status | Notes |
252
+ | --- | --- | --- | --- |
253
+ | Markdown | Markdown | Supported | Source-fidelity selection and transforms. |
254
+ | Markdown | HTML | Supported | Rendered via CommonMarker when available, fallback renderer otherwise. |
255
+ | HTML | Markdown | Best effort | Uses Nokogiri-backed conversion. |
256
+ | HTML | HTML | Best effort | Converts through Markdown, then renders back to HTML. |
257
+
258
+ Host-specific sanitization, custom attributes, and asset policies should live outside the gem.
259
+
260
+ ## Development
261
+
262
+ Run the test suite:
263
+
264
+ ```bash
265
+ bundle exec rake test
266
+ ```
267
+
268
+ Build the gem package:
269
+
270
+ ```bash
271
+ gem build markdown_composer.gemspec
272
+ ```
273
+
274
+ The release test suite covers public helper APIs, diagnostics, transform capability coverage, Markdown/HTML conversion fixtures, syntax checks, and coupling guards.
275
+
276
+ ## License
277
+
278
+ Markdown Composer is released under the MIT License. See `LICENSE.txt`.
data/ROADMAP.md ADDED
@@ -0,0 +1,80 @@
1
+ # Roadmap
2
+
3
+ This roadmap tracks planned improvements for Markdown Composer after the `0.7.0` standalone gem release. It is intentionally focused on the gem's portable contract: syntax, normalization, validation, diagnostics, composition, and framework-independent helper APIs.
4
+
5
+ ## 1. Canonical Field Syntax Printer
6
+
7
+ **Status:** Planned
8
+
9
+ Add a helper that converts normalized selector/config hashes back into canonical field syntax strings.
10
+
11
+ Example target API:
12
+
13
+ ```ruby
14
+ MarkdownComposer.to_field_syntax(selector_hash)
15
+ # => 'heading_2_section where none(title:equals("Table of Contents"))'
16
+ ```
17
+
18
+ This belongs in the gem because the gem owns the field syntax contract.
19
+
20
+ Expected behavior:
21
+
22
+ - Produce deterministic, valid field syntax from normalized hashes.
23
+ - Prefer canonical formatting over preserving original spacing or quote style.
24
+ - Support imports, YAML-to-field workflows, examples, migrations, and normalized presets.
25
+ - Include focused tests for selectors, `where` clauses, predicates, grouping, and invalid input diagnostics.
26
+
27
+ Non-goal:
28
+
29
+ - Restoring the exact user-entered text after normalization. Typos, spacing, quote choices, aliases, and partially invalid syntax cannot be reconstructed from a normalized hash.
30
+
31
+ ## 2. Extension Registry API
32
+
33
+ **Status:** Planned
34
+
35
+ Add a supported extension API for adding Composer vocabulary and behavior without changing the core row syntax.
36
+
37
+ Target extension areas:
38
+
39
+ - Unit tokens, such as custom blocks, callouts, figures, or inline elements.
40
+ - `where` fields, predicates, and groups.
41
+ - Take operators, such as custom windowing or proximity rules.
42
+ - Sources, targets, transforms, and field interpolation providers.
43
+
44
+ Expected behavior:
45
+
46
+ - Extensions register metadata for validation, diagnostics, and GUI capability discovery.
47
+ - Runtime behavior is registered explicitly through handlers or callables.
48
+ - Unknown or disabled extension tokens fail validation instead of being accepted silently.
49
+ - Host-specific behavior stays in host apps or separate extension gems, not in the core gem.
50
+
51
+ ## 3. Authored Field Preservation Guidance
52
+
53
+ **Status:** Planned
54
+
55
+ Document and support the expected editing workflow for host applications:
56
+
57
+ - Preserve authored field strings when exact edit-screen fidelity matters.
58
+ - Normalize field strings, YAML, JSON, and row input for validation and execution.
59
+ - Use future canonical pretty-printer helpers when generated syntax is acceptable.
60
+
61
+ For Rails and other host frameworks, this means the application should keep the user's authored field string while editing, then pass normalized data to the gem for validation and composition.
62
+
63
+ Expected outcome:
64
+
65
+ - Clear README examples showing authored string preservation alongside normalized configs.
66
+ - Validation guidance explaining what the gem can verify and what the host framework must verify.
67
+ - Tests proving canonical output is stable without implying exact round-trip text restoration.
68
+
69
+ ## 4. Maintenance And Bug Fixes
70
+
71
+ **Status:** Ongoing
72
+
73
+ Maintain the gem through focused bug fixes, compatibility updates, and regression tests.
74
+
75
+ Priorities:
76
+
77
+ - Fix parser, normalization, validation, diagnostics, composition, and transform bugs.
78
+ - Add regression tests for confirmed bugs.
79
+ - Keep supported Ruby versions and runtime dependencies current.
80
+ - Avoid breaking stored configs or public APIs without a planned migration note.
@@ -0,0 +1,50 @@
1
+ ---
2
+ title: Markdown Composer Architecture
3
+ type: architecture
4
+ status: current
5
+ updated: 2026-06-01
6
+ description: Public architecture overview for Markdown Composer internals and host boundaries.
7
+ ---
8
+
9
+ # Markdown Composer Architecture
10
+
11
+ ### References
12
+
13
+ | What | Where |
14
+ |------|-------|
15
+ | Concepts | [./_md_composer_concepts.md](./_md_composer_concepts.md) |
16
+ | API reference | [./reference/md_composer_reference_api.md](./reference/md_composer_reference_api.md) |
17
+ | Registries reference | [./reference/md_composer_reference_registries.md](./reference/md_composer_reference_registries.md) |
18
+
19
+ ---
20
+
21
+ ## 1. Execution Pipeline
22
+
23
+ Markdown Composer runs in five broad stages:
24
+
25
+ ```text
26
+ Parse config -> Validate -> Index sources -> Execute compose rows -> Run transforms
27
+ ```
28
+
29
+ The result contains Markdown, optional HTML, diagnostics, errors, and optional stage snapshots.
30
+
31
+ ## 2. Source Indexing
32
+
33
+ Each source is converted into a document index. The index exposes nodes and sections so selectors can match headings, sections, paragraphs, lists, tables, code blocks, data blocks, and other units.
34
+
35
+ HTML input is converted to Markdown as a best-effort/lossy source format. Markdown input is the source-fidelity format.
36
+
37
+ ## 3. Composition Buffer
38
+
39
+ Compose actions write to a `CompositionBuffer`. The buffer is Markdown text plus origin metadata. Target matching runs against a fresh index of the current buffer.
40
+
41
+ This detail matters: derived `data_record` and `data_value` nodes can be produced from `data_path(...)`, but ordinary target matching reparses the serialized Markdown buffer. Treat those tokens as structured-output metadata unless a host adapter materializes them as matchable buffer nodes.
42
+
43
+ ## 4. Transforms
44
+
45
+ Transforms run through `TransformRunner` against the buffer. Top-level transforms come from `config["transform"]`. Row-level transforms are used by `modify` and `transform_buffer_target`.
46
+
47
+ ## 5. Host Boundary
48
+
49
+ The gem owns parsing, validation, indexing, composition, transforms, and capabilities metadata. The host owns source lookup, UI, persistence, authorization, preview shell, and any policy choices for adapter/policy-gated features.
50
+
@@ -0,0 +1,72 @@
1
+ ---
2
+ title: Markdown Composer Cheatsheet
3
+ type: cheatsheet
4
+ status: current
5
+ updated: 2026-06-01
6
+ description: Public quick-reference for Markdown Composer syntax and docs navigation.
7
+ ---
8
+
9
+ # Markdown Composer Cheatsheet
10
+
11
+ ### References
12
+
13
+ | What | Where |
14
+ |------|-------|
15
+ | Compose anatomy | [./compose/md_composer_compose_anatomy.md](./compose/md_composer_compose_anatomy.md) |
16
+ | Transform anatomy | [./transform/md_composer_transform_anatomy.md](./transform/md_composer_transform_anatomy.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
+ | Examples | [./examples/md_composer_examples_readme.md](./examples/md_composer_examples_readme.md) |
21
+
22
+ ---
23
+
24
+ ## 1. Compose Row
25
+
26
+ ```text
27
+ Source -> Select -> Include -> Action -> Target
28
+ ```
29
+
30
+ | Field | Common values |
31
+ |-------|---------------|
32
+ | Source | `current`, `explicit`, `previous`, `buffer`, `inline` |
33
+ | Select | `all`, `heading_2_section[first:1]`, `paragraph where text:contains("x")` |
34
+ | Include | `all`, `content`, `heading_title`, `paragraph[first:2]`, `data_path("title")` |
35
+ | Action | `set`, `append`, `prepend`, `replace`, `modify`, `remove_buffer_target`, `transform_buffer_target` |
36
+ | Target | blank, `whole output`, `start`, `end`, selector, `before ...`, `after ...`, `between ... and ...`, `in_place` |
37
+
38
+ ## 2. Transform Row
39
+
40
+ ```text
41
+ Scope -> Transform -> Mode -> Options
42
+ ```
43
+
44
+ | Field | Common values |
45
+ |-------|---------------|
46
+ | Scope | `output`, `heading_2_section`, `paragraph where text:contains("x")` |
47
+ | Transform | `replace_text`, `heading_numbers`, `heading_levels`, `links`, `remove_empty` |
48
+ | Mode | transform-specific, such as `literal`, `rebuild`, `promote`, `rewrite_url` |
49
+ | Options | hash or string, such as `{ "from" => "Draft", "to" => "Final" }` or `from:"Draft"; to:"Final"` |
50
+
51
+ ## 3. Select Syntax
52
+
53
+ | Need | Syntax |
54
+ |------|--------|
55
+ | First H2 section | `heading_2_section[first:1]` |
56
+ | H2 section by title | `heading_2_section where title:equals("API")` |
57
+ | Paragraphs containing text | `paragraph where text:contains("warning")` |
58
+ | Positions 2 and 3 | `heading_2_section[position:2,3]` |
59
+ | Range | `heading_2_section[range:2..4]` |
60
+ | Nested include | `heading_2_section { heading_title; paragraph[first:1] }` |
61
+
62
+ ## 4. High-Value Docs
63
+
64
+ | Need | Read |
65
+ |------|------|
66
+ | How compose rows work | [Compose anatomy](./compose/md_composer_compose_anatomy.md) |
67
+ | Which unit tokens exist | [Unit tokens](./reference/md_composer_unit_tokens.md) |
68
+ | How `[first:1]`, `[position:2]`, and ranges work | [Take modifiers](./reference/md_composer_take.md) |
69
+ | How `where title:contains(...)` works | [Where conditions](./reference/md_composer_where.md) |
70
+ | How `modify` works | [Actions](./compose/md_composer_compose_actions.md) and [modify example](./examples/md_composer_example_modify.md) |
71
+ | How targets work | [Targets](./compose/md_composer_compose_targets.md) |
72
+ | How transform options work | [Options](./transform/md_composer_transform_options.md) |
@@ -0,0 +1,64 @@
1
+ ---
2
+ title: Markdown Composer Concepts
3
+ type: guide
4
+ status: current
5
+ updated: 2026-06-01
6
+ description: Human-facing concepts for Markdown Composer plans, rows, buffers, selectors, and transforms.
7
+ ---
8
+
9
+ # Markdown Composer Concepts
10
+
11
+ ### References
12
+
13
+ | What | Where |
14
+ |------|-------|
15
+ | Compose anatomy | [./compose/md_composer_compose_anatomy.md](./compose/md_composer_compose_anatomy.md) |
16
+ | Buffer guide | [./compose/md_composer_compose_buffer.md](./compose/md_composer_compose_buffer.md) |
17
+ | Transform anatomy | [./transform/md_composer_transform_anatomy.md](./transform/md_composer_transform_anatomy.md) |
18
+
19
+ ---
20
+
21
+ ## 1. Plans
22
+
23
+ A plan is the normalized instruction set Markdown Composer executes. Public configs usually use:
24
+
25
+ ```ruby
26
+ {
27
+ "compose" => [ ... ],
28
+ "transform" => [ ... ],
29
+ "output" => "markdown"
30
+ }
31
+ ```
32
+
33
+ `compose` builds the buffer. `transform` reshapes the buffer. `output` is `markdown` or `html`.
34
+
35
+ ## 2. Compose Rows
36
+
37
+ Compose rows answer: what content should be selected, what part of it should be kept, and where should it go?
38
+
39
+ ```text
40
+ Source -> Select -> Include -> Action -> Target
41
+ ```
42
+
43
+ The most common row is: select a section from the current source, include all of it, and `set` the buffer to that content.
44
+
45
+ ## 3. The Buffer
46
+
47
+ The buffer is the working Markdown document being built. Early rows create it. Later rows append, prepend, replace, move, remove, or transform content already in it.
48
+
49
+ Use `source: "buffer"` when a row should select from the current buffer instead of from the original source.
50
+
51
+ ## 4. Transform Rows
52
+
53
+ Transform rows answer: what part of the composed buffer should change, what transform should run, which mode should it use, and what options does it need?
54
+
55
+ ```text
56
+ Scope -> Transform -> Mode -> Options
57
+ ```
58
+
59
+ Use top-level transforms for whole-output cleanup. Use row-level transforms with `modify` or `transform_buffer_target` when the change belongs to a specific compose action.
60
+
61
+ ## 5. Diagnostics
62
+
63
+ Markdown Composer prefers diagnostics over silent guessing. A config can be syntactically valid but still produce warnings, such as empty selections or empty destructive targets.
64
+
@@ -0,0 +1,55 @@
1
+ ---
2
+ title: Markdown Composer Dev Guide
3
+ type: dev_guide
4
+ status: current
5
+ updated: 2026-06-01
6
+ description: Public developer guide for testing, validating, and maintaining Markdown Composer docs and examples.
7
+ ---
8
+
9
+ # Markdown Composer Dev Guide
10
+
11
+ ### References
12
+
13
+ | What | Where |
14
+ |------|-------|
15
+ | API reference | [./reference/md_composer_reference_api.md](./reference/md_composer_reference_api.md) |
16
+ | Capabilities reference | [./reference/md_composer_reference_capabilities.md](./reference/md_composer_reference_capabilities.md) |
17
+ | AI source map | [./ai/md_composer_ai_source_map.md](./ai/md_composer_ai_source_map.md) |
18
+
19
+ ---
20
+
21
+ ## 1. Source Of Truth
22
+
23
+ For runtime behavior, trust gem code and tests first. For prose, this public `docs/` tree is the reader-facing set. The `docs/ai/` files map back to the audited canonical source material used to create these docs.
24
+
25
+ ## 2. Validate Examples
26
+
27
+ Use focused Ruby probes for every runnable snippet:
28
+
29
+ ```bash
30
+ bundle exec ruby -Ilib -rmarkdown_composer -e 'puts MarkdownComposer::VERSION'
31
+ ```
32
+
33
+ For broad safety:
34
+
35
+ ```bash
36
+ bundle exec rake test
37
+ ```
38
+
39
+ ## 3. Writing Rules
40
+
41
+ Every public guide should include:
42
+
43
+ | Requirement | Reason |
44
+ |-------------|--------|
45
+ | Syntax anatomy | Readers need to see the shape before memorizing values. |
46
+ | Valid forms | Avoids guesswork between strings, hashes, YAML, and Ruby. |
47
+ | Runnable example | Keeps docs grounded. |
48
+ | Expected output | Makes behavior inspectable. |
49
+ | Common mistakes | Turns diagnostics into learning. |
50
+ | Related docs | Keeps pages focused without hiding depth. |
51
+
52
+ ## 4. Public Boundary
53
+
54
+ Keep core gem docs framework-independent. Hosts can provide source pickers, persistence, policy gates, and preview UI, but Markdown Composer itself remains headless.
55
+