Class: Seo::LlmsTxtBuilder

Inherits:
BaseService show all
Defined in:
app/services/seo/llms_txt_builder.rb

Overview

Renders the llmstxt.org-style discovery manifest from the SiteMap
canonical source of truth — same table that backs the XML sitemaps and
schema.org rendering. No second hand-edited file to drift.

Per the llmstxt.org spec, llms.txt is a curated decision-helper for
AI agents, not a comprehensive URL index. Crawlers that want every URL
should follow sitemap.xml. This builder therefore surfaces:

  • The product-line hub for each catalog area (with an inline list of
    subpage slugs, so agents know what's drillable without crawling).
  • Standalone tools (calculators, design tool, quote builder).
  • The top reference articles by seo_traffic — pages an agent should
    consult for buying-decision questions.
  • A pointer to the full XML sitemap.

Output is bilingual: every entry shows both the en-US and en-CA URL.

Examples:

Seo::LlmsTxtBuilder.new.process

Constant Summary collapse

TOP_POSTS =

Top posts.

10
TOP_TECH_ARTICLES =

Top tech articles.

5
PRODUCT_LINE_SLUGS =

Curated product-line hubs surfaced to agents, in display order. This
is an explicit allow-list rather than a skip-list — partner-facing
(/trade, /retailer), promotional (/sales, /referral-program), and
transactional (/payment, /tell-us-about-your-project) hubs add noise
for agents trying to answer end-customer questions and don't belong
in a discovery manifest.

%w[
  floor-heating
  snow-melting
  towel-warmer
  infrared-heating-panels
  roof-and-gutter-deicing
  pipe-freeze-protection
  mirror-defogger
  led-mirror
  countertop-heater
].freeze
SUPPLEMENTARY_SLUGS =

Non-product entry points worth surfacing — educational and
cross-product content. Display order matters here too.

%w[
  radiant-heating
  design-guides
  products
  case-studies2
].freeze
SECTION_OVERRIDES =

Cosmetic touch-ups where humanizing the slug looks awkward. Keep
this list small — every entry is a hand-edited deviation.

{
  'led-mirror'              => 'LED Mirrors',
  'mirror-defogger'         => 'Mirror Defoggers',
  'towel-warmer'            => 'Towel Warmers',
  'infrared-heating-panels'     => 'Infrared Heating Panels',
  'roof-and-gutter-deicing' => 'Roof and Gutter De-icing',
  'pipe-freeze-protection'  => 'Pipe-Freeze Protection',
  'countertop-heater'       => 'Countertop Heaters'
}.freeze
TOOL_PATHS =

Standalone tool paths — calculators and configurators that agents
should be able to deep-link into without traversing the hub.

%w[
  /floor-heating/cost-calculator
  /floor-heating/quote-builder
  /snow-melting/cost-calculator
  /snow-melting/quote-builder
  /tools/online-design-tool
].freeze

Instance Attribute Summary

Attributes inherited from BaseService

#options

Instance Method Summary collapse

Methods inherited from BaseService

#initialize, #log_debug, #log_error, #log_info, #log_warning, #logger, #tagged_logger

Constructor Details

This class inherits a constructor from BaseService

Instance Method Details

#last_modifiedTime?

Latest last_mod across input rows, for HTTP cache freshness.

Returns:

  • (Time, nil)


94
95
96
97
98
99
100
101
# File 'app/services/seo/llms_txt_builder.rb', line 94

def last_modified
  [
    static_page_scope(:'en-US').maximum(:last_mod),
    static_page_scope(:'en-CA').maximum(:last_mod),
    post_scope(:'en-US').maximum(:last_mod),
    tech_article_scope(:'en-US').maximum(:last_mod)
  ].compact.max
end

#processObject



77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'app/services/seo/llms_txt_builder.rb', line 77

def process
  sections = [
    header,
    product_hubs_section,
    supplementary_section,
    tools_section,
    top_posts_section,
    top_tech_articles_section,
    sitemap_pointer_section,
    contact_section
  ].compact_blank

  "#{sections.join("\n\n")}\n"
end