flexdoc 0.2.0__tar.gz → 0.4.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.4.0}/.claude/scripts/ensure-gh-cli.sh +1 -1
- {flexdoc-0.2.0 → flexdoc-0.4.0}/.codex/ensure-gh-cli.sh +1 -1
- flexdoc-0.4.0/.github/release-request/notes.md +32 -0
- flexdoc-0.4.0/.github/release-request/request.json +3 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/.github/workflows/ci.yml +14 -14
- {flexdoc-0.2.0 → flexdoc-0.4.0}/.github/workflows/publish.yml +1 -0
- flexdoc-0.4.0/.github/workflows/release.yml +54 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/.tbd/config.yml +25 -16
- {flexdoc-0.2.0 → flexdoc-0.4.0}/AGENTS.md +1 -1
- flexdoc-0.4.0/CHANGELOG.md +292 -0
- flexdoc-0.4.0/PKG-INFO +282 -0
- flexdoc-0.4.0/README.md +244 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/SUPPLY-CHAIN-SECURITY.md +19 -45
- {flexdoc-0.2.0 → flexdoc-0.4.0}/TODO.md +17 -8
- {flexdoc-0.2.0 → flexdoc-0.4.0}/devtools/lint.py +5 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/docs/development.md +7 -0
- flexdoc-0.4.0/docs/flexdoc-spec.md +1431 -0
- flexdoc-0.4.0/docs/project/drafts/draft-2026-07-flexdoc-intro-post.md +140 -0
- flexdoc-0.4.0/docs/project/research/research-2026-07-10-text-reference-microformat.md +3546 -0
- flexdoc-0.4.0/docs/project/review/senior-engineering-review-flexdoc-2026-07.md +366 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/docs/project/review/senior-engineering-review-flexdoc-standalone-2026-06.md +36 -36
- flexdoc-0.4.0/docs/project/review/senior-engineering-review-v0.4-release-2026-07.md +253 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/docs/project/specs/active/plan-2026-05-29-unified-document-model.md +8 -8
- {flexdoc-0.2.0 → flexdoc-0.4.0}/docs/project/specs/active/plan-2026-06-11-flexdoc-extraction.md +97 -89
- flexdoc-0.4.0/docs/project/specs/active/plan-2026-07-09-flexdoc-stabilization-roadmap.md +267 -0
- {flexdoc-0.2.0/docs/project/specs/active → flexdoc-0.4.0/docs/project/specs/done}/plan-2026-06-11-structural-metadata.md +5 -5
- flexdoc-0.4.0/docs/project/specs/done/plan-2026-06-13-metrics-use-case.md +561 -0
- flexdoc-0.4.0/docs/project/specs/done/plan-2026-07-08-post-review-refinements.md +169 -0
- flexdoc-0.4.0/docs/project/specs/done/plan-2026-07-13-logical-word-metrics.md +260 -0
- flexdoc-0.4.0/docs/project/specs/done/plan-2026-07-14-native-textref-integration.md +344 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/docs/publishing.md +56 -11
- flexdoc-0.4.0/docs/usage.md +245 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/examples/backfill_timestamps.py +12 -1
- {flexdoc-0.2.0 → flexdoc-0.4.0}/examples/doc_structure.py +10 -5
- {flexdoc-0.2.0 → flexdoc-0.4.0}/examples/normalized_form.py +2 -1
- flexdoc-0.4.0/examples/textref_workflows.py +89 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/pyproject.toml +20 -17
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/__init__.py +25 -6
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/docs/__init__.py +60 -81
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/docs/base_blocks.py +1 -1
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/docs/block_info.py +5 -5
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/docs/block_tree.py +11 -9
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/docs/collect.py +13 -11
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/docs/debug.py +19 -10
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/docs/doc_graph.py +125 -50
- flexdoc-0.4.0/src/flexdoc/docs/doc_graph_schema.json +603 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/docs/flex_doc.py +99 -77
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/docs/frontmatter.py +23 -6
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/docs/links.py +2 -2
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/docs/node_table.py +2 -1
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/docs/paragraphs.py +52 -2
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/docs/render.py +43 -1
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/docs/sections.py +32 -17
- flexdoc-0.4.0/src/flexdoc/docs/serialization.py +26 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/docs/sizes.py +19 -4
- flexdoc-0.4.0/src/flexdoc/docs/span_ref.py +345 -0
- flexdoc-0.4.0/src/flexdoc/docs/text_annotations.py +157 -0
- flexdoc-0.4.0/src/flexdoc/docs/text_ref.py +855 -0
- flexdoc-0.4.0/src/flexdoc/docs/text_ref_context.py +730 -0
- flexdoc-0.4.0/src/flexdoc/docs/text_ref_schema.json +315 -0
- flexdoc-0.4.0/src/flexdoc/util/__init__.py +23 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/util/read_time.py +5 -7
- flexdoc-0.4.0/src/flexdoc/util/token_estimate.py +25 -0
- flexdoc-0.4.0/src/flexdoc/util/word_count.py +74 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/docs/test_block_info.py +3 -2
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/docs/test_block_types.py +3 -3
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/docs/test_blocks.py +27 -7
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/docs/test_caching_threadsafe.py +21 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/docs/test_collect.py +20 -5
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/docs/test_doc_graph.py +124 -35
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/docs/test_flex_doc.py +2 -3
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/docs/test_frontmatter.py +44 -5
- flexdoc-0.4.0/tests/docs/test_line_endings.py +86 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/docs/test_links.py +8 -1
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/docs/test_sections.py +35 -8
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/docs/test_span_ref.py +126 -18
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/docs/test_spans.py +1 -1
- flexdoc-0.4.0/tests/docs/test_text_annotations.py +245 -0
- flexdoc-0.4.0/tests/docs/test_text_ref.py +399 -0
- flexdoc-0.4.0/tests/docs/test_text_ref_context.py +242 -0
- flexdoc-0.4.0/tests/docs/test_text_ref_render.py +136 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/README.md +13 -9
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/documents/footnotes_refs.md +11 -2
- flexdoc-0.4.0/tests/golden/documents/preamble_only.md +13 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/expected/footnotes_refs/docgraph.yaml +19 -11
- flexdoc-0.4.0/tests/golden/expected/footnotes_refs/reassembled.md +15 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/expected/footnotes_refs/report.yaml +120 -66
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/expected/heading_edges/docgraph.yaml +3 -2
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/expected/heading_edges/report.yaml +7 -12
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/expected/inline_html/docgraph.yaml +3 -2
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/expected/inline_html/report.yaml +1 -1
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/expected/inline_pathology/docgraph.yaml +3 -2
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/expected/inline_pathology/report.yaml +6 -11
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/expected/kitchen_sink/docgraph.yaml +3 -2
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/expected/kitchen_sink/report.yaml +3 -5
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/expected/link_taxonomy/docgraph.yaml +3 -2
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/expected/link_taxonomy/report.yaml +8 -14
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/expected/malformed/docgraph.yaml +3 -2
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/expected/malformed/report.yaml +1 -1
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/expected/nested_list/docgraph.yaml +3 -2
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/expected/nested_list/report.yaml +1 -1
- flexdoc-0.4.0/tests/golden/expected/preamble_only/docgraph.yaml +84 -0
- flexdoc-0.4.0/tests/golden/expected/preamble_only/reassembled.md +5 -0
- flexdoc-0.4.0/tests/golden/expected/preamble_only/report.yaml +141 -0
- flexdoc-0.4.0/tests/golden/expected/textref-annotations.md +59 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/expected/tight_vs_loose/docgraph.yaml +3 -2
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/expected/tight_vs_loose/report.yaml +1 -1
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/expected/unicode/docgraph.yaml +3 -2
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/expected/unicode/report.yaml +4 -6
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/test_golden_docs.py +10 -9
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/test_examples.py +1 -0
- flexdoc-0.4.0/tests/test_root_api.py +157 -0
- flexdoc-0.4.0/tests/test_word_count.py +117 -0
- {flexdoc-0.2.0 → flexdoc-0.4.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/flexdoc-spec.md +0 -1018
- flexdoc-0.2.0/docs/project/specs/active/plan-2026-06-13-metrics-use-case.md +0 -531
- flexdoc-0.2.0/docs/usage.md +0 -136
- flexdoc-0.2.0/src/flexdoc/docs/doc_graph_schema.json +0 -219
- flexdoc-0.2.0/src/flexdoc/docs/span_ref.py +0 -192
- flexdoc-0.2.0/src/flexdoc/util/__init__.py +0 -11
- flexdoc-0.2.0/src/flexdoc/util/token_estimate.py +0 -34
- 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.4.0}/.agents/skills/simple-modern-uv/SKILL.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/.agents/skills/simple-modern-uv/references/adopt-existing.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/.agents/skills/simple-modern-uv/references/customize.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/.agents/skills/simple-modern-uv/references/faq.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/.agents/skills/tbd/SKILL.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/.claude/.gitignore +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/.claude/hooks/tbd-closing-reminder.sh +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/.claude/scripts/tbd-session.sh +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/.claude/settings.json +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/.claude/skills/tbd/SKILL.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/.codex/hooks.json +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/.codex/tbd-closing-reminder.sh +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/.codex/tbd-session.sh +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/.copier-answers.yml +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/.gitignore +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/.tbd/.gitattributes +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/.tbd/.gitignore +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/CLAUDE.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/LICENSE +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/Makefile +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/docs/installation.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/docs/project/research/research-2026-05-29-document-model.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/docs/project/research/research-2026-05-30-multilayer-parsing.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/docs/project/research/research-2026-05-30-span-references.md +0 -0
- {flexdoc-0.2.0/docs/project/specs/active → flexdoc-0.4.0/docs/project/specs/done}/plan-2026-05-31-doc-model-refinements.md +0 -0
- {flexdoc-0.2.0/docs/project/specs/active → flexdoc-0.4.0/docs/project/specs/done}/plan-2026-05-31-golden-doc-testing.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/skills-lock.json +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/docs/block_types.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/docs/interval_index.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/docs/node.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/docs/search_tokens.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/docs/token_diffs.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/docs/token_mapping.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/docs/wordtoks.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/html/__init__.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/html/extractor.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/html/html_in_md.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/html/html_plaintext.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/html/html_tags.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/html/timestamps.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/src/flexdoc/py.typed +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/__init__.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/docs/__init__.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/docs/test_base_blocks.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/docs/test_footnote_ref.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/docs/test_interval_index.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/docs/test_node.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/docs/test_node_table.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/docs/test_offsets.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/docs/test_subdoc_and_empty.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/docs/test_token_diffs.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/docs/test_token_mapping.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/docs/test_token_validation.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/docs/test_wordtoks.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/documents/heading_edges.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/documents/inline_html.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/documents/inline_pathology.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/documents/kitchen_sink.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/documents/link_taxonomy.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/documents/malformed.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/documents/nested_list.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/documents/tight_vs_loose.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/documents/unicode.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/expected/heading_edges/reassembled.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/expected/inline_html/reassembled.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/expected/inline_pathology/reassembled.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/expected/kitchen_sink/reassembled.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/expected/link_taxonomy/reassembled.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/expected/malformed/reassembled.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/expected/nested_list/reassembled.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/expected/tight_vs_loose/reassembled.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/golden/expected/unicode/reassembled.md +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/html/__init__.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/html/test_html_tag_hardening.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/html/test_html_tags.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/html/test_html_validation_and_classes.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.0}/tests/html/test_timestamps.py +0 -0
- {flexdoc-0.2.0 → flexdoc-0.4.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,32 @@
|
|
|
1
|
+
## What's Changed
|
|
2
|
+
|
|
3
|
+
This release adds the portable reference and annotation layer from the 2026-07 TextRef design cycle plus cross-language logical word metrics, finalized under an independent senior release review ([review and breaking-change catalog](https://github.com/jlevy/flexdoc/blob/main/docs/project/review/senior-engineering-review-v0.4-release-2026-07.md); PRs #18, #20, #21). 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`). If you depend on an unbounded `flexdoc>=0.3.0`, review the breaking changes below or pin `<0.4` before upgrading.
|
|
4
|
+
|
|
5
|
+
### New Features
|
|
6
|
+
|
|
7
|
+
**Native TextRef integration**
|
|
8
|
+
|
|
9
|
+
Strict `DocRef`/`TextRef` values identify whole documents, spans, points, and semantic sections with canonical JSON and reversible `textref:0.1` URIs. `FlexDoc.references()` binds a document locator to one source snapshot: it maps every locatable public value (`for_target`, `for_span`, `for_point`, `for_section`), resolves references with typed outcomes on independent document/source-validation/selector axes (resolution never silently picks a duplicate), retrieves structured line-window context, and renders deterministic annotation views for humans and LLMs. Span quote evidence is configurable per context or per span; compact exact-less spans stay bound to one source hash.
|
|
10
|
+
|
|
11
|
+
**Consumer-owned annotations**
|
|
12
|
+
|
|
13
|
+
`TextAnnotation` and the one-document `AnnotationSet` sidecar round-trip strict JSON and safe YAML, and every sidecar entry is validated through the complete TextRef evidence contract at parse time. `DocGraph/v0.2` embeds a matching sidecar after verifying its document and source hash against the snapshot, and bounds-checks embedded positions. Committed JSON Schemas ship for both formats in the wheel.
|
|
14
|
+
|
|
15
|
+
**Cross-language logical word metrics**
|
|
16
|
+
|
|
17
|
+
`TextUnit.words` now measures normalized word-equivalent volume across natural language, CJK text, source code, URLs, and punctuation-dense content. `TextUnit.raw_words` and `raw_word_count()` keep literal whitespace counting, and `logical_word_count()` exposes the dependency-free primitive. See the [logical-word definition and validation](https://gist.github.com/jlevy/0d6d87885f6d85f31440e58b8cfce663).
|
|
18
|
+
|
|
19
|
+
### Breaking Changes
|
|
20
|
+
|
|
21
|
+
Made cleanly with no compatibility aliases, given the pre-1.0 status. Full catalog with rationales and migrations: [review doc §4](https://github.com/jlevy/flexdoc/blob/main/docs/project/review/senior-engineering-review-v0.4-release-2026-07.md).
|
|
22
|
+
|
|
23
|
+
- **`FlexDoc.graph()` requires a document locator** — `doc.graph()` becomes `doc.graph(document="path/or/id.md")`; `build_doc_graph()` and the debug helpers `doc_graph_yaml()`/`dump_views()` take the same argument. Every serialized graph is now self-identifying.
|
|
24
|
+
- **DocGraph wire contract v0.1 → v0.2** — `schema` is `"DocGraph/v0.2"`; `source.document` is required; the unqualified `source.sha256` field is replaced by algorithm-qualified `source.source_hash` (shared with TextRef); models are strict (unknown fields rejected, frozen, strict types) and validate node references, span/text consistency, and annotation bounds.
|
|
25
|
+
- **`TextUnit.words` is logical** — it matches raw counts for ordinary non-wide prose but differs for wide/fullwidth scripts, URLs, code, and symbolic content; use `TextUnit.raw_words` for the previous whitespace-split behavior. Ripples through `size()`, `size_summary()`, `section_size_tree()`, and debug reports.
|
|
26
|
+
- **Token estimates scale logical words** — `estimate_tokens(text, tokens_per_logical_word=1.6)` replaces the `chars_per_token` parameter, and `TOKENS_PER_LOGICAL_WORD` replaces `CHARS_PER_TOKEN`; estimates change numerically even at defaults.
|
|
27
|
+
- **`format_read_time()` takes logical word counts** — same signature; the default rate corresponds to roughly 450 CJK characters per minute under the default wide-character weight.
|
|
28
|
+
- **Pinned outputs change** — regenerate any golden DocGraph/report snapshots (schema string, `source` block, and `words` fields).
|
|
29
|
+
|
|
30
|
+
### Full Changelog
|
|
31
|
+
|
|
32
|
+
https://github.com/jlevy/flexdoc/compare/v0.3.0...v0.4.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,27 +14,25 @@ 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
|
|
33
34
|
shortcuts/system/skill-minimal.md: internal:shortcuts/system/skill-minimal.md
|
|
35
|
+
shortcuts/standard/address-pr-review.md: internal:shortcuts/standard/address-pr-review.md
|
|
34
36
|
shortcuts/standard/agent-handoff.md: internal:shortcuts/standard/agent-handoff.md
|
|
35
37
|
shortcuts/standard/checkout-third-party-repo.md: internal:shortcuts/standard/checkout-third-party-repo.md
|
|
36
38
|
shortcuts/standard/code-cleanup-all.md: internal:shortcuts/standard/code-cleanup-all.md
|
|
@@ -50,6 +52,7 @@ docs_cache:
|
|
|
50
52
|
shortcuts/standard/new-shortcut.md: internal:shortcuts/standard/new-shortcut.md
|
|
51
53
|
shortcuts/standard/new-validation-plan.md: internal:shortcuts/standard/new-validation-plan.md
|
|
52
54
|
shortcuts/standard/plan-implementation-with-beads.md: internal:shortcuts/standard/plan-implementation-with-beads.md
|
|
55
|
+
shortcuts/standard/pr-review-workflows.md: internal:shortcuts/standard/pr-review-workflows.md
|
|
53
56
|
shortcuts/standard/precommit-process.md: internal:shortcuts/standard/precommit-process.md
|
|
54
57
|
shortcuts/standard/review-code-python.md: internal:shortcuts/standard/review-code-python.md
|
|
55
58
|
shortcuts/standard/review-code-typescript.md: internal:shortcuts/standard/review-code-typescript.md
|
|
@@ -58,6 +61,7 @@ docs_cache:
|
|
|
58
61
|
shortcuts/standard/revise-all-architecture-docs.md: internal:shortcuts/standard/revise-all-architecture-docs.md
|
|
59
62
|
shortcuts/standard/revise-architecture-doc.md: internal:shortcuts/standard/revise-architecture-doc.md
|
|
60
63
|
shortcuts/standard/setup-github-cli.md: internal:shortcuts/standard/setup-github-cli.md
|
|
64
|
+
shortcuts/standard/suggest-upstream-improvements.md: internal:shortcuts/standard/suggest-upstream-improvements.md
|
|
61
65
|
shortcuts/standard/sync-failure-recovery.md: internal:shortcuts/standard/sync-failure-recovery.md
|
|
62
66
|
shortcuts/standard/update-specs-status.md: internal:shortcuts/standard/update-specs-status.md
|
|
63
67
|
shortcuts/standard/welcome-user.md: internal:shortcuts/standard/welcome-user.md
|
|
@@ -92,3 +96,8 @@ docs_cache:
|
|
|
92
96
|
templates/plan-spec.md: internal:templates/plan-spec.md
|
|
93
97
|
templates/qa-playbook.md: internal:templates/qa-playbook.md
|
|
94
98
|
templates/research-brief.md: internal:templates/research-brief.md
|
|
99
|
+
references/docmap-format.md: internal:references/docmap-format.md
|
|
100
|
+
references/docref-format.md: internal:references/docref-format.md
|
|
101
|
+
lookup_path:
|
|
102
|
+
- .tbd/docs/shortcuts/system
|
|
103
|
+
- .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,292 @@
|
|
|
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.4.0 (2026-07-20)
|
|
8
|
+
|
|
9
|
+
This is an intentional pre-1.0 API break; per the pre-1.0 rule it is a minor release,
|
|
10
|
+
not a 0.3.x patch. The release-gate review, validation evidence, and full
|
|
11
|
+
breaking-change catalog with migrations are in
|
|
12
|
+
`docs/project/review/senior-engineering-review-v0.4-release-2026-07.md`.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
|
|
16
|
+
- **Native TextRef integration.** Strict `DocRef`/`TextRef` values now identify whole
|
|
17
|
+
documents, spans, points, and semantic sections with canonical JSON and reversible
|
|
18
|
+
`textref:0.1` URIs. `FlexDoc.references()` maps every locatable public document value,
|
|
19
|
+
resolves references with typed outcomes, retrieves structured source context, and
|
|
20
|
+
renders deterministic human/LLM-readable annotation views. Span quote evidence is
|
|
21
|
+
optional: callers can select a context-wide size policy or override individual spans,
|
|
22
|
+
while compact exact-less spans remain safely bound to one source hash.
|
|
23
|
+
- **Consumer-owned TextRef annotations.** `TextAnnotation` and the one-document
|
|
24
|
+
`AnnotationSet` sidecar round-trip strict JSON and safe YAML. `DocGraph/v0.2` carries
|
|
25
|
+
required document identity and source hash in every graph, with optional typed
|
|
26
|
+
annotations in the same schema. The protocol and renderer add no dependencies.
|
|
27
|
+
- **Cross-language logical word metrics.** `TextUnit.words` now measures normalized
|
|
28
|
+
word-equivalent volume across natural language, CJK text, source code, URLs, and other
|
|
29
|
+
punctuation-dense content. `TextUnit.raw_words` and `raw_word_count()` preserve
|
|
30
|
+
literal whitespace-delimited counting, while `logical_word_count()` exposes the
|
|
31
|
+
dependency-free normalized primitive.
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
- **DocGraph is v0.2 and self-identifying.** `FlexDoc.graph()` and `build_doc_graph()`
|
|
36
|
+
now require a consumer-owned `document` locator and always return `DocGraph/v0.2`;
|
|
37
|
+
annotations optionally populate that same model, and the debug helpers
|
|
38
|
+
`doc_graph_yaml()`/`dump_views()` take the same `document` argument. In the
|
|
39
|
+
serialized graph, `source.document` is required and the unqualified `source.sha256`
|
|
40
|
+
field is replaced by `source.source_hash`, whose algorithm-qualified value is shared
|
|
41
|
+
with TextRef. All DocGraph models are now strict (unknown fields rejected, strict
|
|
42
|
+
types, frozen instances) and validate node-reference integrity, span/text
|
|
43
|
+
consistency, and annotation bounds.
|
|
44
|
+
|
|
45
|
+
- **`TextUnit.words` changes from raw to logical semantics.** It matches the raw count
|
|
46
|
+
for ordinary non-wide prose averaging 3–6 characters per word, but differs for
|
|
47
|
+
wide/fullwidth scripts, longer or shorter average word lengths, URLs, code, and other
|
|
48
|
+
symbolic content. Callers requiring the exact previous whitespace-split behavior
|
|
49
|
+
must use `TextUnit.raw_words`; there is no separate `TextUnit.logical_words` member.
|
|
50
|
+
Size summaries, section-tree defaults, and debug-report `words` fields use the logical
|
|
51
|
+
measure, and aggregate counts are rounded only after the full text is measured.
|
|
52
|
+
- **Approximate token estimates now scale logical words.** `estimate_tokens()` uses
|
|
53
|
+
`TOKENS_PER_LOGICAL_WORD` (default `1.6`) instead of the former
|
|
54
|
+
`CHARS_PER_TOKEN`/`chars_per_token` API. This is still a model-family heuristic; use
|
|
55
|
+
the target provider's tokenizer for exact counts or hard context limits.
|
|
56
|
+
- **Reading-time guidance is language-robust.** Pass logical word counts to
|
|
57
|
+
`format_read_time()`; the default rate corresponds to roughly 450 CJK characters per
|
|
58
|
+
minute under the default wide-character weight.
|
|
59
|
+
|
|
60
|
+
## 0.3.0 (2026-07-11)
|
|
61
|
+
|
|
62
|
+
Fixes from the 2026-07 pre-promotion design review
|
|
63
|
+
(`docs/project/review/senior-engineering-review-flexdoc-2026-07.md`). These changes
|
|
64
|
+
alter documented behavior, so per the pre-1.0 rule this is a minor release, not a
|
|
65
|
+
0.2.x patch.
|
|
66
|
+
|
|
67
|
+
### Fixed
|
|
68
|
+
|
|
69
|
+
- **CRLF input no longer corrupts the structural views.** marko computes block positions
|
|
70
|
+
against LF-only text, so `\r` in the input desynchronized every structural span
|
|
71
|
+
(`blocks()`, `sections()`, `base_blocks()`, `links()`, `prose_text()`, the node table)
|
|
72
|
+
from `source_text`, silently garbling content and violating the base-block cover
|
|
73
|
+
invariant. `from_text` now normalizes `\r\n` and lone `\r` to `\n` and retains the
|
|
74
|
+
normalized string as `source_text`, so all layers share one offset space.
|
|
75
|
+
Callers anchoring offsets to an external CRLF original must normalize it the same way
|
|
76
|
+
first.
|
|
77
|
+
- **Markdown constructs inside frontmatter can no longer swallow the body.** The shared
|
|
78
|
+
parse previously included the frontmatter region, so e.g. a YAML block scalar
|
|
79
|
+
containing a code fence opened a fenced block spanning the rest of the document,
|
|
80
|
+
leaving `blocks()` empty.
|
|
81
|
+
The frontmatter region is now blanked out of the shared parse (offsets preserved);
|
|
82
|
+
frontmatter remains a non-content region.
|
|
83
|
+
Link extraction now reuses that blanked parse instead of reparsing the body for
|
|
84
|
+
frontmatter documents.
|
|
85
|
+
- **`resolve()` no longer guesses on ambiguous quotes.** Per the spec’s error posture
|
|
86
|
+
(§11), a `SpanRef` quote that occurs multiple times with no disambiguating
|
|
87
|
+
prefix/suffix (or a tied context score) now resolves to `None` instead of silently
|
|
88
|
+
anchoring to the first occurrence.
|
|
89
|
+
A context-free position hint also cannot select a duplicate, because no context or
|
|
90
|
+
source identity proves which occurrence was intended.
|
|
91
|
+
A zero-width quote (`exact=""`) also resolves to `None` on both the fast and slow
|
|
92
|
+
paths.
|
|
93
|
+
- **`collect(overlaps=...)` treats empty intervals as empty.** A degenerate `[x, x)`
|
|
94
|
+
region (or node span) now overlaps nothing, matching half-open interval semantics;
|
|
95
|
+
point queries use `(x, x + 1)`.
|
|
96
|
+
- **Render helpers harden their HTML output.** `render_node_attrs` attribute-escapes
|
|
97
|
+
`node.id`, and `wrap_with_node_attrs` validates the tag name (raising `ValueError`),
|
|
98
|
+
matching the validation in the `flexdoc.html` tag helpers.
|
|
99
|
+
|
|
100
|
+
### Changed
|
|
101
|
+
|
|
102
|
+
- **The dependency lock is refreshed under the 14-day cool-off.** The cutoff is
|
|
103
|
+
2026-06-26, expired per-package exceptions are removed, and the audit group resolves
|
|
104
|
+
fixed `pip` and `msgpack` versions, so CI runs `pip-audit` without advisory ignores.
|
|
105
|
+
- **`graph()` accepts any set.** `FlexDoc.graph()` and `build_doc_graph()` annotate
|
|
106
|
+
`include`/`detail` as `collections.abc.Set`, so plain `set` literals type-check
|
|
107
|
+
(matching `collect()`); behavior is unchanged.
|
|
108
|
+
- **`TextUnit` is a `StrEnum`**, matching every other public enum, so
|
|
109
|
+
`TextUnit.words == "words"` now holds.
|
|
110
|
+
Source-compatible for enum-member access; only `str()`/equality-with-string behavior
|
|
111
|
+
changes.
|
|
112
|
+
- **Recursive `collect()` includes inline descendants by default.** The `inline`
|
|
113
|
+
parameter is now tri-state: omission follows recursive traversal or an explicit
|
|
114
|
+
inline-kind filter, `inline=False` excludes inline nodes, and `inline=True` includes
|
|
115
|
+
them for any query. Callers that need the previous block-only recursive result must
|
|
116
|
+
pass `inline=False`.
|
|
117
|
+
- **Cached structural views are mutation-safe.** `Block` is now frozen, `Block.children`
|
|
118
|
+
and `TableInfo.alignments` are tuples, and `sections()` returns recursively isolated
|
|
119
|
+
section/paragraph copies.
|
|
120
|
+
Code constructing blocks directly must pass child tuples; code comparing or building
|
|
121
|
+
table metadata must use alignment tuples.
|
|
122
|
+
Mutation of a returned section remains local to that view and does not persist to a
|
|
123
|
+
later call.
|
|
124
|
+
- **`SpanRef` owns its public resolution API.** Call `ref.resolve(source_text)` or
|
|
125
|
+
`ref.resolve_and_update(source_text)` on the root-exported type.
|
|
126
|
+
The generic `resolve` and `resolve_and_update` names are no longer promoted from
|
|
127
|
+
`flexdoc.docs`; update package-level imports and calls to use the methods.
|
|
128
|
+
- **Paragraph heading metadata is property-based.** `Paragraph.heading_level` and
|
|
129
|
+
`Paragraph.heading_title` now match `Paragraph.block_type` and `Block.heading_level`.
|
|
130
|
+
Remove `()` from calls to the two former methods.
|
|
131
|
+
- **The navigable-link constant is accurately named.** Import `NAVIGABLE_LINK_FORMS`
|
|
132
|
+
instead of `TRUE_LINK_FORMS`; no compatibility alias is retained.
|
|
133
|
+
- **`flexdoc.docs` now promotes the document model only.** Word-token/search and
|
|
134
|
+
diff/mapping names are no longer re-exported.
|
|
135
|
+
Import them from `flexdoc.docs.wordtoks`, `search_tokens`, `token_diffs`, or
|
|
136
|
+
`token_mapping` instead.
|
|
137
|
+
The current Chopdiff integration already uses these owning-module paths.
|
|
138
|
+
- **Frontmatter delimiters tolerate trailing horizontal whitespace.** Opening and
|
|
139
|
+
closing `---` lines may end in spaces or tabs while remaining verbatim in
|
|
140
|
+
`frontmatter`; leading whitespace still disqualifies a delimiter, and an unclosed
|
|
141
|
+
opening remains a thematic break.
|
|
142
|
+
- **The OS-independent classifier is backed by macOS CI.** Ubuntu still covers every
|
|
143
|
+
supported Python version, and Python 3.13 now runs the full lint/test gate on
|
|
144
|
+
`macos-latest` as a representative second platform.
|
|
145
|
+
- **Local release preparation is tag-aware.** The runbook fetches tags before building
|
|
146
|
+
and verifies candidate wheel metadata from an isolated local tag, preventing a tagless
|
|
147
|
+
clone from silently producing a `0.0.1.devN` release artifact.
|
|
148
|
+
- **Section sizing no longer constructs temporary documents.** `FlexDoc` and `Section`
|
|
149
|
+
now share private paragraph aggregation for every `TextUnit` and size summary; public
|
|
150
|
+
results and signatures are unchanged.
|
|
151
|
+
|
|
152
|
+
Remaining pre-1.0 design decisions and future mechanisms are collected in
|
|
153
|
+
`docs/project/specs/active/plan-2026-07-09-flexdoc-stabilization-roadmap.md`.
|
|
154
|
+
|
|
155
|
+
## 0.2.0 (2026-06-14)
|
|
156
|
+
|
|
157
|
+
Correctness fixes and a completed inline/heading/link surface for the document-metrics
|
|
158
|
+
use case (`docs/project/specs/done/plan-2026-06-13-metrics-use-case.md`, issue #6). As
|
|
159
|
+
a preview-stage library this takes the cleanest shape with no compatibility shims; the
|
|
160
|
+
API additions below include breaking signature changes (see **Changed**).
|
|
161
|
+
|
|
162
|
+
### Fixed
|
|
163
|
+
|
|
164
|
+
- **`node_table()` / `collect()` / `graph()` no longer raise on valid Markdown.** Inline
|
|
165
|
+
elements were discovered over the whole source and parented by start offset, so
|
|
166
|
+
backtick pairing across a block boundary (an empty fence next to inline backticks)
|
|
167
|
+
produced an inline span escaping its parent block and raised a layer-nesting error.
|
|
168
|
+
Inline discovery is now scoped per leaf content block, so an inline node can never
|
|
169
|
+
straddle a block boundary; links/images/definitions are parented by full containment.
|
|
170
|
+
- **`sections()` / `toc()` recover every heading `blocks()` finds, and own their content
|
|
171
|
+
correctly.** Headings were re-derived from the blank-line paragraph view, dropping
|
|
172
|
+
tight headings and headings preceded by a non-blank line (e.g. an HTML-comment
|
|
173
|
+
marker), and section content was bucketed from that same view, so a heading glued to
|
|
174
|
+
its body lost the body.
|
|
175
|
+
Sections now derive entirely from the structural block tree—the heading set *and* each
|
|
176
|
+
section’s own content (`own_paragraphs()` / `blocks()` / sizes) come from the
|
|
177
|
+
section’s source region—so tight and marker-preceded headings own exactly their
|
|
178
|
+
content.
|
|
179
|
+
- **Section spans nest correctly** even when a blank-line paragraph straddles a later
|
|
180
|
+
heading (e.g. an embedded `---` block marko reads as a setext heading): each section
|
|
181
|
+
spans from its heading to the next same-or-higher heading (trimmed), which nests by
|
|
182
|
+
construction. Byte-identical to the prior span for well-formed documents.
|
|
183
|
+
- **Reference-definition nodes attach to their block.** A `link_ref_def` span included
|
|
184
|
+
the line’s trailing newline and escaped the containing paragraph, leaving the node
|
|
185
|
+
unparented so a block-scoped `collect()` missed it; spans are now trimmed like every
|
|
186
|
+
structural block.
|
|
187
|
+
|
|
188
|
+
### Added
|
|
189
|
+
|
|
190
|
+
- **Heading metadata on the structural block**: `Block.heading_info` (`HeadingInfo` with
|
|
191
|
+
parser-authoritative `level` and `title`) and the `Block.heading_level` convenience;
|
|
192
|
+
`HeadingInfo` is exported from `flexdoc.docs`. The node table reads heading level from
|
|
193
|
+
it.
|
|
194
|
+
- **Typed link forms**: `LinkForm` (`inline` / `reference` / `autolink` / `bare_url` /
|
|
195
|
+
`image` / `reference_definition`) and `Link.link_form`. `FlexDoc.links(link_forms=…)`
|
|
196
|
+
selects any forms (default: navigable links only), and `FlexDoc.images()` is a
|
|
197
|
+
convenience for image access.
|
|
198
|
+
Reference definitions (`[id]: url`) are surfaced as `NodeKind.link_ref_def` nodes and
|
|
199
|
+
via `links(link_forms={LinkForm.reference_definition})`.
|
|
200
|
+
- **`FlexDoc.prose_text()`**: prose-only text for editorial linting and prose metrics —
|
|
201
|
+
prose blocks (paragraphs/headings, and table cells when `include_tables=True`) with
|
|
202
|
+
inline code and footnote refs dropped, links/images replaced by their text/alt,
|
|
203
|
+
inline-HTML tags dropped (wrapped text kept), and heading/blockquote/list markers and
|
|
204
|
+
reference-definition lines stripped; from verbatim source slices (line wrapping
|
|
205
|
+
preserved, never reflowed) so spacing like a spaced em-dash is kept exactly.
|
|
206
|
+
- **`FlexDoc.block_at_offset()`**: the innermost structural `Block` containing an offset
|
|
207
|
+
(the structural counterpart of `paragraph_at_offset`; the name, freed in 0.1.0, now
|
|
208
|
+
correctly returns a `Block`).
|
|
209
|
+
- **Test-suite hardening**: adversarial corpus documents (`inline_pathology`,
|
|
210
|
+
`heading_edges`, `link_taxonomy`); cross-projection invariants tying `toc()` to the
|
|
211
|
+
heading blocks, inline nesting on the query surface, and link-form accounting; and a
|
|
212
|
+
dogfood test that parses every Markdown file in the repo and asserts the invariants.
|
|
213
|
+
See the spec’s “Why These Bugs Escaped the Tests” analysis.
|
|
214
|
+
|
|
215
|
+
### Changed
|
|
216
|
+
|
|
217
|
+
These are breaking, made cleanly (no aliases) given the preview status:
|
|
218
|
+
|
|
219
|
+
- **`Link` gains a required `link_form: LinkForm` field.** Direct `Link(...)`
|
|
220
|
+
construction must pass it.
|
|
221
|
+
- **`block_links()` returns all link-like constructs** (navigable links, images, and
|
|
222
|
+
reference definitions), each with a `link_form`; previously it returned navigable
|
|
223
|
+
links only. `FlexDoc.links()` filters to navigable links by default, so its default
|
|
224
|
+
result is unchanged.
|
|
225
|
+
- **`collect()` returns inline-kind nodes without `recursive=True`.** An inline-kind
|
|
226
|
+
request (e.g. `collect(kinds={NodeKind.link})`) now widens the candidate set instead
|
|
227
|
+
of silently returning `[]`—matching the documented behavior.
|
|
228
|
+
|
|
229
|
+
## 0.1.0 (2026-06-12)
|
|
230
|
+
|
|
231
|
+
First release.
|
|
232
|
+
|
|
233
|
+
### Added
|
|
234
|
+
|
|
235
|
+
- **Initial flexdoc package**, extracted from
|
|
236
|
+
[chopdiff](https://github.com/jlevy/chopdiff) as its own standalone distribution.
|
|
237
|
+
This is the document/markdown layer—`FlexDoc`, paragraphs/sentences, the block tree
|
|
238
|
+
and block types, sections, the node table, `collect()`, `DocGraph`, `SpanRef`, token
|
|
239
|
+
diffs/mappings, word tokenization, html-in-md, and read-time/token estimation—with no
|
|
240
|
+
dependency on chopdiff’s diff and windowed-transform machinery.
|
|
241
|
+
|
|
242
|
+
The import roots are `flexdoc.docs`, `flexdoc.html`, and `flexdoc.util`. Parse
|
|
243
|
+
behavior is unchanged from the `flexdoc.*` modules that previously shipped inside the
|
|
244
|
+
chopdiff wheel; this release packages them independently.
|
|
245
|
+
See `docs/project/specs/active/plan-2026-06-11-flexdoc-extraction.md`.
|
|
246
|
+
|
|
247
|
+
- **A deliberate root API**: the working set is importable from the package root —
|
|
248
|
+
`FlexDoc`, `DocGraph`, `Detail`, `SpanRef`, `BlockType`, `NodeKind`, `Layer`,
|
|
249
|
+
`TextUnit`—designed against the known downstream users and pinned by contract tests.
|
|
250
|
+
The render helpers for source-linked HTML (`render_node_attrs`,
|
|
251
|
+
`wrap_with_node_attrs`, `parse_source_span_attr`) are public in `flexdoc.docs`.
|
|
252
|
+
|
|
253
|
+
- **DocGraph paragraph view**: `Views.paragraphs` joins `toc`/`blocks`/`links`/
|
|
254
|
+
`sentences` in the serialized projection.
|
|
255
|
+
|
|
256
|
+
### Changed (Relative to the Modules as Shipped in Chopdiff)
|
|
257
|
+
|
|
258
|
+
The first standalone release also refines the API surface (the pre-publish design
|
|
259
|
+
review, `docs/project/review/senior-engineering-review-flexdoc-standalone-2026-06.md`);
|
|
260
|
+
these are intentional hard cuts with no compatibility aliases:
|
|
261
|
+
|
|
262
|
+
- **`TextDoc` is renamed `FlexDoc`**—the package’s single entry point, named for the
|
|
263
|
+
model it carries (all layered projections hang off it).
|
|
264
|
+
It is importable from the package root: `from flexdoc import FlexDoc`. The module is
|
|
265
|
+
`flexdoc.docs.flex_doc` (was `chopdiff.docs.text_doc`), and the design of record is
|
|
266
|
+
now `docs/flexdoc-spec.md` (was `textdoc-spec.md`).
|
|
267
|
+
- **`collect()` is fully keyword-only and the deprecated aliases are gone**: use
|
|
268
|
+
`subtree_of=` (was `scope=`, previously also positional) and `within=` (was
|
|
269
|
+
`contains=`).
|
|
270
|
+
- **Editing-view methods are named in paragraph terms**, so “block” always means the
|
|
271
|
+
structural layer: `FlexDoc.paragraph_at_offset` (was `block_at_offset`),
|
|
272
|
+
`FlexDoc.iter_paragraphs` (was `iter_blocks`), `Section.own_paragraphs`/
|
|
273
|
+
`subtree_paragraphs` (were `own_blocks`/`subtree_blocks`).
|
|
274
|
+
- **The export surface is settled**: `flexdoc.docs` now exports
|
|
275
|
+
`CodeInfo`/`TableInfo`/`ListInfo`, `resolve`/`resolve_and_update`,
|
|
276
|
+
`parse_blocks`/`walk_blocks`/`block_type_for`, and `DEFAULT_INCLUDE`; `flexdoc.html`
|
|
277
|
+
exports `html_p`, `html_tag`, `escape_attribute`, `tag_wrapper`, and
|
|
278
|
+
`identity_wrapper`. Link extraction is public as `flexdoc.docs.links.block_links`.
|
|
279
|
+
- **`Node.attrs` values are JSON-typed** (`AttrValue`), validated at `DocGraph`
|
|
280
|
+
serialization, and node-id assignment order is pinned and tested for cross-language
|
|
281
|
+
ports; layer nesting guarantees (`LAYER_NESTING`) are enforced at node-table build.
|
|
282
|
+
- Internally, the former `text_doc.py` was split into `flex_doc.py`, `paragraphs.py`,
|
|
283
|
+
`links.py`, and `sections.py` (package imports from `flexdoc.docs` are unaffected),
|
|
284
|
+
and `sections()` is now cached like the other derived views.
|
|
285
|
+
|
|
286
|
+
Migration from chopdiff in one pass: `chopdiff.docs.TextDoc` → `flexdoc.FlexDoc` (or
|
|
287
|
+
`flexdoc.docs.FlexDoc`), `chopdiff.docs.*` → `flexdoc.docs.*`, plus the method renames
|
|
288
|
+
above.
|
|
289
|
+
|
|
290
|
+
<!-- This document follows common-doc-guidelines.md.
|
|
291
|
+
See github.com/jlevy/practical-prose and review guidelines before editing.
|
|
292
|
+
-->
|