Skip to content

Treat childless file: subdir/index.md as a single-page folder#3471

Open
cotti wants to merge 2 commits into
mainfrom
fix/764-deep-linked-index-files
Open

Treat childless file: subdir/index.md as a single-page folder#3471
cotti wants to merge 2 commits into
mainfrom
fix/764-deep-linked-index-files

Conversation

@cotti

@cotti cotti commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Why

Closes #764. Authors expect to add single-page subdirectories to the TOC with the natural full path:

toc:
  - file: reference/1password/index.md
  - file: reference/activemq/index.md

Instead, every such childless file: subdir/index.md entry was silently dropped from the navigation, with no error. The only workaround was the unintuitive folder: + file: index.md form (or renaming files to reference/1password.md).

What

A childless file: entry pointing at a subdirectory index.md previously resolved to a bare leaf that competed with its siblings for the parent's index slot — losing that race meant it never appeared in the tree (and in nested cases its path got doubled).

The YAML converter now treats such an entry as sugar for folder: subdir with a single file: index.md child, so it becomes its own single-page subsection and the full-path form works as documented. Entries that declare explicit children are untouched and keep their existing virtual-file (deep-linking) semantics, so the change is backward compatible.

Covered by new regression tests in DeepLinkedIndexFileTests.cs (conversion, backward-compat for bare index.md and entries with children, rendering as a plain link, multiple siblings under a parent folder, and no path-doubling under a virtual-file parent). Navigation behavior is documented under "Deep-linked index.md files" in the navigation config docs.

Made with Cursor

@cotti cotti requested a review from a team as a code owner June 5, 2026 00:51
@cotti cotti added the bug label Jun 5, 2026
@cotti cotti requested a review from a team as a code owner June 5, 2026 00:51
@cotti cotti requested a review from Mpdreamz June 5, 2026 00:51
@cotti cotti temporarily deployed to integration-tests June 5, 2026 00:51 — with GitHub Actions Inactive
@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@cotti, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 19 minutes and 23 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 7b2087c7-4eed-4fe6-a406-985a438b602f

📥 Commits

Reviewing files that changed from the base of the PR and between 778baaa and a657395.

📒 Files selected for processing (3)
  • docs/configure/content-set/navigation.md
  • src/Elastic.Documentation.Configuration/Toc/TableOfContentsYamlConverters.cs
  • tests/Navigation.Tests/Isolation/DeepLinkedIndexFileTests.cs
📝 Walkthrough

Walkthrough

The PR adds documentation for deep-linked index.md TOC entries and updates TocItemYamlConverter.ReadYaml so childless .../index.md entries become FolderRef values with a generated FolderIndexFileRef. Root index.md stays an IndexFileRef, and entries with explicit children keep the existing virtual grouping behavior. New tests cover conversion, navigation rendering, and URL resolution.

Sequence Diagram(s)

sequenceDiagram
  participant TocEntry as docset.yml TOC entry
  participant Converter as TocItemYamlConverter.ReadYaml
  participant Navigation as navigation rendering
  participant Collector as error collector

  TocEntry->>Converter: file: reference/.../index.md
  alt childless deep-linked index.md
    Converter-->>Navigation: FolderRef with FolderIndexFileRef
  else bare root index.md
    Converter-->>Navigation: IndexFileRef
  else deep-linked entry with children
    Converter-->>Navigation: FileRef with children
  end
  Navigation-->>Collector: zero errors
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: treating childless subdirectory index.md entries as single-page folders.
Description check ✅ Passed The description matches the implemented fix, its backward-compatibility behavior, and the added documentation and tests.
Linked Issues check ✅ Passed The change satisfies #764 by allowing full-path subdirectory index.md entries to appear in the TOC as single-page folders.
Out of Scope Changes check ✅ Passed The docs and regression tests are directly related to the fix and no unrelated changes are evident.
✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch fix/764-deep-linked-index-files

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Mpdreamz Mpdreamz left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the fix — the implementation is correct and the test coverage is solid. A few things to address before merging:

Comment verbosity

The four-line comment block in TableOfContentsYamlConverters.cs re-narrates the bug history. That belongs in the commit/PR description, not the source. Trim to one line:

// Sugar: "file: subdir/index.md" without children → single-page folder to avoid silent nav drop.
if (children.Count == 0 && fileOnly.EndsWith("/index.md", StringComparison.Ordinal))

Docs framing

The PR description calls folder: + file: index.md "the unintuitive workaround" but that has always been the preferred canonical form. The docs section should reflect this — e.g.:

"This is shorthand for the explicit folder: + file: form, which remains the preferred approach for folders that also have additional children."

Missing docs: behavior under folder: + children:

The test FolderWithChildlessIndexFileChildrenKeepsEveryEntry shows the sugar works inside a parent folder's children: list too, but the docs don't mention it. This is the primary real-world use case (e.g. 100+ single-page integrations under a reference/ folder):

toc:
  - folder: reference
    file: index.md
    children:
      - file: 1password/index.md       # → becomes its own FolderRef
      - file: activemq/index.md        # → becomes its own FolderRef

Each entry becomes its own subsection; none "steals" the parent's index. Worth a short mention in the docs since it's the main usage pattern.

toc: version scope

The version: key goes through a separate converter branch. Please confirm whether file: subdir/index.md under a versioned toc entry is also covered (or explicitly out of scope). If it still silently drops there, that's worth a follow-up issue at minimum.

Minor: test class docstring

The class-level <summary> block in DeepLinkedIndexFileTests.cs is multi-paragraph. Project convention is no multi-paragraph docstrings — either drop it or fold to one line.

@cotti cotti force-pushed the fix/764-deep-linked-index-files branch from 83724b0 to 778baaa Compare June 26, 2026 15:05
@cotti cotti temporarily deployed to integration-tests June 26, 2026 15:05 — with GitHub Actions Inactive
@cotti

cotti commented Jun 26, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review @Mpdreamz — addressed in 778baaa (also rebased onto latest main):

  • Comment verbosity — trimmed the block to a single line on the if.
  • Docs framing — reworded so the folder: + file: form is presented as the preferred canonical approach, and the deep-linked form as shorthand for it.
  • Missing docs (folder: + children:) — added a dedicated example showing the shorthand inside a parent folder's children: list (the dozens-of-integrations case), noting each child becomes its own subsection and none is consumed as the parent's index.
  • Test class docstring — folded to one line.

On toc: version scope: I went looking for a separate versioned-toc converter branch and couldn't find one in this repo. All file: entries — top-level in docset.yml, nested under folder:/children:, and inside isolated sub-toc.yml files (IsolatedTableOfContentsRef) — are deserialized by the single TocItemYamlConverter.ReadYaml, so the sugar applies uniformly everywhere a file: entry is parsed. The only other TOC converter is SiteTableOfContentsRefYamlConverter for the assembler navigation.yml, but that branch only accepts whole-content-set toc:/path_prefix: references and never parses individual file: entries, so it's unaffected. Net: deep-linked index.md isn't silently dropped under any of these paths, so no follow-up issue seems warranted. If you were referring to a specific versioned-toc construct I'm not seeing (e.g. the changelog version:target: normalization, which is unrelated to navigation), point me at it and I'll cover it.

cotti and others added 2 commits June 26, 2026 12:45
A childless `file: subdir/index.md` entry resolved to a bare leaf that
competed with its siblings for the parent's index slot and was silently
dropped from the navigation, forcing authors to use a `folder:` workaround.
Treat it as sugar for a folder with an index file so the full path works as
expected. Entries that declare children keep their virtual-file semantics.

Fixes #764

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Condense the converter comment to one line, reframe the navigation docs to
present folder: + file: as the preferred canonical form, document the
shorthand under a parent folder's children: list (the main use case), and
fold the test class docstring to a single line per project convention.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Directories with a single index.md file require special docset.yml pattern

2 participants