From 17d78a60bcc6288ee106025ffdd5e71242f05e3f Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 9 Jan 2026 00:08:51 +0000
Subject: [PATCH 1/9] Initial plan
From 809fb585bd272f3aa99cd7d1b6a2034efed6994a Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 9 Jan 2026 00:22:39 +0000
Subject: [PATCH 2/9] Work in progress on applies_to in LLM markdown output
Co-authored-by: reakaleek <16325797+reakaleek@users.noreply.github.com>
---
.../Exporters/LlmMarkdownExporter.cs | 9 ++
.../LlmMarkdown/LlmAppliesToHelper.cs | 94 +++++++++++++++++++
.../LlmMarkdown/LlmBlockRenderers.cs | 13 ++-
.../LlmMarkdown/LlmInlineRenderers.cs | 9 +-
.../LlmMarkdown/LlmMarkdownOutput.fs | 4 +-
5 files changed, 125 insertions(+), 4 deletions(-)
create mode 100644 src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmAppliesToHelper.cs
diff --git a/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs b/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs
index 09e78ddad..6d8675402 100644
--- a/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs
+++ b/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs
@@ -8,6 +8,7 @@
using Elastic.Documentation.Configuration;
using Elastic.Documentation.Configuration.Products;
using Elastic.Markdown.Helpers;
+using Elastic.Markdown.Myst.Renderers.LlmMarkdown;
using Markdig.Syntax;
namespace Elastic.Markdown.Exporters;
@@ -155,6 +156,14 @@ private string CreateLlmContentWithMetadata(MarkdownExportFileContext context, s
_ = metadata.AppendLine($" - {item}");
}
+ // Add applies_to information from frontmatter
+ if (sourceFile.YamlFrontMatter?.AppliesTo is not null)
+ {
+ var appliesToText = LlmAppliesToHelper.RenderApplicableTo(sourceFile.YamlFrontMatter.AppliesTo, context.BuildContext);
+ if (!string.IsNullOrEmpty(appliesToText))
+ _ = metadata.AppendLine($"applies_to: {appliesToText}");
+ }
+
_ = metadata.AppendLine("---");
_ = metadata.AppendLine();
_ = metadata.AppendLine($"# {sourceFile.Title}");
diff --git a/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmAppliesToHelper.cs b/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmAppliesToHelper.cs
new file mode 100644
index 000000000..23b1d6ee4
--- /dev/null
+++ b/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmAppliesToHelper.cs
@@ -0,0 +1,94 @@
+// Licensed to Elasticsearch B.V under one or more agreements.
+// Elasticsearch B.V licenses this file to you under the Apache 2.0 License.
+// See the LICENSE file in the project root for more information
+
+using System.Text;
+using Elastic.Documentation;
+using Elastic.Documentation.AppliesTo;
+using Elastic.Documentation.Configuration;
+using Elastic.Markdown.Myst.Components;
+
+namespace Elastic.Markdown.Myst.Renderers.LlmMarkdown;
+
+///
+/// Helper class to render ApplicableTo information in LLM-friendly text format
+///
+public static class LlmAppliesToHelper
+{
+ ///
+ /// Converts ApplicableTo to a readable text format for LLM consumption
+ ///
+ public static string RenderApplicableTo(ApplicableTo? appliesTo, IDocumentationConfigurationContext buildContext)
+ {
+ if (appliesTo is null || appliesTo == ApplicableTo.All)
+ return string.Empty;
+
+ var viewModel = new ApplicableToViewModel
+ {
+ AppliesTo = appliesTo,
+ Inline = false,
+ ShowTooltip = false,
+ VersionsConfig = buildContext.VersionsConfiguration
+ };
+
+ var items = viewModel.GetApplicabilityItems();
+ if (items.Count == 0)
+ return string.Empty;
+
+ var itemList = new List();
+
+ foreach (var item in items)
+ {
+ var text = BuildApplicabilityText(item);
+ if (!string.IsNullOrEmpty(text))
+ itemList.Add(text);
+ }
+
+ if (itemList.Count == 0)
+ return string.Empty;
+
+ return string.Join(", ", itemList);
+ }
+
+ private static string BuildApplicabilityText(ApplicabilityItem item)
+ {
+ // For LLM output, use the shorter Key name for better readability
+ var parts = new List { item.Key };
+
+ // For LLM output, show the actual applicability information directly
+ var applicability = item.Applicability;
+
+ // Add lifecycle if it's not GA
+ if (applicability.Lifecycle != ProductLifecycle.GenerallyAvailable)
+ parts.Add(applicability.GetLifeCycleName());
+
+ // Add version information if present
+ if (applicability.Version is not null and not AllVersionsSpec)
+ {
+ var versionText = FormatVersion(applicability.Version);
+ if (!string.IsNullOrEmpty(versionText))
+ parts.Add(versionText);
+ }
+
+ return string.Join(" ", parts);
+ }
+
+ private static string FormatVersion(VersionSpec versionSpec)
+ {
+ var min = versionSpec.Min;
+ var max = versionSpec.Max;
+ var showMinPatch = versionSpec.ShowMinPatch;
+ var showMaxPatch = versionSpec.ShowMaxPatch;
+
+ static string FormatSemVersion(SemVersion v, bool showPatch) =>
+ showPatch ? $"{v.Major}.{v.Minor}.{v.Patch}" : $"{v.Major}.{v.Minor}";
+
+ return versionSpec.Kind switch
+ {
+ VersionSpecKind.GreaterThanOrEqual => $"{FormatSemVersion(min, showMinPatch)}+",
+ VersionSpecKind.Range when max is not null => $"{FormatSemVersion(min, showMinPatch)}-{FormatSemVersion(max, showMaxPatch)}",
+ VersionSpecKind.Exact => FormatSemVersion(min, showMinPatch),
+ _ => string.Empty
+ };
+ }
+}
diff --git a/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmBlockRenderers.cs b/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmBlockRenderers.cs
index d5eeec1c9..35b32f0f0 100644
--- a/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmBlockRenderers.cs
+++ b/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmBlockRenderers.cs
@@ -436,7 +436,18 @@ protected override void Write(LlmMarkdownRenderer renderer, DirectiveBlock obj)
switch (obj)
{
case IBlockAppliesTo appliesBlock when !string.IsNullOrEmpty(appliesBlock.AppliesToDefinition):
- renderer.Writer.Write($" applies-to=\"{appliesBlock.AppliesToDefinition}\"");
+ // Check if the block has a parsed AppliesTo object (e.g., AdmonitionBlock)
+ var appliesToText = obj switch
+ {
+ AdmonitionBlock admonition when admonition.AppliesTo is not null =>
+ LlmAppliesToHelper.RenderApplicableTo(admonition.AppliesTo, renderer.BuildContext),
+ _ => null
+ };
+ // Fallback to raw definition if parsing didn't work or returned empty
+ appliesToText ??= appliesBlock.AppliesToDefinition;
+
+ if (!string.IsNullOrEmpty(appliesToText))
+ renderer.Writer.Write($" applies-to=\"{appliesToText}\"");
break;
}
diff --git a/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmInlineRenderers.cs b/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmInlineRenderers.cs
index a78bcd534..5be49bf2b 100644
--- a/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmInlineRenderers.cs
+++ b/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmInlineRenderers.cs
@@ -4,6 +4,7 @@
using Elastic.Markdown.Myst.InlineParsers.Substitution;
using Elastic.Markdown.Myst.Roles;
+using Elastic.Markdown.Myst.Roles.AppliesTo;
using Elastic.Markdown.Myst.Roles.Kbd;
using Markdig.Renderers;
using Markdig.Syntax.Inlines;
@@ -101,7 +102,13 @@ protected override void Write(LlmMarkdownRenderer renderer, RoleLeaf obj)
renderer.Writer.Write(output);
break;
}
- // TODO: Add support for applies_to role
+ case AppliesToRole appliesTo:
+ {
+ var text = LlmAppliesToHelper.RenderApplicableTo(appliesTo.AppliesTo, renderer.BuildContext);
+ if (!string.IsNullOrEmpty(text))
+ renderer.Writer.Write($"[{text}]");
+ break;
+ }
default:
{
new LlmCodeInlineRenderer().Write(renderer, obj);
diff --git a/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs b/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs
index 123455e19..2abc73147 100644
--- a/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs
+++ b/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs
@@ -133,9 +133,9 @@ This is an inline {applies_to}`stack: preview 9.1` element.
"""
[]
- let ``converts to plain text with optional comment`` () =
+ let ``converts to readable text`` () =
markdown |> convertsToNewLLM """
- This is an inline `stack: preview 9.1` element.
+ This is an inline [Stack Preview 9.1+] element.
"""
type ``admonition directive`` () =
From c2b91808eab99cad48966aab681af79d6730c33d Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 9 Jan 2026 00:28:54 +0000
Subject: [PATCH 3/9] Implement LLM-friendly applies_to rendering using popover
data
Co-authored-by: reakaleek <16325797+reakaleek@users.noreply.github.com>
---
.../Exporters/LlmMarkdownExporter.cs | 4 +-
.../LlmMarkdown/LlmAppliesToHelper.cs | 108 ++++++++++--------
.../LlmMarkdown/LlmBlockRenderers.cs | 2 +-
.../LlmMarkdown/LlmMarkdownOutput.fs | 2 +-
4 files changed, 65 insertions(+), 51 deletions(-)
diff --git a/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs b/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs
index 6d8675402..d5f2371e2 100644
--- a/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs
+++ b/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs
@@ -159,9 +159,9 @@ private string CreateLlmContentWithMetadata(MarkdownExportFileContext context, s
// Add applies_to information from frontmatter
if (sourceFile.YamlFrontMatter?.AppliesTo is not null)
{
- var appliesToText = LlmAppliesToHelper.RenderApplicableTo(sourceFile.YamlFrontMatter.AppliesTo, context.BuildContext);
+ var appliesToText = LlmAppliesToHelper.RenderAppliesToBlock(sourceFile.YamlFrontMatter.AppliesTo, context.BuildContext);
if (!string.IsNullOrEmpty(appliesToText))
- _ = metadata.AppendLine($"applies_to: {appliesToText}");
+ _ = metadata.Append(appliesToText);
}
_ = metadata.AppendLine("---");
diff --git a/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmAppliesToHelper.cs b/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmAppliesToHelper.cs
index 23b1d6ee4..c55a0ff01 100644
--- a/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmAppliesToHelper.cs
+++ b/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmAppliesToHelper.cs
@@ -16,13 +16,47 @@ namespace Elastic.Markdown.Myst.Renderers.LlmMarkdown;
public static class LlmAppliesToHelper
{
///
- /// Converts ApplicableTo to a readable text format for LLM consumption
+ /// Converts ApplicableTo to a readable text format for LLM consumption (block level - for page or section)
+ ///
+ public static string RenderAppliesToBlock(ApplicableTo? appliesTo, IDocumentationConfigurationContext buildContext)
+ {
+ if (appliesTo is null || appliesTo == ApplicableTo.All)
+ return string.Empty;
+
+ var items = GetApplicabilityItems(appliesTo, buildContext);
+ if (items.Count == 0)
+ return string.Empty;
+
+ var sb = new StringBuilder();
+ _ = sb.AppendLine();
+ _ = sb.AppendLine("This applies to:");
+
+ foreach (var (productName, availabilityText) in items)
+ _ = sb.AppendLine($"- {availabilityText} for {productName}");
+
+ return sb.ToString();
+ }
+
+ ///
+ /// Converts ApplicableTo to a readable inline text format for LLM consumption
///
public static string RenderApplicableTo(ApplicableTo? appliesTo, IDocumentationConfigurationContext buildContext)
{
if (appliesTo is null || appliesTo == ApplicableTo.All)
return string.Empty;
+ var items = GetApplicabilityItems(appliesTo, buildContext);
+ if (items.Count == 0)
+ return string.Empty;
+
+ var itemList = items.Select(item => $"{item.availabilityText} for {item.productName}").ToList();
+ return string.Join(", ", itemList);
+ }
+
+ private static List<(string productName, string availabilityText)> GetApplicabilityItems(
+ ApplicableTo appliesTo,
+ IDocumentationConfigurationContext buildContext)
+ {
var viewModel = new ApplicableToViewModel
{
AppliesTo = appliesTo,
@@ -31,64 +65,44 @@ public static string RenderApplicableTo(ApplicableTo? appliesTo, IDocumentationC
VersionsConfig = buildContext.VersionsConfiguration
};
- var items = viewModel.GetApplicabilityItems();
- if (items.Count == 0)
- return string.Empty;
+ var applicabilityItems = viewModel.GetApplicabilityItems();
+ var results = new List<(string productName, string availabilityText)>();
- var itemList = new List();
-
- foreach (var item in items)
+ foreach (var item in applicabilityItems)
{
- var text = BuildApplicabilityText(item);
- if (!string.IsNullOrEmpty(text))
- itemList.Add(text);
- }
+ var renderData = item.RenderData;
+ var productName = item.Key;
- if (itemList.Count == 0)
- return string.Empty;
+ // Get the availability text from the popover data
+ var availabilityText = GetAvailabilityText(renderData);
+ if (!string.IsNullOrEmpty(availabilityText))
+ results.Add((productName, availabilityText));
+ }
- return string.Join(", ", itemList);
+ return results;
}
- private static string BuildApplicabilityText(ApplicabilityItem item)
+ private static string GetAvailabilityText(ApplicabilityRenderer.ApplicabilityRenderData renderData)
{
- // For LLM output, use the shorter Key name for better readability
- var parts = new List { item.Key };
-
- // For LLM output, show the actual applicability information directly
- var applicability = item.Applicability;
-
- // Add lifecycle if it's not GA
- if (applicability.Lifecycle != ProductLifecycle.GenerallyAvailable)
- parts.Add(applicability.GetLifeCycleName());
-
- // Add version information if present
- if (applicability.Version is not null and not AllVersionsSpec)
+ // Use the first availability item's text if available (this is what the popover shows)
+ if (renderData.PopoverData?.AvailabilityItems is { Length: > 0 } items)
{
- var versionText = FormatVersion(applicability.Version);
- if (!string.IsNullOrEmpty(versionText))
- parts.Add(versionText);
+ // The popover text already includes lifecycle and version info
+ // e.g., "Generally available since 9.1", "Preview in 8.0", etc.
+ return items[0].Text;
}
- return string.Join(" ", parts);
- }
+ // Fallback to constructing from badge data
+ var parts = new List();
- private static string FormatVersion(VersionSpec versionSpec)
- {
- var min = versionSpec.Min;
- var max = versionSpec.Max;
- var showMinPatch = versionSpec.ShowMinPatch;
- var showMaxPatch = versionSpec.ShowMaxPatch;
+ if (!string.IsNullOrEmpty(renderData.LifecycleName) && renderData.LifecycleName != "Generally available")
+ parts.Add(renderData.LifecycleName);
- static string FormatSemVersion(SemVersion v, bool showPatch) =>
- showPatch ? $"{v.Major}.{v.Minor}.{v.Patch}" : $"{v.Major}.{v.Minor}";
+ if (!string.IsNullOrEmpty(renderData.Version))
+ parts.Add(renderData.Version);
+ else if (!string.IsNullOrEmpty(renderData.BadgeLifecycleText))
+ parts.Add(renderData.BadgeLifecycleText);
- return versionSpec.Kind switch
- {
- VersionSpecKind.GreaterThanOrEqual => $"{FormatSemVersion(min, showMinPatch)}+",
- VersionSpecKind.Range when max is not null => $"{FormatSemVersion(min, showMinPatch)}-{FormatSemVersion(max, showMaxPatch)}",
- VersionSpecKind.Exact => FormatSemVersion(min, showMinPatch),
- _ => string.Empty
- };
+ return parts.Count > 0 ? string.Join(" ", parts) : "Available";
}
}
diff --git a/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmBlockRenderers.cs b/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmBlockRenderers.cs
index 35b32f0f0..26a6e5b74 100644
--- a/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmBlockRenderers.cs
+++ b/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmBlockRenderers.cs
@@ -445,7 +445,7 @@ protected override void Write(LlmMarkdownRenderer renderer, DirectiveBlock obj)
};
// Fallback to raw definition if parsing didn't work or returned empty
appliesToText ??= appliesBlock.AppliesToDefinition;
-
+
if (!string.IsNullOrEmpty(appliesToText))
renderer.Writer.Write($" applies-to=\"{appliesToText}\"");
break;
diff --git a/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs b/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs
index 2abc73147..3b9ac5b2d 100644
--- a/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs
+++ b/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs
@@ -135,7 +135,7 @@ This is an inline {applies_to}`stack: preview 9.1` element.
[]
let ``converts to readable text`` () =
markdown |> convertsToNewLLM """
- This is an inline [Stack Preview 9.1+] element.
+ This is an inline [Planned in Stack] element.
"""
type ``admonition directive`` () =
From 1c2a134dc023bc88000aa4af8c21612cbca507e7 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 9 Jan 2026 00:34:20 +0000
Subject: [PATCH 4/9] Update test expectations for LLM applies_to rendering
Co-authored-by: reakaleek <16325797+reakaleek@users.noreply.github.com>
---
tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs b/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs
index 3b9ac5b2d..1d57025cf 100644
--- a/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs
+++ b/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs
@@ -135,7 +135,7 @@ This is an inline {applies_to}`stack: preview 9.1` element.
[]
let ``converts to readable text`` () =
markdown |> convertsToNewLLM """
- This is an inline [Planned in Stack] element.
+ This is an inline [Planned for Stack] element.
"""
type ``admonition directive`` () =
@@ -214,15 +214,15 @@ This is a custom admonition with applies_to information.
[]
let ``renders correctly with applies_to information`` () =
markdown |> convertsToNewLLM """
-
+
This is a note admonition with applies_to information.
-
+
This is a warning admonition with applies_to information.
-
+
This is a tip admonition with applies_to information.
From fcbf94bc84327aed4fbf8e49101c7dbf47e961a8 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 9 Jan 2026 00:37:19 +0000
Subject: [PATCH 5/9] Address code review comments with clarifying
documentation
Co-authored-by: reakaleek <16325797+reakaleek@users.noreply.github.com>
---
.../Myst/Renderers/LlmMarkdown/LlmAppliesToHelper.cs | 2 ++
.../Myst/Renderers/LlmMarkdown/LlmBlockRenderers.cs | 2 ++
2 files changed, 4 insertions(+)
diff --git a/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmAppliesToHelper.cs b/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmAppliesToHelper.cs
index c55a0ff01..58568bd19 100644
--- a/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmAppliesToHelper.cs
+++ b/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmAppliesToHelper.cs
@@ -89,6 +89,8 @@ private static string GetAvailabilityText(ApplicabilityRenderer.ApplicabilityRen
{
// The popover text already includes lifecycle and version info
// e.g., "Generally available since 9.1", "Preview in 8.0", etc.
+ // We use the first item because it represents the most current/relevant status
+ // (items are sorted by version descending in ApplicabilityRenderer)
return items[0].Text;
}
diff --git a/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmBlockRenderers.cs b/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmBlockRenderers.cs
index 26a6e5b74..5b1e44d3f 100644
--- a/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmBlockRenderers.cs
+++ b/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmBlockRenderers.cs
@@ -437,6 +437,8 @@ protected override void Write(LlmMarkdownRenderer renderer, DirectiveBlock obj)
{
case IBlockAppliesTo appliesBlock when !string.IsNullOrEmpty(appliesBlock.AppliesToDefinition):
// Check if the block has a parsed AppliesTo object (e.g., AdmonitionBlock)
+ // Only AdmonitionBlock currently parses the YAML into an ApplicableTo object
+ // Other directive types may implement IBlockAppliesTo but not parse it
var appliesToText = obj switch
{
AdmonitionBlock admonition when admonition.AppliesTo is not null =>
From 0c1252ff2d1319ec0fe76e4a2df95313e8f90545 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 9 Jan 2026 00:46:38 +0000
Subject: [PATCH 6/9] Add test for page-level applies_to in frontmatter
Co-authored-by: reakaleek <16325797+reakaleek@users.noreply.github.com>
---
.../LlmMarkdown/LlmMarkdownOutput.fs | 35 +++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs b/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs
index 1d57025cf..15889331e 100644
--- a/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs
+++ b/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs
@@ -751,3 +751,38 @@ type ``images in tables`` () =
|---------------------------------------|------|
|  | Logo |
"""
+
+type ``page level applies_to in frontmatter`` () =
+ static let markdown = Setup.Document """---
+applies_to:
+ stack: ga 8.5
+ serverless: preview
+---
+
+# Test Page
+
+This is a test page with applies_to frontmatter.
+"""
+
+ []
+ let ``parses applies_to frontmatter correctly`` () =
+ // Test that the applies_to frontmatter is correctly parsed
+ let results = markdown.Value
+ let defaultFile = results.MarkdownResults |> Seq.find (fun r -> r.File.RelativePath = "index.md")
+
+ // Test that the file has the correct applies_to information
+ test <@ defaultFile.File.YamlFrontMatter <> null @>
+ match defaultFile.File.YamlFrontMatter with
+ | NonNull yamlFrontMatter ->
+ test <@ yamlFrontMatter.AppliesTo <> null @>
+ match yamlFrontMatter.AppliesTo with
+ | NonNull appliesTo ->
+ // Verify stack applies_to
+ match appliesTo.Stack with
+ | NonNull stack -> test <@ stack.Count > 0 @>
+ | _ -> ()
+
+ // Verify serverless applies_to
+ test <@ appliesTo.Serverless <> null @>
+ | _ -> ()
+ | _ -> ()
From e8369f50f46773e3937619acb7b3fbc80822c299 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 9 Jan 2026 11:53:11 +0000
Subject: [PATCH 7/9] Improve page-level applies_to test with clearer
assertions
Co-authored-by: reakaleek <16325797+reakaleek@users.noreply.github.com>
---
.../LlmMarkdown/LlmMarkdownOutput.fs | 26 +++++++++----------
1 file changed, 13 insertions(+), 13 deletions(-)
diff --git a/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs b/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs
index 15889331e..466000ba1 100644
--- a/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs
+++ b/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs
@@ -765,24 +765,24 @@ This is a test page with applies_to frontmatter.
"""
[]
- let ``parses applies_to frontmatter correctly`` () =
- // Test that the applies_to frontmatter is correctly parsed
+ let ``outputs content correctly`` () =
+ // The page content should be rendered in LLM markdown
+ markdown |> convertsToNewLLM """
+This is a test page with applies_to frontmatter.
+"""
+
+ []
+ let ``parses applies_to from frontmatter`` () =
+ // Verify that applies_to frontmatter is parsed into the ApplicableTo object
+ // Note: The actual "This applies to:" text is added during export in LlmMarkdownExporter.CreateLlmContentWithMetadata
+ // which is not tested by convertsToNewLLM (that only tests document content, not the full export metadata)
let results = markdown.Value
let defaultFile = results.MarkdownResults |> Seq.find (fun r -> r.File.RelativePath = "index.md")
- // Test that the file has the correct applies_to information
+ // Verify frontmatter was parsed
test <@ defaultFile.File.YamlFrontMatter <> null @>
match defaultFile.File.YamlFrontMatter with
| NonNull yamlFrontMatter ->
+ // Verify applies_to was parsed
test <@ yamlFrontMatter.AppliesTo <> null @>
- match yamlFrontMatter.AppliesTo with
- | NonNull appliesTo ->
- // Verify stack applies_to
- match appliesTo.Stack with
- | NonNull stack -> test <@ stack.Count > 0 @>
- | _ -> ()
-
- // Verify serverless applies_to
- test <@ appliesTo.Serverless <> null @>
- | _ -> ()
| _ -> ()
From b3a3094d341147148f692195854cf9f29993c1c4 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 9 Jan 2026 11:57:46 +0000
Subject: [PATCH 8/9] Add clearer documentation for page-level applies_to test
with expected export format
Co-authored-by: reakaleek <16325797+reakaleek@users.noreply.github.com>
---
.../Framework/LlmMarkdownAssertions.fs | 18 ++++++++++++
.../LlmMarkdown/LlmMarkdownOutput.fs | 29 +++++++++++++++----
2 files changed, 41 insertions(+), 6 deletions(-)
diff --git a/tests/authoring/Framework/LlmMarkdownAssertions.fs b/tests/authoring/Framework/LlmMarkdownAssertions.fs
index 91c6404ca..ffbd9ce6a 100644
--- a/tests/authoring/Framework/LlmMarkdownAssertions.fs
+++ b/tests/authoring/Framework/LlmMarkdownAssertions.fs
@@ -37,5 +37,23 @@ module LlmMarkdownAssertions =
-- ACTUAL --
{actualLLM}
+"""
+ raise (XunitException(msg))
+
+ []
+ let llmOutputContains (expectedText: string) (actual: Lazy) =
+ // Check if the document content contains the expected text
+ let results = actual.Value
+ let defaultFile = results.MarkdownResults |> Seq.find (fun r -> r.File.RelativePath = "index.md")
+ let actualLLM = toLlmMarkdown defaultFile
+
+ if not (actualLLM.Contains(expectedText)) then
+ let msg = $"""LLM output does not contain expected text
+
+-- EXPECTED TO CONTAIN --
+{expectedText}
+
+-- ACTUAL OUTPUT --
+{actualLLM}
"""
raise (XunitException(msg))
diff --git a/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs b/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs
index 466000ba1..602782750 100644
--- a/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs
+++ b/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs
@@ -765,17 +765,27 @@ This is a test page with applies_to frontmatter.
"""
[]
- let ``outputs content correctly`` () =
+ let ``renders content correctly`` () =
// The page content should be rendered in LLM markdown
markdown |> convertsToNewLLM """
This is a test page with applies_to frontmatter.
"""
[]
- let ``parses applies_to from frontmatter`` () =
- // Verify that applies_to frontmatter is parsed into the ApplicableTo object
- // Note: The actual "This applies to:" text is added during export in LlmMarkdownExporter.CreateLlmContentWithMetadata
- // which is not tested by convertsToNewLLM (that only tests document content, not the full export metadata)
+ let ``frontmatter applies_to is exported in metadata section`` () =
+ // The applies_to from frontmatter is processed and would appear in the exported LLM file
+ // In the format:
+ // ---
+ // title: Test Page
+ // description: ...
+ //
+ // This applies to:
+ // - Generally available since 8.5 for Stack
+ // - Preview for Serverless
+ // ---
+ //
+ // This happens in LlmMarkdownExporter.CreateLlmContentWithMetadata during export.
+ // We verify the frontmatter is parsed correctly so it can be used during export.
let results = markdown.Value
let defaultFile = results.MarkdownResults |> Seq.find (fun r -> r.File.RelativePath = "index.md")
@@ -783,6 +793,13 @@ This is a test page with applies_to frontmatter.
test <@ defaultFile.File.YamlFrontMatter <> null @>
match defaultFile.File.YamlFrontMatter with
| NonNull yamlFrontMatter ->
- // Verify applies_to was parsed
+ // Verify applies_to was parsed and contains expected data
test <@ yamlFrontMatter.AppliesTo <> null @>
+ match yamlFrontMatter.AppliesTo with
+ | NonNull appliesTo ->
+ // Verify Stack configuration exists
+ test <@ appliesTo.Stack <> null @>
+ // Verify Serverless configuration exists
+ test <@ appliesTo.Serverless <> null @>
+ | _ -> ()
| _ -> ()
From 5a4c5608eca5d463cda8589daafa0d1d7235dbbf Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 10 Jan 2026 00:14:02 +0000
Subject: [PATCH 9/9] Add public API to test LLM export metadata and test
applies_to rendering
Co-authored-by: reakaleek <16325797+reakaleek@users.noreply.github.com>
---
.../Exporters/LlmMarkdownExporter.cs | 11 ++++-
.../Framework/LlmMarkdownAssertions.fs | 18 --------
.../LlmMarkdown/LlmMarkdownOutput.fs | 44 +++++++------------
3 files changed, 24 insertions(+), 49 deletions(-)
diff --git a/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs b/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs
index d5f2371e2..109b5f9e4 100644
--- a/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs
+++ b/src/Elastic.Markdown/Exporters/LlmMarkdownExporter.cs
@@ -77,7 +77,7 @@ public async ValueTask ExportAsync(MarkdownExportFileContext fileContext,
if (outputFile.Directory is { Exists: false })
outputFile.Directory.Create();
- var content = IsRootIndexFile(fileContext) ? LlmsTxtTemplate : CreateLlmContentWithMetadata(fileContext, llmMarkdown);
+ var content = IsRootIndexFile(fileContext) ? LlmsTxtTemplate : CreateLlmContentWithMetadataInternal(fileContext, llmMarkdown);
await fileContext.SourceFile.SourceFile.FileSystem.File.WriteAllTextAsync(
outputFile.FullName,
@@ -94,6 +94,13 @@ public static string ConvertToLlmMarkdown(MarkdownDocument document, IDocumentat
_ = renderer.Render(obj);
});
+ ///
+ /// Creates the full LLM content with metadata section (frontmatter).
+ /// This is exposed for testing purposes.
+ ///
+ public static string CreateLlmContentWithMetadata(MarkdownExportFileContext context, string llmMarkdown) =>
+ new LlmMarkdownExporter().CreateLlmContentWithMetadataInternal(context, llmMarkdown);
+
private static bool IsRootIndexFile(MarkdownExportFileContext fileContext)
{
var fs = fileContext.BuildContext.ReadFileSystem;
@@ -129,7 +136,7 @@ private static IFileInfo GetLlmOutputFile(MarkdownExportFileContext fileContext)
}
- private string CreateLlmContentWithMetadata(MarkdownExportFileContext context, string llmMarkdown)
+ private string CreateLlmContentWithMetadataInternal(MarkdownExportFileContext context, string llmMarkdown)
{
var sourceFile = context.SourceFile;
var metadata = DocumentationObjectPoolProvider.StringBuilderPool.Get();
diff --git a/tests/authoring/Framework/LlmMarkdownAssertions.fs b/tests/authoring/Framework/LlmMarkdownAssertions.fs
index ffbd9ce6a..91c6404ca 100644
--- a/tests/authoring/Framework/LlmMarkdownAssertions.fs
+++ b/tests/authoring/Framework/LlmMarkdownAssertions.fs
@@ -37,23 +37,5 @@ module LlmMarkdownAssertions =
-- ACTUAL --
{actualLLM}
-"""
- raise (XunitException(msg))
-
- []
- let llmOutputContains (expectedText: string) (actual: Lazy) =
- // Check if the document content contains the expected text
- let results = actual.Value
- let defaultFile = results.MarkdownResults |> Seq.find (fun r -> r.File.RelativePath = "index.md")
- let actualLLM = toLlmMarkdown defaultFile
-
- if not (actualLLM.Contains(expectedText)) then
- let msg = $"""LLM output does not contain expected text
-
--- EXPECTED TO CONTAIN --
-{expectedText}
-
--- ACTUAL OUTPUT --
-{actualLLM}
"""
raise (XunitException(msg))
diff --git a/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs b/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs
index 602782750..b99b3d2ba 100644
--- a/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs
+++ b/tests/authoring/LlmMarkdown/LlmMarkdownOutput.fs
@@ -765,41 +765,27 @@ This is a test page with applies_to frontmatter.
"""
[]
- let ``renders content correctly`` () =
- // The page content should be rendered in LLM markdown
- markdown |> convertsToNewLLM """
-This is a test page with applies_to frontmatter.
-"""
-
- []
- let ``frontmatter applies_to is exported in metadata section`` () =
- // The applies_to from frontmatter is processed and would appear in the exported LLM file
- // In the format:
- // ---
- // title: Test Page
- // description: ...
- //
- // This applies to:
- // - Generally available since 8.5 for Stack
- // - Preview for Serverless
- // ---
- //
- // This happens in LlmMarkdownExporter.CreateLlmContentWithMetadata during export.
- // We verify the frontmatter is parsed correctly so it can be used during export.
+ let ``exports with applies_to in metadata`` () =
+ // Test that the applies_to helper renders the expected output
let results = markdown.Value
let defaultFile = results.MarkdownResults |> Seq.find (fun r -> r.File.RelativePath = "index.md")
- // Verify frontmatter was parsed
+ // Get the AppliesTo object from frontmatter
test <@ defaultFile.File.YamlFrontMatter <> null @>
match defaultFile.File.YamlFrontMatter with
| NonNull yamlFrontMatter ->
- // Verify applies_to was parsed and contains expected data
test <@ yamlFrontMatter.AppliesTo <> null @>
match yamlFrontMatter.AppliesTo with
| NonNull appliesTo ->
- // Verify Stack configuration exists
- test <@ appliesTo.Stack <> null @>
- // Verify Serverless configuration exists
- test <@ appliesTo.Serverless <> null @>
- | _ -> ()
- | _ -> ()
+ // Test that the LlmAppliesToHelper renders the correct output
+ let appliesToText = Elastic.Markdown.Myst.Renderers.LlmMarkdown.LlmAppliesToHelper.RenderAppliesToBlock(
+ appliesTo,
+ defaultFile.Context.Generator.Context
+ )
+
+ // Verify it contains the expected sections
+ test <@ appliesToText.Contains("This applies to:") @>
+ test <@ appliesToText.Contains("for Stack") @>
+ test <@ appliesToText.Contains("for Serverless") @>
+ | _ -> failwith "AppliesTo should not be null"
+ | _ -> failwith "YamlFrontMatter should not be null"