mdka 2.2.0__tar.gz → 2.2.2__tar.gz

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 (156) hide show
  1. mdka-2.2.2/.github/CONTRIBUTING.md +42 -0
  2. {mdka-2.2.0 → mdka-2.2.2}/.github/workflows/ci.yaml +1 -1
  3. mdka-2.2.2/.github/workflows/crates-package-gate.yaml +78 -0
  4. {mdka-2.2.0 → mdka-2.2.2}/.github/workflows/create-release.yaml +17 -2
  5. mdka-2.2.2/.github/workflows/docs-example-gate.yaml +94 -0
  6. mdka-2.2.2/.github/workflows/npm-install-gate.yaml +68 -0
  7. mdka-2.2.2/.github/workflows/pypi-wheel-gate.yaml +112 -0
  8. {mdka-2.2.0 → mdka-2.2.2}/.github/workflows/release-npm.yaml +26 -2
  9. mdka-2.2.2/.github/workflows/scripts/check-docs-examples.py +307 -0
  10. {mdka-2.2.0 → mdka-2.2.2}/.gitignore +2 -0
  11. {mdka-2.2.0 → mdka-2.2.2}/CHANGELOG.md +85 -0
  12. {mdka-2.2.0 → mdka-2.2.2}/Cargo.lock +7 -46
  13. {mdka-2.2.0 → mdka-2.2.2}/Cargo.toml +2 -5
  14. {mdka-2.2.0 → mdka-2.2.2}/PKG-INFO +2 -2
  15. {mdka-2.2.0/python → mdka-2.2.2}/README.md +1 -1
  16. mdka-2.2.2/ROADMAP.md +602 -0
  17. {mdka-2.2.0 → mdka-2.2.2}/benches/memory.rs +7 -0
  18. {mdka-2.2.0 → mdka-2.2.2}/docs/src/api/core.md +9 -9
  19. {mdka-2.2.0 → mdka-2.2.2}/docs/src/api/elements.md +32 -1
  20. {mdka-2.2.0 → mdka-2.2.2}/docs/src/api/errors.md +2 -2
  21. {mdka-2.2.0 → mdka-2.2.2}/docs/src/api/modes.md +5 -5
  22. {mdka-2.2.0 → mdka-2.2.2}/docs/src/api/options.md +3 -3
  23. {mdka-2.2.0 → mdka-2.2.2}/docs/src/design/architecture.md +1 -1
  24. {mdka-2.2.0 → mdka-2.2.2}/docs/src/design/performance-characteristics.md +1 -1
  25. {mdka-2.2.0 → mdka-2.2.2}/docs/src/getting-started/installation.md +2 -2
  26. mdka-2.2.2/docs/src/getting-started/usage-cli.md +64 -0
  27. mdka-2.2.2/docs/src/getting-started/usage-nodejs.md +143 -0
  28. {mdka-2.2.0 → mdka-2.2.2}/docs/src/getting-started/usage-python.md +23 -3
  29. {mdka-2.2.0 → mdka-2.2.2}/examples/measure_mem.rs +9 -3
  30. {mdka-2.2.0 → mdka-2.2.2}/examples/quick_mem.rs +6 -0
  31. {mdka-2.2.0 → mdka-2.2.2}/mdka/__init__.py +1 -1
  32. {mdka-2.2.0 → mdka-2.2.2}/pyproject.toml +1 -1
  33. {mdka-2.2.0 → mdka-2.2.2}/python/Cargo.toml +1 -1
  34. {mdka-2.2.0 → mdka-2.2.2/python}/README.md +1 -1
  35. {mdka-2.2.0 → mdka-2.2.2}/python/src/lib.rs +21 -20
  36. {mdka-2.2.0 → mdka-2.2.2}/rfcs/README.md +23 -2
  37. mdka-2.2.2/rfcs/accepted/024-inline-composition-output-sink.md +146 -0
  38. mdka-2.2.2/rfcs/accepted/025-output-validity-harness.md +168 -0
  39. mdka-2.2.2/rfcs/accepted/028-emphasis-around-block-content.md +169 -0
  40. {mdka-2.2.0 → mdka-2.2.2}/rfcs/done/000-rfc-lifecycle-policy.md +65 -0
  41. mdka-2.2.2/rfcs/done/007-english-only-public-surface.md +134 -0
  42. {mdka-2.2.0 → mdka-2.2.2}/rfcs/done/015-release-tooling-completion.md +1 -1
  43. mdka-2.2.2/rfcs/done/020-npm-distribution-repair.md +262 -0
  44. mdka-2.2.2/rfcs/done/021-bulk-output-collision-safety.md +116 -0
  45. mdka-2.2.2/rfcs/done/022-cli-allocator-and-jemalloc.md +151 -0
  46. mdka-2.2.2/rfcs/done/023-getting-started-doc-reconciliation.md +105 -0
  47. mdka-2.2.2/rfcs/done/026-consumer-artifact-gates.md +167 -0
  48. mdka-2.2.2/rfcs/done/027-verification-discipline.md +260 -0
  49. {mdka-2.2.0 → mdka-2.2.2}/rfcs/handoffs/005-conversion-options-semantics/implementation-handoff.md +1 -1
  50. {mdka-2.2.0 → mdka-2.2.2}/rfcs/handoffs/005-conversion-options-semantics/slice-b1-placement-correction-handoff.md +1 -1
  51. {mdka-2.2.0 → mdka-2.2.2}/rfcs/handoffs/005-conversion-options-semantics/slices-bc-handoff.md +1 -1
  52. {mdka-2.2.0 → mdka-2.2.2}/rfcs/handoffs/006-option-docs-and-binding-parity/implementation-handoff.md +2 -2
  53. mdka-2.2.2/rfcs/handoffs/007-english-only-public-surface/implementation-handoff.md +164 -0
  54. {mdka-2.2.0 → mdka-2.2.2}/rfcs/handoffs/018-readme-prebuilt-binaries/implementation-handoff.md +1 -1
  55. {mdka-2.2.0 → mdka-2.2.2}/rfcs/handoffs/019-release-creation-via-dispatch/implementation-handoff.md +1 -1
  56. mdka-2.2.2/rfcs/handoffs/020-npm-distribution-repair/implementation-handoff.md +188 -0
  57. mdka-2.2.2/rfcs/handoffs/021-bulk-output-collision-safety/implementation-handoff.md +134 -0
  58. mdka-2.2.2/rfcs/handoffs/022-cli-allocator-and-jemalloc/alloc-counter-deprecation-handoff.md +136 -0
  59. mdka-2.2.2/rfcs/handoffs/022-cli-allocator-and-jemalloc/implementation-handoff.md +126 -0
  60. mdka-2.2.2/rfcs/handoffs/023-getting-started-doc-reconciliation/implementation-handoff.md +136 -0
  61. mdka-2.2.2/rfcs/handoffs/024-inline-composition-output-sink/implementation-handoff.md +152 -0
  62. mdka-2.2.2/rfcs/handoffs/025-output-validity-harness/implementation-handoff.md +147 -0
  63. mdka-2.2.2/rfcs/handoffs/026-consumer-artifact-gates/implementation-handoff.md +152 -0
  64. mdka-2.2.2/rfcs/handoffs/027-verification-discipline/implementation-handoff.md +170 -0
  65. mdka-2.2.2/rfcs/handoffs/028-emphasis-around-block-content/implementation-handoff.md +217 -0
  66. {mdka-2.2.0 → mdka-2.2.2}/src/alloc_counter.rs +8 -0
  67. {mdka-2.2.0 → mdka-2.2.2}/src/lib.rs +96 -28
  68. {mdka-2.2.0 → mdka-2.2.2}/src/options.rs +47 -43
  69. {mdka-2.2.0 → mdka-2.2.2}/tests/file_conversion.rs +85 -0
  70. mdka-2.2.0/.github/CONTRIBUTING.md +0 -13
  71. mdka-2.2.0/ROADMAP.md +0 -309
  72. mdka-2.2.0/docs/src/getting-started/usage-cli.md +0 -55
  73. mdka-2.2.0/docs/src/getting-started/usage-nodejs.md +0 -117
  74. {mdka-2.2.0 → mdka-2.2.2}/.gitattributes +0 -0
  75. {mdka-2.2.0 → mdka-2.2.2}/.github/CODE_OF_CONDUCT.md +0 -0
  76. {mdka-2.2.0 → mdka-2.2.2}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
  77. {mdka-2.2.0 → mdka-2.2.2}/.github/ISSUE_TEMPLATE/config.yml +0 -0
  78. {mdka-2.2.0 → mdka-2.2.2}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
  79. {mdka-2.2.0 → mdka-2.2.2}/.github/ISSUE_TEMPLATE/question.yml +0 -0
  80. {mdka-2.2.0 → mdka-2.2.2}/.github/SECURITY.md +0 -0
  81. {mdka-2.2.0 → mdka-2.2.2}/.github/workflows/docs.yaml +0 -0
  82. {mdka-2.2.0 → mdka-2.2.2}/.github/workflows/release-crates.yaml +0 -0
  83. {mdka-2.2.0 → mdka-2.2.2}/.github/workflows/release-executable.yaml +0 -0
  84. {mdka-2.2.0 → mdka-2.2.2}/.github/workflows/release-pypi.yaml +0 -0
  85. {mdka-2.2.0 → mdka-2.2.2}/.github/workflows/scripts/install-rust.sh +0 -0
  86. {mdka-2.2.0 → mdka-2.2.2}/.vscode/extensions.json +0 -0
  87. {mdka-2.2.0 → mdka-2.2.2}/.vscode/settings.json +0 -0
  88. {mdka-2.2.0 → mdka-2.2.2}/LICENSE +0 -0
  89. {mdka-2.2.0 → mdka-2.2.2}/NOTICE +0 -0
  90. {mdka-2.2.0 → mdka-2.2.2}/benches/bench_common.rs +0 -0
  91. {mdka-2.2.0 → mdka-2.2.2}/benches/benchdata/deep_nest.html +0 -0
  92. {mdka-2.2.0 → mdka-2.2.2}/benches/benchdata/flat.html +0 -0
  93. {mdka-2.2.0 → mdka-2.2.2}/benches/benchdata/large.html +0 -0
  94. {mdka-2.2.0 → mdka-2.2.2}/benches/benchdata/malformed.html +0 -0
  95. {mdka-2.2.0 → mdka-2.2.2}/benches/benchdata/medium.html +0 -0
  96. {mdka-2.2.0 → mdka-2.2.2}/benches/benchdata/scale_500k.html +0 -0
  97. {mdka-2.2.0 → mdka-2.2.2}/benches/benchdata/scale_50k.html +0 -0
  98. {mdka-2.2.0 → mdka-2.2.2}/benches/benchdata/scale_5m.html +0 -0
  99. {mdka-2.2.0 → mdka-2.2.2}/benches/benchdata/small.html +0 -0
  100. {mdka-2.2.0 → mdka-2.2.2}/benches/convert.rs +0 -0
  101. {mdka-2.2.0 → mdka-2.2.2}/benches/parallel.rs +0 -0
  102. {mdka-2.2.0 → mdka-2.2.2}/benches/scaling.rs +0 -0
  103. {mdka-2.2.0 → mdka-2.2.2}/cargo-publish.sh +0 -0
  104. {mdka-2.2.0 → mdka-2.2.2}/docs/book.toml +0 -0
  105. {mdka-2.2.0 → mdka-2.2.2}/docs/src/SUMMARY.md +0 -0
  106. {mdka-2.2.0 → mdka-2.2.2}/docs/src/api/index.md +0 -0
  107. {mdka-2.2.0 → mdka-2.2.2}/docs/src/api/text-processing.md +0 -0
  108. {mdka-2.2.0 → mdka-2.2.2}/docs/src/assets/logo.png +0 -0
  109. {mdka-2.2.0 → mdka-2.2.2}/docs/src/design/features.md +0 -0
  110. {mdka-2.2.0 → mdka-2.2.2}/docs/src/design/philosophy.md +0 -0
  111. {mdka-2.2.0 → mdka-2.2.2}/docs/src/getting-started/usage-rust.md +0 -0
  112. {mdka-2.2.0 → mdka-2.2.2}/docs/src/getting-started/usage.md +0 -0
  113. {mdka-2.2.0 → mdka-2.2.2}/docs/src/introduction.md +0 -0
  114. {mdka-2.2.0 → mdka-2.2.2}/examples/quick_bench.rs +0 -0
  115. {mdka-2.2.0 → mdka-2.2.2}/examples/quick_compare.rs +0 -0
  116. {mdka-2.2.0 → mdka-2.2.2}/python/example.py +0 -0
  117. {mdka-2.2.0 → mdka-2.2.2}/python/test_mdka.py +0 -0
  118. {mdka-2.2.0 → mdka-2.2.2}/rfcs/done/001-ci-quality-gates.md +0 -0
  119. {mdka-2.2.0 → mdka-2.2.2}/rfcs/done/002-governance-artifacts.md +0 -0
  120. {mdka-2.2.0 → mdka-2.2.2}/rfcs/done/003-architecture-doc-reconciliation.md +0 -0
  121. {mdka-2.2.0 → mdka-2.2.2}/rfcs/done/004-preprocessor-disposition.md +0 -0
  122. {mdka-2.2.0 → mdka-2.2.2}/rfcs/done/005-conversion-options-semantics.md +0 -0
  123. {mdka-2.2.0 → mdka-2.2.2}/rfcs/done/006-option-docs-and-binding-parity.md +0 -0
  124. {mdka-2.2.0 → mdka-2.2.2}/rfcs/done/014-release-time-ci-verification.md +0 -0
  125. {mdka-2.2.0 → mdka-2.2.2}/rfcs/done/016-hr-newline-reset.md +0 -0
  126. {mdka-2.2.0 → mdka-2.2.2}/rfcs/done/017-pre-fence-newline-reset.md +0 -0
  127. {mdka-2.2.0 → mdka-2.2.2}/rfcs/done/018-readme-prebuilt-binaries.md +0 -0
  128. {mdka-2.2.0 → mdka-2.2.2}/rfcs/done/019-release-creation-via-dispatch.md +0 -0
  129. {mdka-2.2.0 → mdka-2.2.2}/rfcs/handoffs/001-ci-quality-gates/implementation-handoff.md +0 -0
  130. {mdka-2.2.0 → mdka-2.2.2}/rfcs/handoffs/002-governance-artifacts/implementation-handoff.md +0 -0
  131. {mdka-2.2.0 → mdka-2.2.2}/rfcs/handoffs/003-architecture-doc-reconciliation/implementation-handoff.md +0 -0
  132. {mdka-2.2.0 → mdka-2.2.2}/rfcs/handoffs/004-preprocessor-disposition/implementation-handoff.md +0 -0
  133. {mdka-2.2.0 → mdka-2.2.2}/rfcs/handoffs/014-release-time-ci-verification/implementation-handoff.md +0 -0
  134. {mdka-2.2.0 → mdka-2.2.2}/rfcs/handoffs/015-release-tooling-completion/amendment-handoff.md +0 -0
  135. {mdka-2.2.0 → mdka-2.2.2}/rfcs/handoffs/015-release-tooling-completion/implementation-handoff.md +0 -0
  136. {mdka-2.2.0 → mdka-2.2.2}/rfcs/handoffs/015-release-tooling-completion/restore-handoff.md +0 -0
  137. {mdka-2.2.0 → mdka-2.2.2}/rfcs/handoffs/016-hr-newline-reset/implementation-handoff.md +0 -0
  138. {mdka-2.2.0 → mdka-2.2.2}/rfcs/handoffs/017-pre-fence-newline-reset/implementation-handoff.md +0 -0
  139. {mdka-2.2.0 → mdka-2.2.2}/src/renderer.rs +0 -0
  140. {mdka-2.2.0 → mdka-2.2.2}/src/traversal/tests.rs +0 -0
  141. {mdka-2.2.0 → mdka-2.2.2}/src/traversal.rs +0 -0
  142. {mdka-2.2.0 → mdka-2.2.2}/src/utils/tests.rs +0 -0
  143. {mdka-2.2.0 → mdka-2.2.2}/src/utils.rs +0 -0
  144. {mdka-2.2.0 → mdka-2.2.2}/tests/anchor_drift_guard.rs +0 -0
  145. {mdka-2.2.0 → mdka-2.2.2}/tests/block_elements.rs +0 -0
  146. {mdka-2.2.0 → mdka-2.2.2}/tests/characterisation_attributes.rs +0 -0
  147. {mdka-2.2.0 → mdka-2.2.2}/tests/characterisation_elements.rs +0 -0
  148. {mdka-2.2.0 → mdka-2.2.2}/tests/characterisation_structural.rs +0 -0
  149. {mdka-2.2.0 → mdka-2.2.2}/tests/common.rs +0 -0
  150. {mdka-2.2.0 → mdka-2.2.2}/tests/compat.rs +0 -0
  151. {mdka-2.2.0 → mdka-2.2.2}/tests/hr_newline_reset.rs +0 -0
  152. {mdka-2.2.0 → mdka-2.2.2}/tests/inline_elements.rs +0 -0
  153. {mdka-2.2.0 → mdka-2.2.2}/tests/pre_fence_newline_reset.rs +0 -0
  154. {mdka-2.2.0 → mdka-2.2.2}/tests/preserve_ids_anchors.rs +0 -0
  155. {mdka-2.2.0 → mdka-2.2.2}/tests/robustness.rs +0 -0
  156. {mdka-2.2.0 → mdka-2.2.2}/version.sh +0 -0
@@ -0,0 +1,42 @@
1
+ ## ✨ Contributing
2
+
3
+ We’re happy to receive feedback, bug reports, and questions via GitHub Issues.
4
+ Pull requests are also welcome — though please note that we may not always be able to accept them.
5
+
6
+ This project is maintained as a labor of love. We welcome community participation, but:
7
+
8
+ - Issues that are respectful and constructive are appreciated.
9
+ - Pull requests are reviewed, but acceptance is not guaranteed.
10
+ - We do not engage in long debates or vision disagreements.
11
+ - If you have a different direction in mind, please fork freely, provided proper licensing is respected.
12
+
13
+ Thanks for understanding the scope and spirit of the project.
14
+
15
+ ## Documentation examples
16
+
17
+ Every fenced code block in `docs/src/` whose info string names `rust`,
18
+ `python`, `js` or `ts` is **checked in CI** by the `docs example gate`
19
+ workflow. Rust blocks are compiled against the crate, TypeScript blocks are
20
+ type-checked against the generated `node/index.d.ts`, JavaScript blocks are
21
+ parsed, and Python blocks are syntax-checked with every `mdka` symbol they
22
+ name resolved against the installed package.
23
+
24
+ A block that is deliberately **not** runnable on its own — a signature
25
+ display, a type declaration, or a snippet that relies on a variable
26
+ introduced in the surrounding prose — must say so in its info string:
27
+
28
+ ````markdown
29
+ ```rust,fragment
30
+ pub fn html_to_markdown(html: &str) -> String
31
+ ```
32
+ ````
33
+
34
+ **The marker is what excludes a block.** There is no list of exceptions kept
35
+ somewhere else, because such a list drifts out of step with the documents it
36
+ describes. Rust's own `ignore` and `no_run` are honoured for the same
37
+ purpose. A block with no info string at all — which is how sample *output*
38
+ is written — is never checked.
39
+
40
+ If a block is genuinely runnable, do not mark it as a fragment to quiet the
41
+ gate; fix the example. That gate exists because two shipped examples failed
42
+ on their first line.
@@ -50,7 +50,7 @@ jobs:
50
50
  run: cargo clippy --workspace --all-targets --all-features -- -D warnings
51
51
 
52
52
  - name: cargo test
53
- run: cargo test --workspace --locked
53
+ run: cargo test --workspace --all-features --locked
54
54
 
55
55
  - name: cargo build
56
56
  run: cargo build --workspace --locked
@@ -0,0 +1,78 @@
1
+ name: crates package gate
2
+
3
+ # RFC 026 §4.2. The `rust` job in ci.yaml runs `cargo test` in-workspace,
4
+ # where every path dependency resolves and every file is present. It cannot
5
+ # see a crate that compiles here and fails standalone -- through a missing
6
+ # `include`, a path dependency with no version, or a file absent from the
7
+ # packaged .crate.
8
+ #
9
+ # `cargo package` (without --no-verify) packages each crate and then builds
10
+ # from the extracted package, resolving dependencies from the registry
11
+ # rather than from the workspace. That is the consumer's build.
12
+ #
13
+ # Its own workflow file, not ci.yaml -- see RFC 026 §3.5 and RFC 020's
14
+ # correction record.
15
+
16
+ on:
17
+ push:
18
+ branches: [main]
19
+ pull_request:
20
+ branches: [main]
21
+ workflow_dispatch:
22
+
23
+ permissions:
24
+ contents: read
25
+
26
+ concurrency:
27
+ group: ${{ github.workflow }}-${{ github.ref }}
28
+ cancel-in-progress: true
29
+
30
+ defaults:
31
+ run:
32
+ shell: bash
33
+
34
+ jobs:
35
+ crates-package-gate:
36
+ runs-on: ubuntu-latest
37
+ timeout-minutes: 30
38
+
39
+ steps:
40
+ - name: Checkout repository
41
+ uses: actions/checkout@v6
42
+
43
+ - name: Cache cargo dependencies and build
44
+ uses: actions/cache@v5
45
+ with:
46
+ path: |
47
+ ~/.cargo/registry
48
+ ~/.cargo/git
49
+ target
50
+ key: ${{ runner.os }}-cargo-package-gate-${{ hashFiles('**/Cargo.lock') }}
51
+ restore-keys: |
52
+ ${{ runner.os }}-cargo-package-gate-
53
+
54
+ # mdka first: the other three depend on it, and their verification
55
+ # builds resolve it from the registry.
56
+ #
57
+ # No --no-verify anywhere. Packaging without building the result would
58
+ # check that the tarball can be produced, not that it works -- which is
59
+ # the distinction this whole milestone is about.
60
+ - name: cargo package -p mdka
61
+ run: cargo package -p mdka --locked
62
+
63
+ - name: cargo package -p mdka-cli
64
+ run: cargo package -p mdka-cli --locked
65
+
66
+ - name: cargo package -p mdka-node
67
+ run: cargo package -p mdka-node --locked
68
+
69
+ - name: cargo package -p mdka-python
70
+ run: cargo package -p mdka-python --locked
71
+
72
+ - name: Show what each package actually contains
73
+ run: |
74
+ set -euo pipefail
75
+ for crate in target/package/*.crate; do
76
+ echo "=== $crate"
77
+ tar -tzf "$crate" | sed 's/^/ /'
78
+ done
@@ -64,8 +64,23 @@ jobs:
64
64
  env:
65
65
  GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
66
66
  run: |
67
- gh release create "${{ github.ref_name }}" \
68
- --title "${{ github.ref_name }}" \
67
+ tag="${{ github.ref_name }}"
68
+ link="https://github.com/${{ github.repository }}/blob/${tag}/CHANGELOG.md"
69
+
70
+ # Point at this version's CHANGELOG section. GitHub's heading anchor
71
+ # includes the release date ("## [2.2.1] - 2026-09-01" -> #221---2026-09-01),
72
+ # so it has to be derived from the heading rather than the version alone.
73
+ # If the heading is not found, the link degrades to the file itself --
74
+ # never to a broken anchor.
75
+ heading=$(grep -m1 "^## \[${tag}\]" CHANGELOG.md || true)
76
+ if [ -n "$heading" ]; then
77
+ anchor=$(printf '%s' "$heading" | sed 's/^## //' | tr -d '[].' | tr ' ' '-' | tr 'A-Z' 'a-z')
78
+ link="${link}#${anchor}"
79
+ fi
80
+
81
+ gh release create "${tag}" \
82
+ --title "${tag}" \
83
+ --notes "📖 [Changelog for ${tag}](${link})" \
69
84
  --generate-notes
70
85
 
71
86
  - name: Start the publishing workflows
@@ -0,0 +1,94 @@
1
+ name: docs example gate
2
+
3
+ # RFC 026 §4.3. Nothing has ever executed the examples in docs/src/. D-12
4
+ # and D-13 shipped examples that fail on their first line -- a duplicate
5
+ # `const`, and a TypeScript import of a type the bindings never exported.
6
+ #
7
+ # Every fenced block whose info string names a language we can check is
8
+ # checked. A block that is deliberately a fragment carries a `fragment`
9
+ # marker, and the marker is what excludes it: there is no list of exceptions
10
+ # kept elsewhere that can drift out of step with the documents. The
11
+ # convention is documented in CONTRIBUTING.md.
12
+ #
13
+ # Its own workflow file, not ci.yaml -- see RFC 026 §3.5.
14
+
15
+ on:
16
+ push:
17
+ branches: [main]
18
+ pull_request:
19
+ branches: [main]
20
+ workflow_dispatch:
21
+
22
+ permissions:
23
+ contents: read
24
+
25
+ concurrency:
26
+ group: ${{ github.workflow }}-${{ github.ref }}
27
+ cancel-in-progress: true
28
+
29
+ defaults:
30
+ run:
31
+ shell: bash
32
+
33
+ jobs:
34
+ docs-example-gate:
35
+ runs-on: ubuntu-latest
36
+ timeout-minutes: 20
37
+
38
+ steps:
39
+ - name: Checkout repository
40
+ uses: actions/checkout@v6
41
+
42
+ - name: Setup Python
43
+ uses: actions/setup-python@v6
44
+ with:
45
+ python-version: 3.x
46
+
47
+ - name: Setup Node.js
48
+ uses: actions/setup-node@v6
49
+ with:
50
+ node-version: 24
51
+
52
+ - name: Cache cargo dependencies and build
53
+ uses: actions/cache@v5
54
+ with:
55
+ path: |
56
+ ~/.cargo/registry
57
+ ~/.cargo/git
58
+ target
59
+ key: ${{ runner.os }}-cargo-docs-gate-${{ hashFiles('**/Cargo.lock') }}
60
+ restore-keys: |
61
+ ${{ runner.os }}-cargo-docs-gate-
62
+
63
+ # The TypeScript examples are checked against the generated
64
+ # declarations, so those must exist and be current.
65
+ - name: Generate the TypeScript declarations
66
+ working-directory: node
67
+ run: |
68
+ npm ci
69
+ npm run build
70
+
71
+ # Python examples resolve their `mdka` symbols against the installed
72
+ # package, so a documented function that does not exist is caught.
73
+ - name: Stage README for maturin
74
+ run: cp -f README.md python/
75
+
76
+ - name: Install uv
77
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
78
+
79
+ - name: Build and install the wheel into a venv
80
+ run: |
81
+ set -euo pipefail
82
+ uv venv
83
+ uv pip install maturin
84
+ cd python
85
+ source ../.venv/bin/activate
86
+ maturin build --out "$RUNNER_TEMP/wheels"
87
+ cd ..
88
+ python -m venv "$RUNNER_TEMP/docs-venv"
89
+ "$RUNNER_TEMP/docs-venv/bin/pip" install --no-index "$RUNNER_TEMP"/wheels/mdka-*.whl
90
+
91
+ - name: Check every runnable example in docs/src/
92
+ run: |
93
+ python .github/workflows/scripts/check-docs-examples.py \
94
+ --python "$RUNNER_TEMP/docs-venv/bin/python"
@@ -0,0 +1,68 @@
1
+ name: npm install gate
2
+
3
+ # RFC 020 / RFC 026. This gate must NOT live in ci.yaml: release-npm.yaml and
4
+ # create-release.yaml gate on ci.yaml's conclusion, so a red gate there blocks
5
+ # the very release that would make it pass. See RFC 020's correction record.
6
+ #
7
+ # It installs the PUBLISHED package from the registry, the way a consumer does.
8
+ #
9
+ # It deliberately does not pack and install the local tarball. That was the
10
+ # original design and it could never pass: `optionalDependencies` are injected
11
+ # by `napi pre-publish` at release time, so a locally packed tarball has none,
12
+ # resolves no per-platform package, and always fails to load. It was testing an
13
+ # artifact that is not the one published.
14
+ #
15
+ # Consequence, stated rather than hidden: this gate reports on the LAST
16
+ # PUBLISHED release, not on the current working tree. That lag is inherent --
17
+ # a published artifact cannot be verified before it is published. What it buys
18
+ # is an alarm that would have been red for all twelve broken 2.x releases.
19
+
20
+ on:
21
+ push:
22
+ branches: [main]
23
+ pull_request:
24
+ branches: [main]
25
+ workflow_dispatch:
26
+ schedule:
27
+ # Catch a registry-side regression even when nobody pushes.
28
+ - cron: '0 6 * * 1'
29
+
30
+ permissions:
31
+ contents: read
32
+
33
+ defaults:
34
+ run:
35
+ shell: bash
36
+
37
+ jobs:
38
+ npm-install-gate:
39
+ runs-on: ubuntu-latest
40
+ timeout-minutes: 10
41
+
42
+ steps:
43
+ - name: Setup Node.js
44
+ uses: actions/setup-node@v6
45
+ with:
46
+ node-version: 24
47
+
48
+ - name: Install the published package from the registry and require it
49
+ run: |
50
+ set -euo pipefail
51
+ install_dir="$RUNNER_TEMP/npm-install-gate"
52
+ mkdir -p "$install_dir"
53
+ cd "$install_dir"
54
+ npm init -y >/dev/null
55
+
56
+ published=$(npm view mdka version)
57
+ echo "Testing published mdka@${published}"
58
+ npm install "mdka@${published}"
59
+
60
+ node -e "
61
+ const m = require('mdka');
62
+ const out = m.htmlToMarkdown('<h1>x</h1>');
63
+ if (!out.includes('# x')) {
64
+ console.error('unexpected output: ' + JSON.stringify(out));
65
+ process.exit(1);
66
+ }
67
+ console.log('OK: mdka@${published} installs and converts:', JSON.stringify(out));
68
+ "
@@ -0,0 +1,112 @@
1
+ name: pypi wheel gate
2
+
3
+ # RFC 026 §4.1. The `python` job in ci.yaml runs `maturin develop` + pytest,
4
+ # which imports the project from the workspace. That cannot see anything
5
+ # missing from the *wheel* -- the artifact `pip install mdka` actually
6
+ # fetches. This gate builds the wheel, installs it into a venv outside the
7
+ # workspace, and imports it from there.
8
+ #
9
+ # Its own workflow file, not ci.yaml: release-npm.yaml and
10
+ # create-release.yaml gate on ci.yaml's conclusion, so an artifact gate
11
+ # living there can block the very release that would make it pass. That
12
+ # deadlocked 2.2.1. See RFC 020's correction record and RFC 026 §3.5.
13
+ #
14
+ # It does NOT assert py.typed. RFC 023 decided to remove that claim rather
15
+ # than ship the marker: every public symbol comes from a compiled PyO3
16
+ # module with no .pyi stubs, so shipping py.typed would silence a type
17
+ # checker without giving it anything to check. See RFC 023.
18
+
19
+ on:
20
+ push:
21
+ branches: [main]
22
+ pull_request:
23
+ branches: [main]
24
+ workflow_dispatch:
25
+
26
+ permissions:
27
+ contents: read
28
+
29
+ concurrency:
30
+ group: ${{ github.workflow }}-${{ github.ref }}
31
+ cancel-in-progress: true
32
+
33
+ defaults:
34
+ run:
35
+ shell: bash
36
+
37
+ jobs:
38
+ pypi-wheel-gate:
39
+ runs-on: ubuntu-latest
40
+ timeout-minutes: 20
41
+
42
+ steps:
43
+ - name: Checkout repository
44
+ uses: actions/checkout@v6
45
+
46
+ - name: Setup Python
47
+ uses: actions/setup-python@v6
48
+ with:
49
+ python-version: 3.x
50
+
51
+ - name: Cache cargo dependencies and build
52
+ uses: actions/cache@v5
53
+ with:
54
+ path: |
55
+ ~/.cargo/registry
56
+ ~/.cargo/git
57
+ target
58
+ key: ${{ runner.os }}-cargo-wheel-gate-${{ hashFiles('**/Cargo.lock') }}
59
+ restore-keys: |
60
+ ${{ runner.os }}-cargo-wheel-gate-
61
+
62
+ # python/pyproject.toml declares readme = "README.md" but the README
63
+ # lives at the repository root. Same staging the `python` job does.
64
+ - name: Stage README for maturin
65
+ run: cp -f README.md python/
66
+
67
+ - name: Install uv
68
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
69
+
70
+ - name: Install maturin
71
+ run: |
72
+ uv venv
73
+ uv pip install maturin
74
+
75
+ # `maturin build`, never `maturin develop`. develop is the blind spot
76
+ # this gate exists to close.
77
+ - name: Build the wheel
78
+ working-directory: python
79
+ run: |
80
+ source ../.venv/bin/activate
81
+ maturin build --out "$RUNNER_TEMP/wheels"
82
+
83
+ - name: Install the wheel outside the workspace and use it
84
+ run: |
85
+ set -euo pipefail
86
+ venv="$RUNNER_TEMP/wheel-gate-venv"
87
+ python -m venv "$venv"
88
+ wheel=$(ls "$RUNNER_TEMP"/wheels/mdka-*.whl)
89
+ echo "Installing $wheel"
90
+ "$venv/bin/pip" install --no-index "$wheel"
91
+
92
+ # cd out of the workspace so `import mdka` cannot resolve to the
93
+ # source tree instead of the installed package.
94
+ cd "$RUNNER_TEMP"
95
+ "$venv/bin/python" - <<'PY'
96
+ import mdka, pathlib, sys
97
+
98
+ # Prove we imported the installed package, not a source checkout.
99
+ where = pathlib.Path(mdka.__file__).resolve()
100
+ print("imported from:", where)
101
+ assert "site-packages" in where.parts, f"not the installed package: {where}"
102
+
103
+ out = mdka.html_to_markdown("<h1>x</h1>")
104
+ assert "# x" in out, f"unexpected output: {out!r}"
105
+
106
+ # The compiled binding must be inside the wheel, not merely
107
+ # importable because something else built it.
108
+ so = list(where.parent.glob("mdka_python*.so"))
109
+ assert so, f"native binding missing from the installed package: {list(where.parent.iterdir())}"
110
+
111
+ print("OK:", repr(out), "| binding:", so[0].name, "| version:", mdka.__version__)
112
+ PY
@@ -167,7 +167,32 @@ jobs:
167
167
  run: |
168
168
  npx napi create-npm-dirs
169
169
  npx napi artifacts
170
-
170
+
171
+ # RFC 020: this step was missing entirely, which is why `npm install
172
+ # mdka` produced an unusable package for the whole 2.x line -- the
173
+ # per-platform packages were never published past 1.6.9, and the main
174
+ # package's manifest never carried optionalDependencies at all.
175
+ #
176
+ # --no-gh-release is required, not optional: `napi pre-publish`
177
+ # defaults --gh-release to true and would otherwise attempt to create
178
+ # a second GitHub release for a tag create-release.yaml already
179
+ # released, which fails (non-fatally -- it's caught and logged -- but
180
+ # there is no reason to invite the noise).
181
+ - name: napi pre-publish (publish per-platform packages, inject optionalDependencies)
182
+ working-directory: node
183
+ env:
184
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
185
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
186
+ run: npx napi pre-publish --no-gh-release
187
+
188
+ # No `npm ci` here. The `napi pre-publish` step above rewrites
189
+ # package.json to add optionalDependencies for the per-platform
190
+ # packages, which puts it out of sync with package-lock.json --
191
+ # `npm ci` refuses to run in that state and fails the publish. It was
192
+ # also redundant: the "Install dependencies" step above already ran
193
+ # `npm install`, and `npm publish` does not need node_modules.
194
+ # Removed 2026-09-01 after it blocked the 2.2.1 npm publish, which is
195
+ # the first release in which pre-publish ever ran.
171
196
  - name: Publish
172
197
  working-directory: node
173
198
  env:
@@ -175,5 +200,4 @@ jobs:
175
200
  NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
176
201
  run: |
177
202
  cp -f ../README.md .
178
- npm ci
179
203
  npm publish