flexdoc 0.2.0__tar.gz → 0.3.0__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.
- {flexdoc-0.2.0 → flexdoc-0.3.0}/.claude/scripts/ensure-gh-cli.sh +1 -1
- {flexdoc-0.2.0 → flexdoc-0.3.0}/.codex/ensure-gh-cli.sh +1 -1
- flexdoc-0.3.0/.github/release-request/notes.md +49 -0
- flexdoc-0.3.0/.github/release-request/request.json +3 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/.github/workflows/ci.yml +14 -14
- {flexdoc-0.2.0 → flexdoc-0.3.0}/.github/workflows/publish.yml +1 -0
- flexdoc-0.3.0/.github/workflows/release.yml +54 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/.tbd/config.yml +23 -16
- {flexdoc-0.2.0 → flexdoc-0.3.0}/AGENTS.md +1 -1
- flexdoc-0.3.0/CHANGELOG.md +239 -0
- flexdoc-0.3.0/PKG-INFO +170 -0
- flexdoc-0.3.0/README.md +132 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/SUPPLY-CHAIN-SECURITY.md +19 -45
- {flexdoc-0.2.0 → flexdoc-0.3.0}/TODO.md +16 -9
- {flexdoc-0.2.0 → flexdoc-0.3.0}/devtools/lint.py +5 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/docs/development.md +7 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/docs/flexdoc-spec.md +277 -224
- flexdoc-0.3.0/docs/project/drafts/draft-2026-07-flexdoc-intro-post.md +140 -0
- flexdoc-0.3.0/docs/project/research/research-2026-07-10-text-reference-microformat.md +845 -0
- flexdoc-0.3.0/docs/project/review/senior-engineering-review-flexdoc-2026-07.md +366 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/docs/project/review/senior-engineering-review-flexdoc-standalone-2026-06.md +36 -36
- {flexdoc-0.2.0 → flexdoc-0.3.0}/docs/project/specs/active/plan-2026-05-29-unified-document-model.md +8 -8
- {flexdoc-0.2.0 → flexdoc-0.3.0}/docs/project/specs/active/plan-2026-06-11-flexdoc-extraction.md +97 -89
- {flexdoc-0.2.0 → flexdoc-0.3.0}/docs/project/specs/active/plan-2026-06-11-structural-metadata.md +3 -3
- flexdoc-0.3.0/docs/project/specs/active/plan-2026-06-13-metrics-use-case.md +561 -0
- flexdoc-0.3.0/docs/project/specs/active/plan-2026-07-08-post-review-refinements.md +169 -0
- flexdoc-0.3.0/docs/project/specs/active/plan-2026-07-09-flexdoc-stabilization-roadmap.md +255 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/docs/publishing.md +45 -11
- {flexdoc-0.2.0 → flexdoc-0.3.0}/docs/usage.md +48 -15
- {flexdoc-0.2.0 → flexdoc-0.3.0}/examples/backfill_timestamps.py +12 -1
- {flexdoc-0.2.0 → flexdoc-0.3.0}/examples/doc_structure.py +6 -2
- {flexdoc-0.2.0 → flexdoc-0.3.0}/examples/normalized_form.py +2 -1
- {flexdoc-0.2.0 → flexdoc-0.3.0}/pyproject.toml +20 -17
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/__init__.py +8 -5
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/docs/__init__.py +3 -83
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/docs/base_blocks.py +1 -1
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/docs/block_info.py +5 -5
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/docs/block_tree.py +11 -9
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/docs/collect.py +13 -11
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/docs/debug.py +2 -2
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/docs/doc_graph.py +7 -2
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/docs/doc_graph_schema.json +1 -1
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/docs/flex_doc.py +62 -69
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/docs/frontmatter.py +23 -6
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/docs/links.py +2 -2
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/docs/node_table.py +2 -1
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/docs/paragraphs.py +47 -1
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/docs/render.py +43 -1
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/docs/sections.py +32 -17
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/docs/sizes.py +2 -2
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/docs/span_ref.py +101 -30
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/docs/test_block_info.py +3 -2
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/docs/test_blocks.py +27 -7
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/docs/test_caching_threadsafe.py +21 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/docs/test_collect.py +20 -5
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/docs/test_frontmatter.py +43 -4
- flexdoc-0.3.0/tests/docs/test_line_endings.py +86 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/docs/test_links.py +8 -1
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/docs/test_sections.py +27 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/docs/test_span_ref.py +126 -18
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/docs/test_spans.py +1 -1
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/README.md +13 -9
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/documents/footnotes_refs.md +11 -2
- flexdoc-0.3.0/tests/golden/documents/preamble_only.md +13 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/expected/footnotes_refs/docgraph.yaml +17 -10
- flexdoc-0.3.0/tests/golden/expected/footnotes_refs/reassembled.md +15 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/expected/footnotes_refs/report.yaml +120 -64
- flexdoc-0.3.0/tests/golden/expected/preamble_only/docgraph.yaml +83 -0
- flexdoc-0.3.0/tests/golden/expected/preamble_only/reassembled.md +5 -0
- flexdoc-0.3.0/tests/golden/expected/preamble_only/report.yaml +141 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/test_golden_docs.py +3 -3
- flexdoc-0.3.0/tests/test_root_api.py +115 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/uv.lock +170 -165
- flexdoc-0.2.0/CHANGELOG.md +0 -135
- flexdoc-0.2.0/PKG-INFO +0 -101
- flexdoc-0.2.0/README.md +0 -69
- flexdoc-0.2.0/docs/project/specs/active/plan-2026-06-13-metrics-use-case.md +0 -531
- flexdoc-0.2.0/tests/golden/expected/footnotes_refs/reassembled.md +0 -9
- flexdoc-0.2.0/tests/test_root_api.py +0 -53
- {flexdoc-0.2.0 → flexdoc-0.3.0}/.agents/skills/simple-modern-uv/SKILL.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/.agents/skills/simple-modern-uv/references/adopt-existing.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/.agents/skills/simple-modern-uv/references/customize.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/.agents/skills/simple-modern-uv/references/faq.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/.agents/skills/tbd/SKILL.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/.claude/.gitignore +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/.claude/hooks/tbd-closing-reminder.sh +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/.claude/scripts/tbd-session.sh +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/.claude/settings.json +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/.claude/skills/tbd/SKILL.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/.codex/hooks.json +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/.codex/tbd-closing-reminder.sh +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/.codex/tbd-session.sh +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/.copier-answers.yml +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/.gitignore +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/.tbd/.gitattributes +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/.tbd/.gitignore +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/CLAUDE.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/LICENSE +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/Makefile +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/docs/installation.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/docs/project/research/research-2026-05-29-document-model.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/docs/project/research/research-2026-05-30-multilayer-parsing.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/docs/project/research/research-2026-05-30-span-references.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/docs/project/specs/active/plan-2026-05-31-doc-model-refinements.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/docs/project/specs/active/plan-2026-05-31-golden-doc-testing.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/skills-lock.json +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/docs/block_types.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/docs/interval_index.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/docs/node.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/docs/search_tokens.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/docs/token_diffs.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/docs/token_mapping.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/docs/wordtoks.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/html/__init__.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/html/extractor.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/html/html_in_md.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/html/html_plaintext.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/html/html_tags.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/html/timestamps.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/py.typed +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/util/__init__.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/util/read_time.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/src/flexdoc/util/token_estimate.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/__init__.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/docs/__init__.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/docs/test_base_blocks.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/docs/test_block_types.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/docs/test_doc_graph.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/docs/test_flex_doc.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/docs/test_footnote_ref.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/docs/test_interval_index.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/docs/test_node.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/docs/test_node_table.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/docs/test_offsets.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/docs/test_subdoc_and_empty.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/docs/test_token_diffs.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/docs/test_token_mapping.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/docs/test_token_validation.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/docs/test_wordtoks.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/documents/heading_edges.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/documents/inline_html.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/documents/inline_pathology.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/documents/kitchen_sink.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/documents/link_taxonomy.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/documents/malformed.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/documents/nested_list.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/documents/tight_vs_loose.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/documents/unicode.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/expected/heading_edges/docgraph.yaml +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/expected/heading_edges/reassembled.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/expected/heading_edges/report.yaml +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/expected/inline_html/docgraph.yaml +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/expected/inline_html/reassembled.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/expected/inline_html/report.yaml +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/expected/inline_pathology/docgraph.yaml +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/expected/inline_pathology/reassembled.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/expected/inline_pathology/report.yaml +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/expected/kitchen_sink/docgraph.yaml +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/expected/kitchen_sink/reassembled.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/expected/kitchen_sink/report.yaml +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/expected/link_taxonomy/docgraph.yaml +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/expected/link_taxonomy/reassembled.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/expected/link_taxonomy/report.yaml +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/expected/malformed/docgraph.yaml +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/expected/malformed/reassembled.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/expected/malformed/report.yaml +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/expected/nested_list/docgraph.yaml +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/expected/nested_list/reassembled.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/expected/nested_list/report.yaml +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/expected/tight_vs_loose/docgraph.yaml +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/expected/tight_vs_loose/reassembled.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/expected/tight_vs_loose/report.yaml +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/expected/unicode/docgraph.yaml +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/expected/unicode/reassembled.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/golden/expected/unicode/report.yaml +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/html/__init__.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/html/test_html_tag_hardening.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/html/test_html_tags.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/html/test_html_validation_and_classes.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/html/test_timestamps.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/test_examples.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.3.0}/tests/test_supply_chain.py +0 -0
|
@@ -117,7 +117,7 @@ if [ -n "${GH_TOKEN:-}" ]; then
|
|
|
117
117
|
fi
|
|
118
118
|
else
|
|
119
119
|
echo "[gh] NOTE: GH_TOKEN not set - some operations may require authentication"
|
|
120
|
-
echo "[gh] See: docs/
|
|
120
|
+
echo "[gh] See: docs/development.md"
|
|
121
121
|
fi
|
|
122
122
|
|
|
123
123
|
exit 0
|
|
@@ -117,7 +117,7 @@ if [ -n "${GH_TOKEN:-}" ]; then
|
|
|
117
117
|
fi
|
|
118
118
|
else
|
|
119
119
|
echo "[gh] NOTE: GH_TOKEN not set - some operations may require authentication"
|
|
120
|
-
echo "[gh] See: docs/
|
|
120
|
+
echo "[gh] See: docs/development.md"
|
|
121
121
|
fi
|
|
122
122
|
|
|
123
123
|
exit 0
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
## What's Changed
|
|
2
|
+
|
|
3
|
+
This release stabilizes the pre-1.0 API boundary from the 2026-07 pre-promotion design review ([senior engineering review](https://github.com/jlevy/flexdoc/blob/main/docs/project/review/senior-engineering-review-flexdoc-2026-07.md), PRs #9 and #10). It fixes correctness bugs in source-coordinate handling and anchoring, and settles the remaining pre-1.0 API decisions so later releases can add annotation and synthetic-layer mechanisms without reopening the foundation. Because it changes documented behavior on a pre-1.0 library, it bumps the minor version (per the pre-1.0 rule in `docs/publishing.md`).
|
|
4
|
+
|
|
5
|
+
### Bug Fixes
|
|
6
|
+
|
|
7
|
+
**CRLF input no longer corrupts the structural views**
|
|
8
|
+
|
|
9
|
+
marko computes block positions against LF-only text, so `\r` in the input desynchronized every structural span (`blocks()`, `sections()`, `base_blocks()`, `links()`, `prose_text()`, the node table) from `source_text`, silently garbling content. `from_text` now normalizes `\r\n` and lone `\r` to `\n` and retains the normalized string as `source_text`, so all layers share one offset space. Callers anchoring offsets to an external CRLF original must normalize it the same way first.
|
|
10
|
+
|
|
11
|
+
**Markdown constructs inside frontmatter can no longer swallow the body**
|
|
12
|
+
|
|
13
|
+
The shared parse previously included the frontmatter region, so e.g. a YAML block scalar containing a code fence opened a fenced block spanning the rest of the document, leaving `blocks()` empty. The frontmatter region is now blanked out of the shared parse (offsets preserved); frontmatter remains a non-content region.
|
|
14
|
+
|
|
15
|
+
**`resolve()` no longer guesses on ambiguous quotes**
|
|
16
|
+
|
|
17
|
+
A `SpanRef` quote that occurs multiple times with no disambiguating prefix/suffix (or a tied context score) now resolves to `None` instead of silently anchoring to the first occurrence. A context-free position hint also cannot select a duplicate, and a zero-width quote (`exact=""`) resolves to `None`.
|
|
18
|
+
|
|
19
|
+
**`collect(overlaps=...)` treats empty intervals as empty**
|
|
20
|
+
|
|
21
|
+
A degenerate `[x, x)` region now overlaps nothing, matching half-open interval semantics; point queries use `(x, x + 1)`.
|
|
22
|
+
|
|
23
|
+
**Render helpers harden their HTML output**
|
|
24
|
+
|
|
25
|
+
`render_node_attrs` attribute-escapes `node.id`, and `wrap_with_node_attrs` validates the tag name (raising `ValueError`), matching the `flexdoc.html` tag helpers.
|
|
26
|
+
|
|
27
|
+
### Breaking Changes
|
|
28
|
+
|
|
29
|
+
Made cleanly with no compatibility aliases, given the preview (pre-1.0) status:
|
|
30
|
+
|
|
31
|
+
- **`SpanRef` owns its public resolution API** — call `ref.resolve(source_text)` / `ref.resolve_and_update(source_text)`; the generic `resolve` / `resolve_and_update` names are no longer promoted from `flexdoc.docs`.
|
|
32
|
+
- **`flexdoc.docs` now promotes the document model only** — word-token/search and diff/mapping names moved to their owning modules (`flexdoc.docs.wordtoks`, `search_tokens`, `token_diffs`, `token_mapping`).
|
|
33
|
+
- **Recursive `collect()` includes inline descendants by default** — `inline` is now tri-state; pass `inline=False` for the previous block-only recursive result.
|
|
34
|
+
- **`Paragraph.heading_level` / `Paragraph.heading_title` are properties** — remove the `()` from calls.
|
|
35
|
+
- **`NAVIGABLE_LINK_FORMS` replaces `TRUE_LINK_FORMS`** — no alias retained.
|
|
36
|
+
- **`TextUnit` is a `StrEnum`** — `TextUnit.words == "words"` now holds; only `str()`/equality behavior changes.
|
|
37
|
+
- **Cached structural views are mutation-safe** — `Block` is frozen, `Block.children` and `TableInfo.alignments` are tuples, and `sections()` returns isolated copies; direct `Block` construction must pass child tuples.
|
|
38
|
+
|
|
39
|
+
### Other Changes
|
|
40
|
+
|
|
41
|
+
- `graph()` accepts any `Set` for `include`/`detail`, so plain `set` literals type-check.
|
|
42
|
+
- Frontmatter delimiters tolerate trailing horizontal whitespace.
|
|
43
|
+
- The dependency lock is refreshed under the 14-day cool-off (cutoff 2026-06-26) with no per-package exceptions or audit ignores; CI runs `pip-audit` clean.
|
|
44
|
+
- The OS-independent classifier is backed by a macOS CI job (Python 3.13 full lint/test gate on `macos-latest`).
|
|
45
|
+
- Local release preparation is tag-aware, preventing a tagless clone from producing a `0.0.1.devN` artifact.
|
|
46
|
+
|
|
47
|
+
### Full Changelog
|
|
48
|
+
|
|
49
|
+
https://github.com/jlevy/flexdoc/compare/v0.2.0...v0.3.0
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Install locked dependencies, lint, and test across every supported Python version on
|
|
2
|
+
# Linux plus one representative macOS job for the OS-independent package contract.
|
|
2
3
|
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python
|
|
3
4
|
|
|
4
5
|
name: CI
|
|
@@ -18,12 +19,18 @@ jobs:
|
|
|
18
19
|
build:
|
|
19
20
|
strategy:
|
|
20
21
|
matrix:
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
22
|
+
include:
|
|
23
|
+
- os: ubuntu-latest
|
|
24
|
+
python-version: "3.11"
|
|
25
|
+
- os: ubuntu-latest
|
|
26
|
+
python-version: "3.12"
|
|
27
|
+
- os: ubuntu-latest
|
|
28
|
+
python-version: "3.13"
|
|
29
|
+
- os: ubuntu-latest
|
|
30
|
+
python-version: "3.14"
|
|
31
|
+
- os: macos-latest
|
|
32
|
+
python-version: "3.13"
|
|
25
33
|
|
|
26
|
-
# Linux only by default. Use ${{ matrix.os }} for other OSes.
|
|
27
34
|
runs-on: ${{ matrix.os }}
|
|
28
35
|
|
|
29
36
|
steps:
|
|
@@ -91,15 +98,8 @@ jobs:
|
|
|
91
98
|
version: "0.11.12"
|
|
92
99
|
enable-cache: true
|
|
93
100
|
|
|
94
|
-
# --ignore-vuln PYSEC-2026-196: a CVE in `pip` itself, which is only present as a
|
|
95
|
-
# transitive dependency of the pip-audit tool (audit group), not a flexdoc
|
|
96
|
-
# runtime/dev dependency, and is never shipped in the flexdoc wheel. Its fix
|
|
97
|
-
# (pip 26.1.2) is newer than the [tool.uv] exclude-newer cool-off cutoff, so there
|
|
98
|
-
# is no within-policy bump; this ignore does not touch dependency resolution or the
|
|
99
|
-
# cool-off. Remove it once the cutoff advances past pip 26.1.2's release. See
|
|
100
|
-
# SUPPLY-CHAIN-SECURITY.md (Audit-Gate Ignores).
|
|
101
101
|
- name: Audit dependencies for known vulnerabilities
|
|
102
|
-
run: uv run --frozen --group audit pip-audit
|
|
102
|
+
run: uv run --frozen --group audit pip-audit
|
|
103
103
|
|
|
104
104
|
# Packaging check: the flexdoc wheel must import cleanly from an isolated install, not
|
|
105
105
|
# just from the source tree. Guards against missing package data or accidental reliance
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
name: Create Release
|
|
2
|
+
|
|
3
|
+
# Release automation for operators that cannot push tags or dispatch workflows
|
|
4
|
+
# directly (e.g. remote agent sessions, which can only push their own working
|
|
5
|
+
# branch and merge PRs). To cut a release: update
|
|
6
|
+
# .github/release-request/request.json and notes.md, land the change on main
|
|
7
|
+
# via PR after the docs/publishing.md pre-release checklist passes, and the
|
|
8
|
+
# merge triggers this workflow. It creates the tag and GitHub Release at the
|
|
9
|
+
# merged commit, then calls publish.yml to build, test, and publish to PyPI.
|
|
10
|
+
#
|
|
11
|
+
# The release is created with GITHUB_TOKEN, whose events do not trigger other
|
|
12
|
+
# workflows, so publish.yml is invoked here as a reusable workflow instead of
|
|
13
|
+
# through its release-published trigger. PyPI trusted publishing validates the
|
|
14
|
+
# job_workflow_ref claim, which points at publish.yml for the called job, so
|
|
15
|
+
# the registered publisher still matches. Releases cut by a maintainer with
|
|
16
|
+
# gh release create (the runbook path) fire publish.yml directly and never
|
|
17
|
+
# run this workflow.
|
|
18
|
+
|
|
19
|
+
on:
|
|
20
|
+
push:
|
|
21
|
+
branches:
|
|
22
|
+
- main
|
|
23
|
+
paths:
|
|
24
|
+
- ".github/release-request/**"
|
|
25
|
+
|
|
26
|
+
jobs:
|
|
27
|
+
create-release:
|
|
28
|
+
runs-on: ubuntu-latest
|
|
29
|
+
permissions:
|
|
30
|
+
contents: write
|
|
31
|
+
steps:
|
|
32
|
+
- name: Checkout (official GitHub action)
|
|
33
|
+
# actions/checkout@v6
|
|
34
|
+
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10
|
|
35
|
+
|
|
36
|
+
- name: Create tag and release
|
|
37
|
+
env:
|
|
38
|
+
GH_TOKEN: ${{ github.token }}
|
|
39
|
+
run: |
|
|
40
|
+
TAG="$(jq -r .tag .github/release-request/request.json)"
|
|
41
|
+
case "$TAG" in
|
|
42
|
+
v[0-9]*) ;;
|
|
43
|
+
*) echo "Invalid tag in release request: $TAG" >&2; exit 1 ;;
|
|
44
|
+
esac
|
|
45
|
+
gh release create "$TAG" --repo "$GITHUB_REPOSITORY" \
|
|
46
|
+
--target "$GITHUB_SHA" --title "$TAG" \
|
|
47
|
+
--notes-file .github/release-request/notes.md
|
|
48
|
+
|
|
49
|
+
publish:
|
|
50
|
+
needs: create-release
|
|
51
|
+
permissions:
|
|
52
|
+
id-token: write
|
|
53
|
+
contents: read
|
|
54
|
+
uses: ./.github/workflows/publish.yml
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
-
tbd_format:
|
|
1
|
+
tbd_format: f06
|
|
2
2
|
tbd_version: 0.2.3
|
|
3
|
+
# tbd_upgrades: tbd versions that have run `tbd setup` in this repo (oldest first);
|
|
4
|
+
# tbd_version above is the most recent. Informational; updated automatically by setup.
|
|
5
|
+
tbd_upgrades:
|
|
6
|
+
- version: 0.2.3
|
|
3
7
|
display:
|
|
4
8
|
id_prefix: flexdoc
|
|
5
9
|
sync:
|
|
@@ -10,23 +14,20 @@ settings:
|
|
|
10
14
|
auto_sync: false
|
|
11
15
|
doc_auto_sync_hours: 24
|
|
12
16
|
use_gh_cli: true
|
|
13
|
-
# Documentation cache
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
# - internal
|
|
17
|
-
# -
|
|
18
|
-
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
# Auto-sync: Docs are automatically synced when stale (default: every 24 hours).
|
|
24
|
-
# Configure with settings.doc_auto_sync_hours (0 = disabled).
|
|
17
|
+
# Documentation cache: the docs tbd serves (guidelines, shortcuts, templates), synced
|
|
18
|
+
# into the gitignored .tbd/docs/ cache. Managed with `tbd docs` (run `tbd docs --help`).
|
|
19
|
+
# files: destination path (under .tbd/docs/) -> source docref. Common source forms:
|
|
20
|
+
# - internal:<path> bundled with tbd (e.g. internal:guidelines/typescript-rules.md)
|
|
21
|
+
# - github:owner/repo@ref//path/to/file.md a file in a git repo (gitlab: too; @ref optional)
|
|
22
|
+
# - https://host/path/file.md a plain URL, kept verbatim
|
|
23
|
+
# Full docref grammar: `tbd docs show docref-format`.
|
|
24
|
+
# lookup_path: doc lookup search order, like shell $PATH (earlier paths win).
|
|
25
|
+
# Refresh the cache with `tbd docs sync`; it also auto-syncs when stale
|
|
26
|
+
# (settings.doc_auto_sync_hours, default 24; 0 disables).
|
|
25
27
|
docs_cache:
|
|
26
|
-
lookup_path:
|
|
27
|
-
- .tbd/docs/shortcuts/system
|
|
28
|
-
- .tbd/docs/shortcuts/standard
|
|
29
28
|
files:
|
|
29
|
+
references/tbd-docs.md: internal:tbd-docs.md
|
|
30
|
+
references/tbd-design.md: internal:tbd-design.md
|
|
30
31
|
shortcuts/system/shortcut-explanation.md: internal:shortcuts/system/shortcut-explanation.md
|
|
31
32
|
shortcuts/system/skill-baseline.md: internal:shortcuts/system/skill-baseline.md
|
|
32
33
|
shortcuts/system/skill-brief.md: internal:shortcuts/system/skill-brief.md
|
|
@@ -58,6 +59,7 @@ docs_cache:
|
|
|
58
59
|
shortcuts/standard/revise-all-architecture-docs.md: internal:shortcuts/standard/revise-all-architecture-docs.md
|
|
59
60
|
shortcuts/standard/revise-architecture-doc.md: internal:shortcuts/standard/revise-architecture-doc.md
|
|
60
61
|
shortcuts/standard/setup-github-cli.md: internal:shortcuts/standard/setup-github-cli.md
|
|
62
|
+
shortcuts/standard/suggest-upstream-improvements.md: internal:shortcuts/standard/suggest-upstream-improvements.md
|
|
61
63
|
shortcuts/standard/sync-failure-recovery.md: internal:shortcuts/standard/sync-failure-recovery.md
|
|
62
64
|
shortcuts/standard/update-specs-status.md: internal:shortcuts/standard/update-specs-status.md
|
|
63
65
|
shortcuts/standard/welcome-user.md: internal:shortcuts/standard/welcome-user.md
|
|
@@ -92,3 +94,8 @@ docs_cache:
|
|
|
92
94
|
templates/plan-spec.md: internal:templates/plan-spec.md
|
|
93
95
|
templates/qa-playbook.md: internal:templates/qa-playbook.md
|
|
94
96
|
templates/research-brief.md: internal:templates/research-brief.md
|
|
97
|
+
references/docmap-format.md: internal:references/docmap-format.md
|
|
98
|
+
references/docref-format.md: internal:references/docref-format.md
|
|
99
|
+
lookup_path:
|
|
100
|
+
- .tbd/docs/shortcuts/system
|
|
101
|
+
- .tbd/docs/shortcuts/standard
|
|
@@ -364,7 +364,7 @@ Always use full type annotations, generics, and other modern practices.
|
|
|
364
364
|
- When changing code in a library or general function, if a change to an API or library
|
|
365
365
|
will break backward compatibility, MENTION THIS to the user.
|
|
366
366
|
|
|
367
|
-
- DO NOT implement additional code for backward
|
|
367
|
+
- DO NOT implement additional code for backward compatibility (such as extra methods or
|
|
368
368
|
variable aliases or comments about backward compatibility) UNLESS the user has
|
|
369
369
|
confirmed that it is necessary.
|
|
370
370
|
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to flexdoc are documented here.
|
|
4
|
+
This project uses [semantic versioning](https://semver.org/); while pre-1.0, breaking
|
|
5
|
+
changes bump the **minor** version (see `docs/publishing.md`).
|
|
6
|
+
|
|
7
|
+
## 0.3.0 (2026-07-11)
|
|
8
|
+
|
|
9
|
+
Fixes from the 2026-07 pre-promotion design review
|
|
10
|
+
(`docs/project/review/senior-engineering-review-flexdoc-2026-07.md`). These changes
|
|
11
|
+
alter documented behavior, so per the pre-1.0 rule this is a minor release, not a
|
|
12
|
+
0.2.x patch.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- **CRLF input no longer corrupts the structural views.** marko computes block positions
|
|
17
|
+
against LF-only text, so `\r` in the input desynchronized every structural span
|
|
18
|
+
(`blocks()`, `sections()`, `base_blocks()`, `links()`, `prose_text()`, the node table)
|
|
19
|
+
from `source_text`, silently garbling content and violating the base-block cover
|
|
20
|
+
invariant. `from_text` now normalizes `\r\n` and lone `\r` to `\n` and retains the
|
|
21
|
+
normalized string as `source_text`, so all layers share one offset space.
|
|
22
|
+
Callers anchoring offsets to an external CRLF original must normalize it the same way
|
|
23
|
+
first.
|
|
24
|
+
- **Markdown constructs inside frontmatter can no longer swallow the body.** The shared
|
|
25
|
+
parse previously included the frontmatter region, so e.g. a YAML block scalar
|
|
26
|
+
containing a code fence opened a fenced block spanning the rest of the document,
|
|
27
|
+
leaving `blocks()` empty.
|
|
28
|
+
The frontmatter region is now blanked out of the shared parse (offsets preserved);
|
|
29
|
+
frontmatter remains a non-content region.
|
|
30
|
+
Link extraction now reuses that blanked parse instead of reparsing the body for
|
|
31
|
+
frontmatter documents.
|
|
32
|
+
- **`resolve()` no longer guesses on ambiguous quotes.** Per the spec’s error posture
|
|
33
|
+
(§11), a `SpanRef` quote that occurs multiple times with no disambiguating
|
|
34
|
+
prefix/suffix (or a tied context score) now resolves to `None` instead of silently
|
|
35
|
+
anchoring to the first occurrence.
|
|
36
|
+
A context-free position hint also cannot select a duplicate, because no context or
|
|
37
|
+
source identity proves which occurrence was intended.
|
|
38
|
+
A zero-width quote (`exact=""`) also resolves to `None` on both the fast and slow
|
|
39
|
+
paths.
|
|
40
|
+
- **`collect(overlaps=...)` treats empty intervals as empty.** A degenerate `[x, x)`
|
|
41
|
+
region (or node span) now overlaps nothing, matching half-open interval semantics;
|
|
42
|
+
point queries use `(x, x + 1)`.
|
|
43
|
+
- **Render helpers harden their HTML output.** `render_node_attrs` attribute-escapes
|
|
44
|
+
`node.id`, and `wrap_with_node_attrs` validates the tag name (raising `ValueError`),
|
|
45
|
+
matching the validation in the `flexdoc.html` tag helpers.
|
|
46
|
+
|
|
47
|
+
### Changed
|
|
48
|
+
|
|
49
|
+
- **The dependency lock is refreshed under the 14-day cool-off.** The cutoff is
|
|
50
|
+
2026-06-26, expired per-package exceptions are removed, and the audit group resolves
|
|
51
|
+
fixed `pip` and `msgpack` versions, so CI runs `pip-audit` without advisory ignores.
|
|
52
|
+
- **`graph()` accepts any set.** `FlexDoc.graph()` and `build_doc_graph()` annotate
|
|
53
|
+
`include`/`detail` as `collections.abc.Set`, so plain `set` literals type-check
|
|
54
|
+
(matching `collect()`); behavior is unchanged.
|
|
55
|
+
- **`TextUnit` is a `StrEnum`**, matching every other public enum, so
|
|
56
|
+
`TextUnit.words == "words"` now holds.
|
|
57
|
+
Source-compatible for enum-member access; only `str()`/equality-with-string behavior
|
|
58
|
+
changes.
|
|
59
|
+
- **Recursive `collect()` includes inline descendants by default.** The `inline`
|
|
60
|
+
parameter is now tri-state: omission follows recursive traversal or an explicit
|
|
61
|
+
inline-kind filter, `inline=False` excludes inline nodes, and `inline=True` includes
|
|
62
|
+
them for any query. Callers that need the previous block-only recursive result must
|
|
63
|
+
pass `inline=False`.
|
|
64
|
+
- **Cached structural views are mutation-safe.** `Block` is now frozen, `Block.children`
|
|
65
|
+
and `TableInfo.alignments` are tuples, and `sections()` returns recursively isolated
|
|
66
|
+
section/paragraph copies.
|
|
67
|
+
Code constructing blocks directly must pass child tuples; code comparing or building
|
|
68
|
+
table metadata must use alignment tuples.
|
|
69
|
+
Mutation of a returned section remains local to that view and does not persist to a
|
|
70
|
+
later call.
|
|
71
|
+
- **`SpanRef` owns its public resolution API.** Call `ref.resolve(source_text)` or
|
|
72
|
+
`ref.resolve_and_update(source_text)` on the root-exported type.
|
|
73
|
+
The generic `resolve` and `resolve_and_update` names are no longer promoted from
|
|
74
|
+
`flexdoc.docs`; update package-level imports and calls to use the methods.
|
|
75
|
+
- **Paragraph heading metadata is property-based.** `Paragraph.heading_level` and
|
|
76
|
+
`Paragraph.heading_title` now match `Paragraph.block_type` and `Block.heading_level`.
|
|
77
|
+
Remove `()` from calls to the two former methods.
|
|
78
|
+
- **The navigable-link constant is accurately named.** Import `NAVIGABLE_LINK_FORMS`
|
|
79
|
+
instead of `TRUE_LINK_FORMS`; no compatibility alias is retained.
|
|
80
|
+
- **`flexdoc.docs` now promotes the document model only.** Word-token/search and
|
|
81
|
+
diff/mapping names are no longer re-exported.
|
|
82
|
+
Import them from `flexdoc.docs.wordtoks`, `search_tokens`, `token_diffs`, or
|
|
83
|
+
`token_mapping` instead.
|
|
84
|
+
The current Chopdiff integration already uses these owning-module paths.
|
|
85
|
+
- **Frontmatter delimiters tolerate trailing horizontal whitespace.** Opening and
|
|
86
|
+
closing `---` lines may end in spaces or tabs while remaining verbatim in
|
|
87
|
+
`frontmatter`; leading whitespace still disqualifies a delimiter, and an unclosed
|
|
88
|
+
opening remains a thematic break.
|
|
89
|
+
- **The OS-independent classifier is backed by macOS CI.** Ubuntu still covers every
|
|
90
|
+
supported Python version, and Python 3.13 now runs the full lint/test gate on
|
|
91
|
+
`macos-latest` as a representative second platform.
|
|
92
|
+
- **Local release preparation is tag-aware.** The runbook fetches tags before building
|
|
93
|
+
and verifies candidate wheel metadata from an isolated local tag, preventing a tagless
|
|
94
|
+
clone from silently producing a `0.0.1.devN` release artifact.
|
|
95
|
+
- **Section sizing no longer constructs temporary documents.** `FlexDoc` and `Section`
|
|
96
|
+
now share private paragraph aggregation for every `TextUnit` and size summary; public
|
|
97
|
+
results and signatures are unchanged.
|
|
98
|
+
|
|
99
|
+
Remaining pre-1.0 design decisions and future mechanisms are collected in
|
|
100
|
+
`docs/project/specs/active/plan-2026-07-09-flexdoc-stabilization-roadmap.md`.
|
|
101
|
+
|
|
102
|
+
## 0.2.0 (2026-06-14)
|
|
103
|
+
|
|
104
|
+
Correctness fixes and a completed inline/heading/link surface for the document-metrics
|
|
105
|
+
use case (`docs/project/specs/active/plan-2026-06-13-metrics-use-case.md`, issue #6). As
|
|
106
|
+
a preview-stage library this takes the cleanest shape with no compatibility shims; the
|
|
107
|
+
API additions below include breaking signature changes (see **Changed**).
|
|
108
|
+
|
|
109
|
+
### Fixed
|
|
110
|
+
|
|
111
|
+
- **`node_table()` / `collect()` / `graph()` no longer raise on valid Markdown.** Inline
|
|
112
|
+
elements were discovered over the whole source and parented by start offset, so
|
|
113
|
+
backtick pairing across a block boundary (an empty fence next to inline backticks)
|
|
114
|
+
produced an inline span escaping its parent block and raised a layer-nesting error.
|
|
115
|
+
Inline discovery is now scoped per leaf content block, so an inline node can never
|
|
116
|
+
straddle a block boundary; links/images/definitions are parented by full containment.
|
|
117
|
+
- **`sections()` / `toc()` recover every heading `blocks()` finds, and own their content
|
|
118
|
+
correctly.** Headings were re-derived from the blank-line paragraph view, dropping
|
|
119
|
+
tight headings and headings preceded by a non-blank line (e.g. an HTML-comment
|
|
120
|
+
marker), and section content was bucketed from that same view, so a heading glued to
|
|
121
|
+
its body lost the body.
|
|
122
|
+
Sections now derive entirely from the structural block tree—the heading set *and* each
|
|
123
|
+
section’s own content (`own_paragraphs()` / `blocks()` / sizes) come from the
|
|
124
|
+
section’s source region—so tight and marker-preceded headings own exactly their
|
|
125
|
+
content.
|
|
126
|
+
- **Section spans nest correctly** even when a blank-line paragraph straddles a later
|
|
127
|
+
heading (e.g. an embedded `---` block marko reads as a setext heading): each section
|
|
128
|
+
spans from its heading to the next same-or-higher heading (trimmed), which nests by
|
|
129
|
+
construction. Byte-identical to the prior span for well-formed documents.
|
|
130
|
+
- **Reference-definition nodes attach to their block.** A `link_ref_def` span included
|
|
131
|
+
the line’s trailing newline and escaped the containing paragraph, leaving the node
|
|
132
|
+
unparented so a block-scoped `collect()` missed it; spans are now trimmed like every
|
|
133
|
+
structural block.
|
|
134
|
+
|
|
135
|
+
### Added
|
|
136
|
+
|
|
137
|
+
- **Heading metadata on the structural block**: `Block.heading_info` (`HeadingInfo` with
|
|
138
|
+
parser-authoritative `level` and `title`) and the `Block.heading_level` convenience;
|
|
139
|
+
`HeadingInfo` is exported from `flexdoc.docs`. The node table reads heading level from
|
|
140
|
+
it.
|
|
141
|
+
- **Typed link forms**: `LinkForm` (`inline` / `reference` / `autolink` / `bare_url` /
|
|
142
|
+
`image` / `reference_definition`) and `Link.link_form`. `FlexDoc.links(link_forms=…)`
|
|
143
|
+
selects any forms (default: navigable links only), and `FlexDoc.images()` is a
|
|
144
|
+
convenience for image access.
|
|
145
|
+
Reference definitions (`[id]: url`) are surfaced as `NodeKind.link_ref_def` nodes and
|
|
146
|
+
via `links(link_forms={LinkForm.reference_definition})`.
|
|
147
|
+
- **`FlexDoc.prose_text()`**: prose-only text for editorial linting and prose metrics —
|
|
148
|
+
prose blocks (paragraphs/headings, and table cells when `include_tables=True`) with
|
|
149
|
+
inline code and footnote refs dropped, links/images replaced by their text/alt,
|
|
150
|
+
inline-HTML tags dropped (wrapped text kept), and heading/blockquote/list markers and
|
|
151
|
+
reference-definition lines stripped; from verbatim source slices (line wrapping
|
|
152
|
+
preserved, never reflowed) so spacing like a spaced em-dash is kept exactly.
|
|
153
|
+
- **`FlexDoc.block_at_offset()`**: the innermost structural `Block` containing an offset
|
|
154
|
+
(the structural counterpart of `paragraph_at_offset`; the name, freed in 0.1.0, now
|
|
155
|
+
correctly returns a `Block`).
|
|
156
|
+
- **Test-suite hardening**: adversarial corpus documents (`inline_pathology`,
|
|
157
|
+
`heading_edges`, `link_taxonomy`); cross-projection invariants tying `toc()` to the
|
|
158
|
+
heading blocks, inline nesting on the query surface, and link-form accounting; and a
|
|
159
|
+
dogfood test that parses every Markdown file in the repo and asserts the invariants.
|
|
160
|
+
See the spec’s “Why These Bugs Escaped the Tests” analysis.
|
|
161
|
+
|
|
162
|
+
### Changed
|
|
163
|
+
|
|
164
|
+
These are breaking, made cleanly (no aliases) given the preview status:
|
|
165
|
+
|
|
166
|
+
- **`Link` gains a required `link_form: LinkForm` field.** Direct `Link(...)`
|
|
167
|
+
construction must pass it.
|
|
168
|
+
- **`block_links()` returns all link-like constructs** (navigable links, images, and
|
|
169
|
+
reference definitions), each with a `link_form`; previously it returned navigable
|
|
170
|
+
links only. `FlexDoc.links()` filters to navigable links by default, so its default
|
|
171
|
+
result is unchanged.
|
|
172
|
+
- **`collect()` returns inline-kind nodes without `recursive=True`.** An inline-kind
|
|
173
|
+
request (e.g. `collect(kinds={NodeKind.link})`) now widens the candidate set instead
|
|
174
|
+
of silently returning `[]`—matching the documented behavior.
|
|
175
|
+
|
|
176
|
+
## 0.1.0 (2026-06-12)
|
|
177
|
+
|
|
178
|
+
First release.
|
|
179
|
+
|
|
180
|
+
### Added
|
|
181
|
+
|
|
182
|
+
- **Initial flexdoc package**, extracted from
|
|
183
|
+
[chopdiff](https://github.com/jlevy/chopdiff) as its own standalone distribution.
|
|
184
|
+
This is the document/markdown layer—`FlexDoc`, paragraphs/sentences, the block tree
|
|
185
|
+
and block types, sections, the node table, `collect()`, `DocGraph`, `SpanRef`, token
|
|
186
|
+
diffs/mappings, word tokenization, html-in-md, and read-time/token estimation—with no
|
|
187
|
+
dependency on chopdiff’s diff and windowed-transform machinery.
|
|
188
|
+
|
|
189
|
+
The import roots are `flexdoc.docs`, `flexdoc.html`, and `flexdoc.util`. Parse
|
|
190
|
+
behavior is unchanged from the `flexdoc.*` modules that previously shipped inside the
|
|
191
|
+
chopdiff wheel; this release packages them independently.
|
|
192
|
+
See `docs/project/specs/active/plan-2026-06-11-flexdoc-extraction.md`.
|
|
193
|
+
|
|
194
|
+
- **A deliberate root API**: the working set is importable from the package root —
|
|
195
|
+
`FlexDoc`, `DocGraph`, `Detail`, `SpanRef`, `BlockType`, `NodeKind`, `Layer`,
|
|
196
|
+
`TextUnit`—designed against the known downstream users and pinned by contract tests.
|
|
197
|
+
The render helpers for source-linked HTML (`render_node_attrs`,
|
|
198
|
+
`wrap_with_node_attrs`, `parse_source_span_attr`) are public in `flexdoc.docs`.
|
|
199
|
+
|
|
200
|
+
- **DocGraph paragraph view**: `Views.paragraphs` joins `toc`/`blocks`/`links`/
|
|
201
|
+
`sentences` in the serialized projection.
|
|
202
|
+
|
|
203
|
+
### Changed (Relative to the Modules as Shipped in Chopdiff)
|
|
204
|
+
|
|
205
|
+
The first standalone release also refines the API surface (the pre-publish design
|
|
206
|
+
review, `docs/project/review/senior-engineering-review-flexdoc-standalone-2026-06.md`);
|
|
207
|
+
these are intentional hard cuts with no compatibility aliases:
|
|
208
|
+
|
|
209
|
+
- **`TextDoc` is renamed `FlexDoc`**—the package’s single entry point, named for the
|
|
210
|
+
model it carries (all layered projections hang off it).
|
|
211
|
+
It is importable from the package root: `from flexdoc import FlexDoc`. The module is
|
|
212
|
+
`flexdoc.docs.flex_doc` (was `chopdiff.docs.text_doc`), and the design of record is
|
|
213
|
+
now `docs/flexdoc-spec.md` (was `textdoc-spec.md`).
|
|
214
|
+
- **`collect()` is fully keyword-only and the deprecated aliases are gone**: use
|
|
215
|
+
`subtree_of=` (was `scope=`, previously also positional) and `within=` (was
|
|
216
|
+
`contains=`).
|
|
217
|
+
- **Editing-view methods are named in paragraph terms**, so “block” always means the
|
|
218
|
+
structural layer: `FlexDoc.paragraph_at_offset` (was `block_at_offset`),
|
|
219
|
+
`FlexDoc.iter_paragraphs` (was `iter_blocks`), `Section.own_paragraphs`/
|
|
220
|
+
`subtree_paragraphs` (were `own_blocks`/`subtree_blocks`).
|
|
221
|
+
- **The export surface is settled**: `flexdoc.docs` now exports
|
|
222
|
+
`CodeInfo`/`TableInfo`/`ListInfo`, `resolve`/`resolve_and_update`,
|
|
223
|
+
`parse_blocks`/`walk_blocks`/`block_type_for`, and `DEFAULT_INCLUDE`; `flexdoc.html`
|
|
224
|
+
exports `html_p`, `html_tag`, `escape_attribute`, `tag_wrapper`, and
|
|
225
|
+
`identity_wrapper`. Link extraction is public as `flexdoc.docs.links.block_links`.
|
|
226
|
+
- **`Node.attrs` values are JSON-typed** (`AttrValue`), validated at `DocGraph`
|
|
227
|
+
serialization, and node-id assignment order is pinned and tested for cross-language
|
|
228
|
+
ports; layer nesting guarantees (`LAYER_NESTING`) are enforced at node-table build.
|
|
229
|
+
- Internally, the former `text_doc.py` was split into `flex_doc.py`, `paragraphs.py`,
|
|
230
|
+
`links.py`, and `sections.py` (package imports from `flexdoc.docs` are unaffected),
|
|
231
|
+
and `sections()` is now cached like the other derived views.
|
|
232
|
+
|
|
233
|
+
Migration from chopdiff in one pass: `chopdiff.docs.TextDoc` → `flexdoc.FlexDoc` (or
|
|
234
|
+
`flexdoc.docs.FlexDoc`), `chopdiff.docs.*` → `flexdoc.docs.*`, plus the method renames
|
|
235
|
+
above.
|
|
236
|
+
|
|
237
|
+
<!-- This document follows common-doc-guidelines.md.
|
|
238
|
+
See github.com/jlevy/practical-prose and review guidelines before editing.
|
|
239
|
+
-->
|