mdka 2.2.2__tar.gz → 2.2.3__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.
- {mdka-2.2.2 → mdka-2.2.3}/.github/workflows/scripts/check-docs-examples.py +124 -10
- {mdka-2.2.2 → mdka-2.2.3}/CHANGELOG.md +53 -0
- {mdka-2.2.2 → mdka-2.2.3}/Cargo.lock +7 -7
- {mdka-2.2.2 → mdka-2.2.3}/Cargo.toml +2 -2
- {mdka-2.2.2 → mdka-2.2.3}/PKG-INFO +39 -15
- {mdka-2.2.2/python → mdka-2.2.3}/README.md +38 -14
- {mdka-2.2.2 → mdka-2.2.3}/ROADMAP.md +47 -3
- mdka-2.2.3/docs/src/getting-started/installation.md +77 -0
- {mdka-2.2.2 → mdka-2.2.3}/docs/src/getting-started/usage-cli.md +6 -0
- {mdka-2.2.2 → mdka-2.2.3}/docs/src/getting-started/usage-nodejs.md +14 -3
- {mdka-2.2.2 → mdka-2.2.3}/docs/src/getting-started/usage-python.md +15 -4
- {mdka-2.2.2 → mdka-2.2.3}/pyproject.toml +1 -1
- {mdka-2.2.2 → mdka-2.2.3}/python/Cargo.toml +1 -1
- {mdka-2.2.2 → mdka-2.2.3/python}/README.md +38 -14
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/README.md +1 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/accepted/024-inline-composition-output-sink.md +27 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/done/026-consumer-artifact-gates.md +43 -0
- mdka-2.2.3/rfcs/done/029-published-surface-documentation-repair.md +126 -0
- mdka-2.2.3/rfcs/handoffs/029-published-surface-documentation-repair/implementation-handoff.md +195 -0
- mdka-2.2.2/docs/src/getting-started/installation.md +0 -53
- {mdka-2.2.2 → mdka-2.2.3}/.gitattributes +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/.github/CODE_OF_CONDUCT.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/.github/CONTRIBUTING.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/.github/ISSUE_TEMPLATE/bug_report.yml +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/.github/ISSUE_TEMPLATE/config.yml +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/.github/ISSUE_TEMPLATE/feature_request.yml +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/.github/ISSUE_TEMPLATE/question.yml +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/.github/SECURITY.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/.github/workflows/ci.yaml +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/.github/workflows/crates-package-gate.yaml +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/.github/workflows/create-release.yaml +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/.github/workflows/docs-example-gate.yaml +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/.github/workflows/docs.yaml +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/.github/workflows/npm-install-gate.yaml +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/.github/workflows/pypi-wheel-gate.yaml +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/.github/workflows/release-crates.yaml +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/.github/workflows/release-executable.yaml +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/.github/workflows/release-npm.yaml +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/.github/workflows/release-pypi.yaml +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/.github/workflows/scripts/install-rust.sh +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/.gitignore +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/.vscode/extensions.json +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/.vscode/settings.json +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/LICENSE +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/NOTICE +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/benches/bench_common.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/benches/benchdata/deep_nest.html +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/benches/benchdata/flat.html +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/benches/benchdata/large.html +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/benches/benchdata/malformed.html +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/benches/benchdata/medium.html +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/benches/benchdata/scale_500k.html +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/benches/benchdata/scale_50k.html +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/benches/benchdata/scale_5m.html +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/benches/benchdata/small.html +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/benches/convert.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/benches/memory.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/benches/parallel.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/benches/scaling.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/cargo-publish.sh +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/docs/book.toml +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/docs/src/SUMMARY.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/docs/src/api/core.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/docs/src/api/elements.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/docs/src/api/errors.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/docs/src/api/index.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/docs/src/api/modes.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/docs/src/api/options.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/docs/src/api/text-processing.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/docs/src/assets/logo.png +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/docs/src/design/architecture.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/docs/src/design/features.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/docs/src/design/performance-characteristics.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/docs/src/design/philosophy.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/docs/src/getting-started/usage-rust.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/docs/src/getting-started/usage.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/docs/src/introduction.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/examples/measure_mem.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/examples/quick_bench.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/examples/quick_compare.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/examples/quick_mem.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/mdka/__init__.py +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/python/example.py +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/python/src/lib.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/python/test_mdka.py +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/accepted/025-output-validity-harness.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/accepted/028-emphasis-around-block-content.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/done/000-rfc-lifecycle-policy.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/done/001-ci-quality-gates.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/done/002-governance-artifacts.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/done/003-architecture-doc-reconciliation.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/done/004-preprocessor-disposition.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/done/005-conversion-options-semantics.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/done/006-option-docs-and-binding-parity.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/done/007-english-only-public-surface.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/done/014-release-time-ci-verification.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/done/015-release-tooling-completion.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/done/016-hr-newline-reset.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/done/017-pre-fence-newline-reset.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/done/018-readme-prebuilt-binaries.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/done/019-release-creation-via-dispatch.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/done/020-npm-distribution-repair.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/done/021-bulk-output-collision-safety.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/done/022-cli-allocator-and-jemalloc.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/done/023-getting-started-doc-reconciliation.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/done/027-verification-discipline.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/handoffs/001-ci-quality-gates/implementation-handoff.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/handoffs/002-governance-artifacts/implementation-handoff.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/handoffs/003-architecture-doc-reconciliation/implementation-handoff.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/handoffs/004-preprocessor-disposition/implementation-handoff.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/handoffs/005-conversion-options-semantics/implementation-handoff.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/handoffs/005-conversion-options-semantics/slice-b1-placement-correction-handoff.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/handoffs/005-conversion-options-semantics/slices-bc-handoff.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/handoffs/006-option-docs-and-binding-parity/implementation-handoff.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/handoffs/007-english-only-public-surface/implementation-handoff.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/handoffs/014-release-time-ci-verification/implementation-handoff.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/handoffs/015-release-tooling-completion/amendment-handoff.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/handoffs/015-release-tooling-completion/implementation-handoff.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/handoffs/015-release-tooling-completion/restore-handoff.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/handoffs/016-hr-newline-reset/implementation-handoff.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/handoffs/017-pre-fence-newline-reset/implementation-handoff.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/handoffs/018-readme-prebuilt-binaries/implementation-handoff.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/handoffs/019-release-creation-via-dispatch/implementation-handoff.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/handoffs/020-npm-distribution-repair/implementation-handoff.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/handoffs/021-bulk-output-collision-safety/implementation-handoff.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/handoffs/022-cli-allocator-and-jemalloc/alloc-counter-deprecation-handoff.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/handoffs/022-cli-allocator-and-jemalloc/implementation-handoff.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/handoffs/023-getting-started-doc-reconciliation/implementation-handoff.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/handoffs/024-inline-composition-output-sink/implementation-handoff.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/handoffs/025-output-validity-harness/implementation-handoff.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/handoffs/026-consumer-artifact-gates/implementation-handoff.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/handoffs/027-verification-discipline/implementation-handoff.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/rfcs/handoffs/028-emphasis-around-block-content/implementation-handoff.md +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/src/alloc_counter.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/src/lib.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/src/options.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/src/renderer.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/src/traversal/tests.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/src/traversal.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/src/utils/tests.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/src/utils.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/tests/anchor_drift_guard.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/tests/block_elements.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/tests/characterisation_attributes.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/tests/characterisation_elements.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/tests/characterisation_structural.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/tests/common.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/tests/compat.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/tests/file_conversion.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/tests/hr_newline_reset.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/tests/inline_elements.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/tests/pre_fence_newline_reset.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/tests/preserve_ids_anchors.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/tests/robustness.rs +0 -0
- {mdka-2.2.2 → mdka-2.2.3}/version.sh +0 -0
|
@@ -38,10 +38,30 @@ class Block:
|
|
|
38
38
|
return f"{self.path}:{self.line}"
|
|
39
39
|
|
|
40
40
|
|
|
41
|
-
def
|
|
42
|
-
"""
|
|
41
|
+
def markdown_files(roots):
|
|
42
|
+
"""Every markdown file under each root. A root may be a file or a directory.
|
|
43
|
+
|
|
44
|
+
README.md is a root in its own right, not something reachable from
|
|
45
|
+
`docs/src`. It renders on GitHub, crates.io, npmjs.com and PyPI, and it was
|
|
46
|
+
invisible to this gate until RFC 029 -- the gate built to catch broken
|
|
47
|
+
examples was pointed at a directory that excluded the most-read file in the
|
|
48
|
+
project.
|
|
49
|
+
"""
|
|
50
|
+
seen, out = set(), []
|
|
51
|
+
for root in roots:
|
|
52
|
+
p = Path(root)
|
|
53
|
+
found = sorted(p.rglob("*.md")) if p.is_dir() else [p]
|
|
54
|
+
for md in found:
|
|
55
|
+
if md.resolve() not in seen:
|
|
56
|
+
seen.add(md.resolve())
|
|
57
|
+
out.append(md)
|
|
58
|
+
return out
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def extract(roots):
|
|
62
|
+
"""Yield every fenced block under `roots`, in document order."""
|
|
43
63
|
blocks = []
|
|
44
|
-
for md in
|
|
64
|
+
for md in markdown_files(roots):
|
|
45
65
|
lines = md.read_text(encoding="utf-8").splitlines()
|
|
46
66
|
i = 0
|
|
47
67
|
while i < len(lines):
|
|
@@ -67,6 +87,42 @@ def extract(root):
|
|
|
67
87
|
return blocks
|
|
68
88
|
|
|
69
89
|
|
|
90
|
+
LINK = re.compile(r"!?\[[^\]]*\]\(([^)\s]+)")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def check_readme_links(path):
|
|
94
|
+
"""Assert README.md carries no relative or root-absolute Markdown link.
|
|
95
|
+
|
|
96
|
+
Deliberately a grep, not a link checker: nothing is resolved, followed or
|
|
97
|
+
crawled. README.md is published inside the npm tarball (four files) and
|
|
98
|
+
rendered on npmjs.com and the PyPI project page, where a relative path
|
|
99
|
+
resolves against the registry rather than the repository. `./CHANGELOG.md`,
|
|
100
|
+
`./docs/` and a root-absolute logo path were all live and broken on both
|
|
101
|
+
registries at 2.2.2.
|
|
102
|
+
|
|
103
|
+
Scoped to README.md alone. `docs/src/` is rendered by mdbook, where
|
|
104
|
+
relative links are correct and expected.
|
|
105
|
+
"""
|
|
106
|
+
p = Path(path)
|
|
107
|
+
if not p.exists():
|
|
108
|
+
return []
|
|
109
|
+
# Strip fenced blocks: a `](` inside a code sample is not a link.
|
|
110
|
+
text, fenced = [], False
|
|
111
|
+
for line in p.read_text(encoding="utf-8").splitlines():
|
|
112
|
+
if line.lstrip().startswith("```"):
|
|
113
|
+
fenced = not fenced
|
|
114
|
+
continue
|
|
115
|
+
text.append("" if fenced else line)
|
|
116
|
+
|
|
117
|
+
bad = []
|
|
118
|
+
for n, line in enumerate(text, 1):
|
|
119
|
+
for target in LINK.findall(line):
|
|
120
|
+
if target.startswith("#") or "://" in target or target.startswith("mailto:"):
|
|
121
|
+
continue
|
|
122
|
+
bad.append(f"{p}:{n}: relative or root-absolute link: {target}")
|
|
123
|
+
return bad
|
|
124
|
+
|
|
125
|
+
|
|
70
126
|
def runnable(blocks):
|
|
71
127
|
out = []
|
|
72
128
|
for b in blocks:
|
|
@@ -172,21 +228,63 @@ def check_python(blocks, workdir, python):
|
|
|
172
228
|
except SyntaxError:
|
|
173
229
|
continue
|
|
174
230
|
names = set()
|
|
231
|
+
imported = set()
|
|
175
232
|
for node in ast.walk(tree):
|
|
176
233
|
if isinstance(node, ast.ImportFrom) and (node.module or "").split(".")[0] == "mdka":
|
|
177
234
|
names.update(a.name for a in node.names)
|
|
235
|
+
imported.update(a.asname or a.name for a in node.names)
|
|
178
236
|
elif (
|
|
179
237
|
isinstance(node, ast.Attribute)
|
|
180
238
|
and isinstance(node.value, ast.Name)
|
|
181
239
|
and node.value.id == "mdka"
|
|
182
240
|
):
|
|
183
241
|
names.add(node.attr)
|
|
184
|
-
|
|
242
|
+
|
|
243
|
+
# Keyword arguments, not just symbols. `html_to_markdown_with` resolves
|
|
244
|
+
# whether or not `preserve_unknown_attrs=True` is a real parameter --
|
|
245
|
+
# and it is not; it raises TypeError. Documenting a call that raises is
|
|
246
|
+
# worse than documenting nothing, so bind the documented kwargs against
|
|
247
|
+
# the installed signature (RFC 029 §4.2).
|
|
248
|
+
calls = []
|
|
249
|
+
for node in ast.walk(tree):
|
|
250
|
+
if not isinstance(node, ast.Call) or not node.keywords:
|
|
251
|
+
continue
|
|
252
|
+
fn = node.func
|
|
253
|
+
if isinstance(fn, ast.Attribute) and isinstance(fn.value, ast.Name) and fn.value.id == "mdka":
|
|
254
|
+
target = fn.attr
|
|
255
|
+
elif isinstance(fn, ast.Name) and fn.id in imported:
|
|
256
|
+
target = fn.id
|
|
257
|
+
else:
|
|
258
|
+
continue
|
|
259
|
+
kws = [k.arg for k in node.keywords if k.arg]
|
|
260
|
+
if kws:
|
|
261
|
+
calls.append((target, kws))
|
|
262
|
+
|
|
263
|
+
if not names and not calls:
|
|
185
264
|
continue
|
|
186
265
|
probe = (
|
|
187
|
-
"import mdka, sys\n"
|
|
266
|
+
"import inspect, mdka, sys\n"
|
|
188
267
|
f"missing = [n for n in {sorted(names)!r} if not hasattr(mdka, n)]\n"
|
|
189
|
-
"
|
|
268
|
+
"if missing:\n"
|
|
269
|
+
" sys.exit('missing from installed mdka: ' + ', '.join(missing))\n"
|
|
270
|
+
f"problems = []\n"
|
|
271
|
+
f"for fname, kws in {calls!r}:\n"
|
|
272
|
+
" fn = getattr(mdka, fname, None)\n"
|
|
273
|
+
" if fn is None:\n"
|
|
274
|
+
" problems.append(fname + ': not in installed mdka'); continue\n"
|
|
275
|
+
" try:\n"
|
|
276
|
+
" sig = inspect.signature(fn)\n"
|
|
277
|
+
" except (TypeError, ValueError):\n"
|
|
278
|
+
" continue\n"
|
|
279
|
+
" params = sig.parameters\n"
|
|
280
|
+
" if any(p.kind is inspect.Parameter.VAR_KEYWORD for p in params.values()):\n"
|
|
281
|
+
" continue\n"
|
|
282
|
+
" bad = [k for k in kws if k not in params]\n"
|
|
283
|
+
" if bad:\n"
|
|
284
|
+
" problems.append(fname + '() rejects: ' + ', '.join(bad))\n"
|
|
285
|
+
"if problems:\n"
|
|
286
|
+
" sys.exit('documented call does not match the installed signature -- '\n"
|
|
287
|
+
" + '; '.join(problems))\n"
|
|
190
288
|
)
|
|
191
289
|
pf = workdir / f"probe{n}.py"
|
|
192
290
|
pf.write_text(probe, encoding="utf-8")
|
|
@@ -252,7 +350,9 @@ def check_ts(blocks, workdir, types_dir):
|
|
|
252
350
|
|
|
253
351
|
def main():
|
|
254
352
|
ap = argparse.ArgumentParser()
|
|
255
|
-
ap.add_argument("--root",
|
|
353
|
+
ap.add_argument("--root", action="append", default=None,
|
|
354
|
+
help="file or directory to scan; repeatable "
|
|
355
|
+
"(default: docs/src and README.md)")
|
|
256
356
|
ap.add_argument("--types", default="node", help="dir holding the generated index.d.ts")
|
|
257
357
|
ap.add_argument("--python", default="", help="interpreter with mdka installed; enables symbol resolution")
|
|
258
358
|
ap.add_argument("--list", action="store_true", help="list blocks and exit")
|
|
@@ -265,7 +365,8 @@ def main():
|
|
|
265
365
|
print(f"error: --python {args.python} does not exist", file=sys.stderr)
|
|
266
366
|
return 2
|
|
267
367
|
|
|
268
|
-
|
|
368
|
+
roots = args.root or ["docs/src", "README.md"]
|
|
369
|
+
blocks = extract(roots)
|
|
269
370
|
run = runnable(blocks)
|
|
270
371
|
|
|
271
372
|
if args.list:
|
|
@@ -291,9 +392,22 @@ def main():
|
|
|
291
392
|
failures += check_js(by.get("js", []), wd)
|
|
292
393
|
failures += check_ts(by.get("ts", []), wd, args.types)
|
|
293
394
|
|
|
294
|
-
|
|
295
|
-
|
|
395
|
+
link_problems = check_readme_links("README.md")
|
|
396
|
+
if link_problems:
|
|
397
|
+
print(f"\n{len(link_problems)} README link problem(s):\n")
|
|
398
|
+
for msg in link_problems:
|
|
399
|
+
print(" " + msg)
|
|
400
|
+
print(
|
|
401
|
+
"\nREADME.md ships inside the npm tarball and renders on npmjs.com\n"
|
|
402
|
+
"and the PyPI project page, where a relative path resolves against the\n"
|
|
403
|
+
"registry rather than the repository. Use an absolute URL."
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
if not failures and not link_problems:
|
|
407
|
+
print("\nAll runnable examples OK. README links OK.")
|
|
296
408
|
return 0
|
|
409
|
+
if not failures:
|
|
410
|
+
return 1
|
|
297
411
|
|
|
298
412
|
print(f"\n{len(failures)} failing example(s):\n")
|
|
299
413
|
for b, err in failures:
|
|
@@ -11,6 +11,59 @@ confidence, that is stated explicitly rather than guessed.
|
|
|
11
11
|
|
|
12
12
|
## [Unreleased]
|
|
13
13
|
|
|
14
|
+
## [2.2.3] - 2026-09-16
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **`mdka --version`.** It previously failed with
|
|
19
|
+
`error: IO error: No such file or directory`, because an unrecognised
|
|
20
|
+
`-`-prefixed argument was treated as a filename. `-V` works too.
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- **The CLI now rejects unknown options instead of treating them as
|
|
25
|
+
filenames.** `mdka --drop-shel page.html` used to exit 0 having converted
|
|
26
|
+
nothing; it now prints `error: unknown option '--drop-shel'` with the usage
|
|
27
|
+
text and exits 1.
|
|
28
|
+
|
|
29
|
+
**This can break an existing invocation.** A file whose name begins with `-`
|
|
30
|
+
was previously converted and is now rejected. Put `--` before it:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
mdka -- -weird.html
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
A bare `-` is still passed through as a path, unchanged.
|
|
37
|
+
|
|
38
|
+
### Fixed
|
|
39
|
+
|
|
40
|
+
- **The README's Node.js Quick Start did not parse.** It declared `const md`
|
|
41
|
+
twice, called `htmlToMarkdownWithAsync` without importing it, referenced an
|
|
42
|
+
undefined `html`, and used top-level `await` in a CommonJS example. Its Rust
|
|
43
|
+
and Python examples also used an undefined `html`. All of them now run as
|
|
44
|
+
written. This is the file rendered on GitHub, crates.io, npmjs.com and PyPI.
|
|
45
|
+
- **README links and the logo now resolve outside GitHub.** The logo path was
|
|
46
|
+
root-absolute and `./docs/`, `./CHANGELOG.md` and `./ROADMAP.md` are not in
|
|
47
|
+
the published npm tarball, so all four were broken on npmjs.com and the PyPI
|
|
48
|
+
project page. They are absolute URLs now, and CI fails if a relative link is
|
|
49
|
+
reintroduced.
|
|
50
|
+
- **The Python and Node.js guides documented two options that do not exist.**
|
|
51
|
+
`preserve_unknown_attrs` and `drop_presentation_attrs` are fields of the Rust
|
|
52
|
+
`ConversionOptions` but are not exposed by either binding: Python raises
|
|
53
|
+
`TypeError`, TypeScript reports `TS2353`. Three of the five deprecated
|
|
54
|
+
attribute options are reachable from the bindings, and the pages now say
|
|
55
|
+
which.
|
|
56
|
+
- **`mdka --help` no longer shows a multi-file example that fails.**
|
|
57
|
+
`mdka --mode minimal --drop-shell *.html` needs `-o` for more than one input,
|
|
58
|
+
two lines below the rule saying so. Fixed in `--help` and in the README.
|
|
59
|
+
- **`installation.md` no longer tells you to run `npm run build` on
|
|
60
|
+
unsupported platforms.** The published npm package contains four files and no
|
|
61
|
+
Rust source, so that could never work. It now names the three platforms with
|
|
62
|
+
prebuilt bindings and what is actually possible elsewhere.
|
|
63
|
+
- **The README now carries two caveats it previously omitted**: that
|
|
64
|
+
`Balanced`, `Strict` and `Preserve` currently produce identical output, and
|
|
65
|
+
that tables are not yet converted.
|
|
66
|
+
|
|
14
67
|
## [2.2.2] - 2026-09-16
|
|
15
68
|
|
|
16
69
|
### Fixed
|
|
@@ -1179,7 +1179,7 @@ dependencies = [
|
|
|
1179
1179
|
|
|
1180
1180
|
[[package]]
|
|
1181
1181
|
name = "mdka"
|
|
1182
|
-
version = "2.2.
|
|
1182
|
+
version = "2.2.3"
|
|
1183
1183
|
dependencies = [
|
|
1184
1184
|
"criterion",
|
|
1185
1185
|
"dom_smoothie",
|
|
@@ -1198,16 +1198,16 @@ dependencies = [
|
|
|
1198
1198
|
|
|
1199
1199
|
[[package]]
|
|
1200
1200
|
name = "mdka-cli"
|
|
1201
|
-
version = "2.2.
|
|
1201
|
+
version = "2.2.3"
|
|
1202
1202
|
dependencies = [
|
|
1203
|
-
"mdka 2.2.
|
|
1203
|
+
"mdka 2.2.3",
|
|
1204
1204
|
]
|
|
1205
1205
|
|
|
1206
1206
|
[[package]]
|
|
1207
1207
|
name = "mdka-node"
|
|
1208
|
-
version = "2.2.
|
|
1208
|
+
version = "2.2.3"
|
|
1209
1209
|
dependencies = [
|
|
1210
|
-
"mdka 2.2.
|
|
1210
|
+
"mdka 2.2.3",
|
|
1211
1211
|
"napi",
|
|
1212
1212
|
"napi-build",
|
|
1213
1213
|
"napi-derive",
|
|
@@ -1216,9 +1216,9 @@ dependencies = [
|
|
|
1216
1216
|
|
|
1217
1217
|
[[package]]
|
|
1218
1218
|
name = "mdka-python"
|
|
1219
|
-
version = "2.2.
|
|
1219
|
+
version = "2.2.3"
|
|
1220
1220
|
dependencies = [
|
|
1221
|
-
"mdka 2.2.
|
|
1221
|
+
"mdka 2.2.3",
|
|
1222
1222
|
"pyo3",
|
|
1223
1223
|
"rayon",
|
|
1224
1224
|
]
|
|
@@ -3,11 +3,11 @@ members = [".", "python"]
|
|
|
3
3
|
resolver = "2"
|
|
4
4
|
|
|
5
5
|
[workspace.dependencies]
|
|
6
|
-
mdka = { version = "2.2.
|
|
6
|
+
mdka = { version = "2.2.3", path = "." }
|
|
7
7
|
|
|
8
8
|
[package]
|
|
9
9
|
name = "mdka"
|
|
10
|
-
version = "2.2.
|
|
10
|
+
version = "2.2.3"
|
|
11
11
|
edition = "2024"
|
|
12
12
|
rust-version = "1.88"
|
|
13
13
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mdka
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.3
|
|
4
4
|
Classifier: Programming Language :: Rust
|
|
5
5
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
6
|
Classifier: License :: OSI Approved :: Apache Software License
|
|
@@ -25,7 +25,7 @@ Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
|
25
25
|
[](https://github.com/nabbisen/mdka-rs/actions/workflows/release-npm.yaml)
|
|
26
26
|
[](https://github.com/nabbisen/mdka-rs/actions/workflows/release-pypi.yaml)
|
|
27
27
|
|
|
28
|
-

|
|
28
|
+

|
|
29
29
|
|
|
30
30
|
mdka balances conversion quality with runtime efficiency —
|
|
31
31
|
readable output from real-world HTML, without sacrificing speed or memory.
|
|
@@ -49,7 +49,8 @@ CMS output, and SPA-rendered DOM without special-casing.
|
|
|
49
49
|
no matter the nesting depth.
|
|
50
50
|
- **Configurable pre-processing.**
|
|
51
51
|
Five [conversion modes](#conversion-modes) let you tune what gets kept or
|
|
52
|
-
stripped
|
|
52
|
+
stripped, from noise-free LLM input to maximum retention. Three of the five
|
|
53
|
+
currently produce identical output — see [Conversion Modes](#conversion-modes).
|
|
53
54
|
- **Multi-language.**
|
|
54
55
|
The same Rust implementation is accessible from Node.js (napi-rs) and
|
|
55
56
|
Python (PyO3).
|
|
@@ -98,7 +99,7 @@ echo '<h1>Hello</h1><p><strong>world</strong></p>' | mdka
|
|
|
98
99
|
|
|
99
100
|
```bash
|
|
100
101
|
mdka page.html # → page.md (same directory)
|
|
101
|
-
mdka --mode minimal --drop-shell *.html
|
|
102
|
+
mdka --mode minimal --drop-shell -o out/ *.html # strip nav/header/footer
|
|
102
103
|
mdka --help # full option list
|
|
103
104
|
```
|
|
104
105
|
|
|
@@ -120,12 +121,14 @@ let md = html_to_markdown("<h1>Hello</h1><p><em>world</em></p>");
|
|
|
120
121
|
With options:
|
|
121
122
|
|
|
122
123
|
```rust
|
|
123
|
-
use mdka::
|
|
124
|
+
use mdka::html_to_markdown_with;
|
|
124
125
|
use mdka::options::{ConversionMode, ConversionOptions};
|
|
125
126
|
|
|
127
|
+
let html = "<nav>menu</nav><h1>Hello</h1>";
|
|
126
128
|
let mut opts = ConversionOptions::for_mode(ConversionMode::Minimal);
|
|
127
129
|
opts.drop_interactive_shell = true;
|
|
128
130
|
let md = html_to_markdown_with(html, &opts);
|
|
131
|
+
// "# Hello\n"
|
|
129
132
|
```
|
|
130
133
|
|
|
131
134
|
### Add to a Node.js project
|
|
@@ -135,14 +138,20 @@ npm install mdka
|
|
|
135
138
|
```
|
|
136
139
|
|
|
137
140
|
```js
|
|
138
|
-
const { htmlToMarkdown,
|
|
141
|
+
const { htmlToMarkdown, htmlToMarkdownWithAsync } = require('mdka')
|
|
139
142
|
|
|
140
143
|
const md = htmlToMarkdown('<h1>Hello</h1>')
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
144
|
+
// "# Hello\n"
|
|
145
|
+
|
|
146
|
+
async function main() {
|
|
147
|
+
const html = '<nav>menu</nav><h1>Hello</h1>'
|
|
148
|
+
const minimal = await htmlToMarkdownWithAsync(html, {
|
|
149
|
+
mode: 'minimal',
|
|
150
|
+
dropInteractiveShell: true,
|
|
151
|
+
})
|
|
152
|
+
console.log(minimal)
|
|
153
|
+
}
|
|
154
|
+
main()
|
|
146
155
|
```
|
|
147
156
|
|
|
148
157
|
### Add to a Python project
|
|
@@ -155,8 +164,10 @@ pip install mdka
|
|
|
155
164
|
import mdka
|
|
156
165
|
|
|
157
166
|
md = mdka.html_to_markdown('<h1>Hello</h1>')
|
|
167
|
+
# "# Hello\n"
|
|
158
168
|
|
|
159
|
-
|
|
169
|
+
html = '<nav>menu</nav><h1>Hello</h1>'
|
|
170
|
+
minimal = mdka.html_to_markdown_with(
|
|
160
171
|
html,
|
|
161
172
|
mode=mdka.ConversionMode.Minimal,
|
|
162
173
|
drop_interactive_shell=True,
|
|
@@ -175,11 +186,24 @@ md = mdka.html_to_markdown_with(
|
|
|
175
186
|
| `Semantic` | SPA content, ARIA-aware pipelines |
|
|
176
187
|
| `Preserve` | Archiving, audit trails |
|
|
177
188
|
|
|
189
|
+
**`Balanced`, `Strict` and `Preserve` currently produce identical output.** They
|
|
190
|
+
differ only in the defaults of five fields that have no effect, so choosing
|
|
191
|
+
between them changes nothing today. They remain distinct API and may diverge
|
|
192
|
+
again — see
|
|
193
|
+
[Conversion Modes](https://nabbisen.github.io/mdka-rs/api/modes), which explains
|
|
194
|
+
why in full.
|
|
195
|
+
|
|
196
|
+
**Tables are not yet converted.** `<table>` cell text is emitted without
|
|
197
|
+
structure or separators, so a table becomes a run of joined text. See
|
|
198
|
+
[Supported Elements](https://nabbisen.github.io/mdka-rs/api/elements) for the
|
|
199
|
+
full list of what is and is not supported.
|
|
200
|
+
|
|
178
201
|
---
|
|
179
202
|
|
|
180
203
|
## Learn More
|
|
181
204
|
|
|
182
|
-
Full documentation
|
|
205
|
+
Full documentation is published as GitHub Pages, and its source lives in
|
|
206
|
+
[`docs/`](https://github.com/nabbisen/mdka-rs/tree/main/docs).
|
|
183
207
|
|
|
184
208
|
https://nabbisen.github.io/mdka-rs/
|
|
185
209
|
|
|
@@ -198,8 +222,8 @@ https://nabbisen.github.io/mdka-rs/
|
|
|
198
222
|
| Performance Characteristics | [/design/performance-characteristics](https://nabbisen.github.io/mdka-rs/design/performance-characteristics) |
|
|
199
223
|
| Architecture | [/design/architecture](https://nabbisen.github.io/mdka-rs/design/architecture) |
|
|
200
224
|
| Features | [/design/features](https://nabbisen.github.io/mdka-rs/design/features) |
|
|
201
|
-
| Changelog | [CHANGELOG.md](
|
|
202
|
-
| Roadmap | [ROADMAP.md](
|
|
225
|
+
| Changelog | [CHANGELOG.md](https://github.com/nabbisen/mdka-rs/blob/main/CHANGELOG.md) |
|
|
226
|
+
| Roadmap | [ROADMAP.md](https://github.com/nabbisen/mdka-rs/blob/main/ROADMAP.md) |
|
|
203
227
|
|
|
204
228
|
---
|
|
205
229
|
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
[](https://github.com/nabbisen/mdka-rs/actions/workflows/release-npm.yaml)
|
|
14
14
|
[](https://github.com/nabbisen/mdka-rs/actions/workflows/release-pypi.yaml)
|
|
15
15
|
|
|
16
|
-

|
|
16
|
+

|
|
17
17
|
|
|
18
18
|
mdka balances conversion quality with runtime efficiency —
|
|
19
19
|
readable output from real-world HTML, without sacrificing speed or memory.
|
|
@@ -37,7 +37,8 @@ CMS output, and SPA-rendered DOM without special-casing.
|
|
|
37
37
|
no matter the nesting depth.
|
|
38
38
|
- **Configurable pre-processing.**
|
|
39
39
|
Five [conversion modes](#conversion-modes) let you tune what gets kept or
|
|
40
|
-
stripped
|
|
40
|
+
stripped, from noise-free LLM input to maximum retention. Three of the five
|
|
41
|
+
currently produce identical output — see [Conversion Modes](#conversion-modes).
|
|
41
42
|
- **Multi-language.**
|
|
42
43
|
The same Rust implementation is accessible from Node.js (napi-rs) and
|
|
43
44
|
Python (PyO3).
|
|
@@ -86,7 +87,7 @@ echo '<h1>Hello</h1><p><strong>world</strong></p>' | mdka
|
|
|
86
87
|
|
|
87
88
|
```bash
|
|
88
89
|
mdka page.html # → page.md (same directory)
|
|
89
|
-
mdka --mode minimal --drop-shell *.html
|
|
90
|
+
mdka --mode minimal --drop-shell -o out/ *.html # strip nav/header/footer
|
|
90
91
|
mdka --help # full option list
|
|
91
92
|
```
|
|
92
93
|
|
|
@@ -108,12 +109,14 @@ let md = html_to_markdown("<h1>Hello</h1><p><em>world</em></p>");
|
|
|
108
109
|
With options:
|
|
109
110
|
|
|
110
111
|
```rust
|
|
111
|
-
use mdka::
|
|
112
|
+
use mdka::html_to_markdown_with;
|
|
112
113
|
use mdka::options::{ConversionMode, ConversionOptions};
|
|
113
114
|
|
|
115
|
+
let html = "<nav>menu</nav><h1>Hello</h1>";
|
|
114
116
|
let mut opts = ConversionOptions::for_mode(ConversionMode::Minimal);
|
|
115
117
|
opts.drop_interactive_shell = true;
|
|
116
118
|
let md = html_to_markdown_with(html, &opts);
|
|
119
|
+
// "# Hello\n"
|
|
117
120
|
```
|
|
118
121
|
|
|
119
122
|
### Add to a Node.js project
|
|
@@ -123,14 +126,20 @@ npm install mdka
|
|
|
123
126
|
```
|
|
124
127
|
|
|
125
128
|
```js
|
|
126
|
-
const { htmlToMarkdown,
|
|
129
|
+
const { htmlToMarkdown, htmlToMarkdownWithAsync } = require('mdka')
|
|
127
130
|
|
|
128
131
|
const md = htmlToMarkdown('<h1>Hello</h1>')
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
132
|
+
// "# Hello\n"
|
|
133
|
+
|
|
134
|
+
async function main() {
|
|
135
|
+
const html = '<nav>menu</nav><h1>Hello</h1>'
|
|
136
|
+
const minimal = await htmlToMarkdownWithAsync(html, {
|
|
137
|
+
mode: 'minimal',
|
|
138
|
+
dropInteractiveShell: true,
|
|
139
|
+
})
|
|
140
|
+
console.log(minimal)
|
|
141
|
+
}
|
|
142
|
+
main()
|
|
134
143
|
```
|
|
135
144
|
|
|
136
145
|
### Add to a Python project
|
|
@@ -143,8 +152,10 @@ pip install mdka
|
|
|
143
152
|
import mdka
|
|
144
153
|
|
|
145
154
|
md = mdka.html_to_markdown('<h1>Hello</h1>')
|
|
155
|
+
# "# Hello\n"
|
|
146
156
|
|
|
147
|
-
|
|
157
|
+
html = '<nav>menu</nav><h1>Hello</h1>'
|
|
158
|
+
minimal = mdka.html_to_markdown_with(
|
|
148
159
|
html,
|
|
149
160
|
mode=mdka.ConversionMode.Minimal,
|
|
150
161
|
drop_interactive_shell=True,
|
|
@@ -163,11 +174,24 @@ md = mdka.html_to_markdown_with(
|
|
|
163
174
|
| `Semantic` | SPA content, ARIA-aware pipelines |
|
|
164
175
|
| `Preserve` | Archiving, audit trails |
|
|
165
176
|
|
|
177
|
+
**`Balanced`, `Strict` and `Preserve` currently produce identical output.** They
|
|
178
|
+
differ only in the defaults of five fields that have no effect, so choosing
|
|
179
|
+
between them changes nothing today. They remain distinct API and may diverge
|
|
180
|
+
again — see
|
|
181
|
+
[Conversion Modes](https://nabbisen.github.io/mdka-rs/api/modes), which explains
|
|
182
|
+
why in full.
|
|
183
|
+
|
|
184
|
+
**Tables are not yet converted.** `<table>` cell text is emitted without
|
|
185
|
+
structure or separators, so a table becomes a run of joined text. See
|
|
186
|
+
[Supported Elements](https://nabbisen.github.io/mdka-rs/api/elements) for the
|
|
187
|
+
full list of what is and is not supported.
|
|
188
|
+
|
|
166
189
|
---
|
|
167
190
|
|
|
168
191
|
## Learn More
|
|
169
192
|
|
|
170
|
-
Full documentation
|
|
193
|
+
Full documentation is published as GitHub Pages, and its source lives in
|
|
194
|
+
[`docs/`](https://github.com/nabbisen/mdka-rs/tree/main/docs).
|
|
171
195
|
|
|
172
196
|
https://nabbisen.github.io/mdka-rs/
|
|
173
197
|
|
|
@@ -186,8 +210,8 @@ https://nabbisen.github.io/mdka-rs/
|
|
|
186
210
|
| Performance Characteristics | [/design/performance-characteristics](https://nabbisen.github.io/mdka-rs/design/performance-characteristics) |
|
|
187
211
|
| Architecture | [/design/architecture](https://nabbisen.github.io/mdka-rs/design/architecture) |
|
|
188
212
|
| Features | [/design/features](https://nabbisen.github.io/mdka-rs/design/features) |
|
|
189
|
-
| Changelog | [CHANGELOG.md](
|
|
190
|
-
| Roadmap | [ROADMAP.md](
|
|
213
|
+
| Changelog | [CHANGELOG.md](https://github.com/nabbisen/mdka-rs/blob/main/CHANGELOG.md) |
|
|
214
|
+
| Roadmap | [ROADMAP.md](https://github.com/nabbisen/mdka-rs/blob/main/ROADMAP.md) |
|
|
191
215
|
|
|
192
216
|
---
|
|
193
217
|
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
# mdka — Roadmap
|
|
2
2
|
|
|
3
3
|
**Status.** Active — planning baseline approved by the project owner on 2026-08-02.
|
|
4
|
-
**Current version.** 2.2.
|
|
5
|
-
**
|
|
6
|
-
|
|
4
|
+
**Current version.** 2.2.3 (released 2026-09-16)
|
|
5
|
+
**Current version note.** `2.2.1` shipped RFC 020; `2.2.2` shipped RFC 007, 021,
|
|
6
|
+
022, 023, 026 and 027; `2.2.3` shipped RFC 029.
|
|
7
|
+
**Milestone progress.** M1, M1b, M2, M2b and **M2c complete**. **M3 is next.**
|
|
7
8
|
**Governance.** RFC lifecycle follows [RFC 000](./rfcs/done/000-rfc-lifecycle-policy.md).
|
|
8
9
|
|
|
9
10
|
This document is the planning baseline from which the RFC portfolio is derived.
|
|
@@ -382,6 +383,49 @@ These gates narrow what the consumer pass has to catch; they do not replace it.
|
|
|
382
383
|
**Do not describe this set as complete coverage.** Four green checkmarks mean
|
|
383
384
|
the Linux artifacts install and the documented examples resolve — nothing more.
|
|
384
385
|
|
|
386
|
+
### M2c · Published-surface repair → `2.2.3` (patch) — ✅ COMPLETE
|
|
387
|
+
|
|
388
|
+
From the **first consumer pass** (RFC 027 Rule 1), run against published `2.2.2`
|
|
389
|
+
by a session with no history of this project. Disposition:
|
|
390
|
+
`.git-exclude/reviewed/2.2.2-consumer-pass/README.md`.
|
|
391
|
+
|
|
392
|
+
| RFC | Title | Priority | Size |
|
|
393
|
+
|---|---|---|---|
|
|
394
|
+
| 029 | Published-surface documentation repair | **P0** | S |
|
|
395
|
+
|
|
396
|
+
**Why a patch of its own.** The README's Node Quick Start does not parse, and it
|
|
397
|
+
renders on GitHub, crates.io, npm and PyPI. `usage-python.md` documents a keyword
|
|
398
|
+
argument that raises `TypeError`. Both are live; neither should wait behind M3's
|
|
399
|
+
renderer work.
|
|
400
|
+
|
|
401
|
+
#### What the consumer pass exposed about our controls
|
|
402
|
+
|
|
403
|
+
**`README.md` has never been in scope for any documentation RFC.** RFC 023's
|
|
404
|
+
boundary was `docs/src/getting-started/`; RFC 007's was six source files. The
|
|
405
|
+
most-read file in the project sat outside every one — **the third scope-boundary
|
|
406
|
+
miss this milestone**, after RFC 022's `examples/` consumers and RFC 007's count
|
|
407
|
+
table.
|
|
408
|
+
|
|
409
|
+
RFC 027 Rule 2 now requires boundaries be derived from a search. **That is
|
|
410
|
+
necessary and was not sufficient**: a search only covers what you think to
|
|
411
|
+
search for.
|
|
412
|
+
|
|
413
|
+
**The docs-example gate could not see it either** — `--root` defaults to
|
|
414
|
+
`docs/src`. And it checks symbol resolution, not keyword arguments, so the
|
|
415
|
+
Python defect was invisible twice over. Both recorded as corrections on RFC 026;
|
|
416
|
+
both fixed by RFC 029.
|
|
417
|
+
|
|
418
|
+
**One finding went to M3, not here.** F-04 — link text silently losing spaces
|
|
419
|
+
inside `<a>` — shares RFC 024's root cause but has a worse symptom: `****[b](/x)`
|
|
420
|
+
is visibly wrong, `[Readmore now]` reads as prose with a word gone. Added to
|
|
421
|
+
RFC 024 as an explicit acceptance criterion so it cannot be fixed by accident.
|
|
422
|
+
|
|
423
|
+
**Recorded, not scheduled:** F-24, deep nesting is quadratic — 25k depth 0.72s,
|
|
424
|
+
100k 10.5s, 300k 255s. The README's "no stack overflow at any depth" claim holds;
|
|
425
|
+
the failure mode moved from crash to hang. M4. And F-23, default `Balanced`
|
|
426
|
+
emitting 1,190 `<a id>` anchors on a real Wikipedia page — a design question
|
|
427
|
+
shared with bekoedit's item 8, not a defect.
|
|
428
|
+
|
|
385
429
|
### M3 · Conversion fidelity → `2.3.0` (minor)
|
|
386
430
|
|
|
387
431
|
Purely additive element coverage. Tables are the largest known gap against the
|