This is an automated email from the ASF dual-hosted git repository.

spmallette pushed a commit to branch afd
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git


The following commit(s) were added to refs/heads/afd by this push:
     new c88deafa66 Fix llms.txt index entries for book landing pages and 
tutorials
c88deafa66 is described below

commit c88deafa66deefd63cc508dabfb3604eba66891c
Author: Stephen Mallette <[email protected]>
AuthorDate: Sat Jul 25 19:40:57 2026 +0000

    Fix llms.txt index entries for book landing pages and tutorials
    
    The summary-driven split produced wrong llms.txt entries for every book's
    landing page: the document title broke off to its own page, leaving index.md
    owning the first chapter, so the index line showed that chapter's title and 
a
    scraped description (an image macro, the version line, or a mid-sentence
    fragment).
    
    - Splitter: fold the leading document-title section into the index page so 
its
      own llms-summary describes index.md and its content renders there, 
instead of
      breaking off to a separate _<doctitle>.md page.
    - Converter: for article-doctype pages (the tutorials), the title-bearing
      section is promoted to the document title, so read its llms-summary /
      allow-oversize from the promoted leading section (not just the Document) 
and
      emit the hidden markers on the landing page.
    - Curation: give the headingless gremlin-language-variants tutorial a titled
      summary, and replace four recipe summaries that had fallen back to 
generic or
      mid-sentence text (collections, shortest-path, edge-move,
      if-then-based-grouping) with proper descriptions.
    
    Result: all 240 pages, including every book/tutorial landing page, have a 
clean
    curated llms.txt description; build passes; 412 links resolve; 174 tests 
pass.
    
    Assisted-by: Claude Code:claude-opus-4-8
---
 docs/src/recipes/collections.asciidoc              |  2 +-
 docs/src/recipes/edge-move.asciidoc                |  2 +-
 docs/src/recipes/if-then-based-grouping.asciidoc   |  2 +-
 docs/src/recipes/shortest-path.asciidoc            |  2 +-
 .../gremlin-language-variants/index.asciidoc       |  3 +++
 .../tinkerpop/tinkeradoc/MarkdownConverter.java    | 28 +++++++++++++++++++-
 .../tinkerpop/tinkeradoc/MarkdownSplitter.java     | 30 ++++++++++++++++++++++
 7 files changed, 64 insertions(+), 5 deletions(-)

diff --git a/docs/src/recipes/collections.asciidoc 
b/docs/src/recipes/collections.asciidoc
index 1dab723f74..5c1d493a53 100644
--- a/docs/src/recipes/collections.asciidoc
+++ b/docs/src/recipes/collections.asciidoc
@@ -16,7 +16,7 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License.
 ////
-[llms-summary="Recipe: A recipe for collections in Gremlin."]
+[llms-summary="Recipe: reshaping List and Map collections within a traversal — 
unfolding, folding, grouping, and reformatting side-effect data."]
 [[collections]]
 == Collections
 
diff --git a/docs/src/recipes/edge-move.asciidoc 
b/docs/src/recipes/edge-move.asciidoc
index 25a416d030..5f8efcec55 100644
--- a/docs/src/recipes/edge-move.asciidoc
+++ b/docs/src/recipes/edge-move.asciidoc
@@ -16,7 +16,7 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License.
 ////
-[llms-summary="Recipe: A recipe for moving an edge in Gremlin."]
+[llms-summary="Recipe: moving an existing edge from one incident vertex to 
another by recreating it with new endpoints and dropping the original."]
 [[edge-move]]
 == Moving an Edge
 
diff --git a/docs/src/recipes/if-then-based-grouping.asciidoc 
b/docs/src/recipes/if-then-based-grouping.asciidoc
index 5d88b9a6ab..753cbb70e9 100644
--- a/docs/src/recipes/if-then-based-grouping.asciidoc
+++ b/docs/src/recipes/if-then-based-grouping.asciidoc
@@ -16,7 +16,7 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License.
 ////
-[llms-summary="Recipe: Consider the following traversal over the \"modern\" 
toy graph:"]
+[llms-summary="Recipe: bucketing elements into groups by if/then-style 
conditions using choose() within group()."]
 [[if-then-based-grouping]]
 == If-Then Based Grouping
 
diff --git a/docs/src/recipes/shortest-path.asciidoc 
b/docs/src/recipes/shortest-path.asciidoc
index 89a0577e9d..66d71f6b12 100644
--- a/docs/src/recipes/shortest-path.asciidoc
+++ b/docs/src/recipes/shortest-path.asciidoc
@@ -16,7 +16,7 @@ KIND, either express or implied.  See the License for the
 specific language governing permissions and limitations
 under the License.
 ////
-[llms-summary="Recipe: A recipe for shortest path in Gremlin."]
+[llms-summary="Recipe: finding the shortest path between vertices with 
Gremlin, using repeat()/until() and path-length comparison."]
 [[shortest-path]]
 == Shortest Path
 
diff --git a/docs/src/tutorials/gremlin-language-variants/index.asciidoc 
b/docs/src/tutorials/gremlin-language-variants/index.asciidoc
index 1a3f285e9f..64779d3915 100644
--- a/docs/src/tutorials/gremlin-language-variants/index.asciidoc
+++ b/docs/src/tutorials/gremlin-language-variants/index.asciidoc
@@ -24,6 +24,9 @@ 
image::apache-tinkerpop-logo.png[width=500,link="https://tinkerpop.apache.org";]
 
 *x.y.z*
 
+[llms-summary="A stub tutorial that redirects to the reference documentation 
on Gremlin drivers and language variants."]
+= Gremlin Language Variants
+
 image::gremlin-house-of-mirrors.png[width=1024]
 
 Please see the 
link:https://tinkerpop.apache.org/docs/x.y.z/reference/#gremlin-drivers-variants[Reference
 Documentation]
diff --git 
a/docs/tinkeradoc-extension/src/main/java/org/apache/tinkerpop/tinkeradoc/MarkdownConverter.java
 
b/docs/tinkeradoc-extension/src/main/java/org/apache/tinkerpop/tinkeradoc/MarkdownConverter.java
index e7b617b503..26269ec1e0 100644
--- 
a/docs/tinkeradoc-extension/src/main/java/org/apache/tinkerpop/tinkeradoc/MarkdownConverter.java
+++ 
b/docs/tinkeradoc-extension/src/main/java/org/apache/tinkerpop/tinkeradoc/MarkdownConverter.java
@@ -167,9 +167,17 @@ public class MarkdownConverter extends StringConverter {
         // already appears as an H1 there (matching the HTML backend's single 
<h1 class="sect0">).
         // In an article, the title is not part of the content, so emit it as 
the leading H1.
         if (title != null && !title.isEmpty() && !isBook(doc)) {
+            // Article doctype: the title-bearing section is promoted to the 
document title, so its
+            // curated llms-summary / allow-oversize attributes are not 
visible via convertSection.
+            // They resolve on the Document (when set as document attributes) 
or on the leading
+            // section AsciidoctorJ promoted to the title; check both so the 
landing page carries the
+            // hidden markers that drive the llms.txt description and the size 
lint.
             final StringBuilder head = new StringBuilder();
             appendAnchor(head, doc.getId());
-            head.append("# ").append(title).append("\n\n").append(content);
+            head.append("# ").append(title).append("\n\n");
+            appendLlmsSummary(head, docLevelAttribute(doc, LLMS_SUMMARY_ATTR));
+            appendAllowOversizeMarker(head, docLevelAttribute(doc, 
LLMS_ALLOW_OVERSIZE_ATTR));
+            head.append(content);
             assembled = head.toString();
         } else {
             assembled = content;
@@ -181,6 +189,24 @@ public class MarkdownConverter extends StringConverter {
         return substituteVersion(doc, assembled);
     }
 
+    /**
+     * Resolves a doc-level attribute for the article title path: prefers the 
Document's own
+     * attribute, falling back to the leading section that AsciidoctorJ 
promoted to the title (which
+     * is where a {@code [llms-summary]} on the article's {@code == Title} 
actually lands).
+     */
+    private static Object docLevelAttribute(final Document doc, final String 
name) {
+        final Object onDoc = doc.getAttribute(name);
+        if (onDoc != null) return onDoc;
+        for (final StructuralNode block : doc.getBlocks()) {
+            if (block instanceof Section) {
+                final Object onSection = block.getAttribute(name);
+                if (onSection != null) return onSection;
+                break; // only the first/leading section is the promoted title
+            }
+        }
+        return null;
+    }
+
     /** Replaces {@code x.y.z} with the resolved TinkerPop version, if 
available. */
     private static String substituteVersion(final Document doc, final String 
text) {
         final Object version = 
firstNonNull(doc.getAttribute("tinkerpop-version"), 
doc.getAttribute("revnumber"));
diff --git 
a/docs/tinkeradoc-extension/src/main/java/org/apache/tinkerpop/tinkeradoc/MarkdownSplitter.java
 
b/docs/tinkeradoc-extension/src/main/java/org/apache/tinkerpop/tinkeradoc/MarkdownSplitter.java
index fcc65ba381..8b37863088 100644
--- 
a/docs/tinkeradoc-extension/src/main/java/org/apache/tinkerpop/tinkeradoc/MarkdownSplitter.java
+++ 
b/docs/tinkeradoc-extension/src/main/java/org/apache/tinkerpop/tinkeradoc/MarkdownSplitter.java
@@ -221,6 +221,18 @@ class MarkdownSplitter {
         // up to — but not including — the next summarized descendant, which 
breaks off to its own
         // page. There is no size-based splitting; page size is an author 
concern surfaced by the
         // build's size lint, with intentional exceptions flagged via 
allow-oversize.
+        // The document title (the single leading top-level section — the 
book's "= Title") is the
+        // landing page: it owns index.md so its own llms-summary describes 
the index and its content
+        // renders there, rather than breaking off to a page of its own. Fold 
it into the root so the
+        // root preamble + doctitle render together as index.md. Everything 
below still breaks on
+        // summaries as usual.
+        final Node docTitle = leadingDocTitle(root);
+        if (docTitle != null) {
+            root.lines.addAll(docTitle.lines);
+            root.children.remove(docTitle);
+            root.children.addAll(0, docTitle.children);
+        }
+
         final Map<String, String> anchorToFile = new LinkedHashMap<>();
         final List<PagePlan> plans = new ArrayList<>();
         final PagePlan index = new PagePlan(indexFileName, root);
@@ -238,6 +250,24 @@ class MarkdownSplitter {
         return pages;
     }
 
+    /**
+     * Returns the document-title node to fold into the index page, or {@code 
null} if there is none.
+     * The doctitle is the book's leading section: the root's first child, 
when that child is the sole
+     * shallowest-level top section (i.e. the other top-level siblings are its 
heading level or
+     * deeper). In practice this is the {@code = Title} that AsciidoctorJ 
renders as the first h1.
+     */
+    private static Node leadingDocTitle(final Node root) {
+        if (root.children.isEmpty()) return null;
+        final Node first = root.children.get(0);
+        // Only treat it as the doctitle when it carries content meant to lead 
the landing page: it
+        // has a summary (so it would otherwise wrongly break off) or the root 
has no preamble of its
+        // own. Guard against odd trees by requiring it be the shallowest 
top-level section.
+        for (final Node sib : root.children) {
+            if (sib != first && sib.level < first.level) return null;
+        }
+        return first;
+    }
+
     /**
      * Walks the tree creating a page for every summarized descendant of 
{@code node} (the node's own
      * page having already been created). Recurses through the whole tree so 
nested summarized

Reply via email to