docs(nav): stop listing "index" in meta.json pages — it detaches the folder index from the tree - #13710

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-12352-meta-index-breadcrumb
Aug 31, 2026
Merged

docs(nav): stop listing "index" in meta.json pages — it detaches the folder index from the tree#13710
os-project-manager merged 1 commit into
mainfrom
claude/issue-12352-meta-index-breadcrumb

Conversation

@os-project-manager

@os-project-manageros-project-manager commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

Part of #12352

Removes "index" from the pages array of 16 of the 17 meta.json files under
content/docs that list it. Producer-side only: no consumer-side URL
reconstruction, in page.tsx or anywhere else.

Mechanism (fumadocs-core 16.14.4, buildFolder() in dist/loader-*.js)

A folder's index.mdx becomes that folder's tree index node only while the
folder's meta.json does not list "index" in pages. Listing it makes the
page an ordinary child and then deletes the folder's index:

if(indexPath){if(excludedPaths.has(indexPath))deletenode.index;// "index" was listedelseexcludedPaths.add(indexPath);}

Two surfaces read that one node:

surfaceconsumerwith "index" listed
breadcrumbgetBreadcrumbItems() links a folder crumb to item.index?.urlancestor has a name and no URL, so docsTrail() drops it
sidebarnode.index ? SidebarFolderLink : SidebarFolderTriggersection header is inert text; the overview page sits below it as a child

Re-derived on current origin/main (not inherited from the card)

Measured by driving the real pinned fumadocs-core loader over content/docs
and applying both consumers to the resulting tree.

  • 17 of 35meta.json files list "index" — unchanged from the card.
  • 172 short trails, and the per-top-folder breakdown matches the card's
    production-build numbers row for row (ai 8 · api 11 · automation 9 ·
    capabilities 10 · concepts 5 · data-modeling 17 · deployment 11 ·
    getting-started 8 · kernel 22 · permissions 20 · plugins 4 · protocol 24 ·
    releases 8 · ui 15).
  • Page count reads 404 where the card read 403 sitemap URLs; the extra one is
    /docs itself, whose trail was already complete. 404 = 232 + 172 against the
    card's 403 = 231 + 172, so the two harnesses agree exactly.

After: short trails 172 to 8. The remaining 8 are every page under
/docs/releases.

Per-file decisions — checked individually, not blanket-edited

"index" was the firstpages entry in 15 files and the first entry after
the ---Start Here--- separator in getting-started. No file placed it between
other entries, so no entry's position depends on it. Each edit asserted, per
file, that pages had exactly one "index", that the resulting array equals the
original minus that one element, and that no other key changed.

content/docs/releases/meta.json is not touched: content/docs/releases/ is
fenced by AGENTS.md and by this card's dispatch. It still lists "index", which
is why this is Part of and not a closing reference — #12352 stays open for that
one file, and whether it should be swept in a dedicated docs-only PR is the
maintainer's call.

Sidebar — verified rendered, before and after

The ruling requires the sidebar, not only the JSON-LD. Captured from a running
dev server, with content/docs reverted to this branch's parent commit for the
"before" pass and restored from HEAD afterwards (git diff HEAD empty, blob
hashes back to the HEAD blobs).

/docs/getting-started/glossary, sidebar entries in document order:

-FOLDER-TRIGGER | Get Started | None
+FOLDER-LINK | Get Started | /docs/getting-started
separator | Start Here | None
-link | What is ObjectStack? | /docs/getting-started
link | How AI Development Works | /docs/getting-started/how-ai-development-works

/docs/data-modeling/objects:

-FOLDER-TRIGGER | Data Modeling | None
-link | Data Modeling | /docs/data-modeling
+FOLDER-LINK | Data Modeling | /docs/data-modeling
link | Schema Design | /docs/data-modeling/schema-design

FOLDER-LINK | Reference | /docs/references renders identically in both passes —
a folder that never listed "index", i.e. the control.

Over the whole tree the delta is exactly 16 headers TRIGGER to LINK plus 16
index children leaving the child list, and nothing else
: every removed child's
URL is now its folder header's href (set equality, checked mechanically), and
no other line moved in either direction. SidebarFolderLink still renders the
collapse chevron, so no section loses collapsibility.

Six of the 16 removed a child whose label was identical to the section header
(Automation, What Can It Do?, Core Concepts, Data Modeling,
Kernel & Services, Permissions & Identity) — a duplicate row, now gone. The
other ten swap the page title for the folder title on the same link:

linksidebar label beforeafter
/docs/getting-startedWhat is ObjectStack?Get Started
/docs/uiUI EngineViews & Apps
/docs/pluginsPlugin SystemPlugins & Packages
/docs/protocol/kernelKernel: The System ProtocolSystem Protocol
/docs/protocolProtocol SpecificationProtocol Spec
/docs/deploymentDeployment OverviewDeployment & Operations
/docs/kernel/runtime-servicesRuntime Service APIsRuntime Services
/docs/kernel/contractsService Contracts OverviewService Contracts
/docs/aiAI OverviewAI
/docs/apiAPI OverviewAPI & SDK

No folder is judged worse: the page stays in the sidebar at the same tree
position, one click away, and this is already how the 18 folders that never
listed "index" render (references, protocol/objectql, protocol/objectui,
…). /docs/getting-started is the widest label gap and is flagged for review
rather than decided here.

Breadcrumb — rendered BreadcrumbList, after

/docs/data-modeling/objects ObjectStack > Documentation > Data Modeling(/docs/data-modeling) > Object Metadata
/docs/getting-started/glossary ObjectStack > Documentation > Get Started(/docs/getting-started) > Glossary
/docs/protocol/objectql/query-syntax ObjectStack > Documentation > Protocol Spec(/docs/protocol) > Data Protocol(/docs/protocol/objectql) > Query Syntax
/docs/releases/v17 ObjectStack > Documentation > v17.0.0 (still short — fenced folder)

The first is the card's own example, which shipped three crumbs with the section
missing. The third is the card's control: it was already linking
/docs/protocol/objectql and now also links /docs/protocol, because
content/docs/protocol/meta.json listed "index" too.

One comment-only edit outside content/

apps/docs/app/[lang]/docs/[[...slug]]/page.tsx carries a doc comment that
states this defect as live and quantifies it ("17 of the 35 …", "172 of 403 …").
Landing this would make that text false, so the paragraph is rewritten to the
post-fix state and to name the one folder still affected. No executable line
changes
— every changed line in that file starts with *, checked
mechanically. The ⛔ fence against reconstructing the URL consumer-side is kept
verbatim and is untouched.

Verification

At 1a5ba4fa4, the branch head:

  • The 43 gate families node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
    derives for this change set: 42 green, 1 NOT MEASURED. The exception is
    scripts/check-test-completeness.mjs, which grades a saved turbo run test
    log and exits 3 with PREREQUISITE NOT MET when the family names it with no
    argument — its own header says to record it as NOT MEASURED locally. Three
    gates first returned PREREQUISITE NOT MET for unbuilt workspace packages
    (@objectstack/formula, @objectstack/lint, packages/spec/json-schema) and
    one for a missing @objectstack/client-react build; all four were rebuilt and
    re-run green, and none of those first readings is reported as a result.
  • pnpm lint (eslint . --no-inline-config, whole repo, no narrowing) — clean.
  • pnpm --filter @objectstack/docs run typecheck
    (fumadocs-mdx && next typegen && tsc --noEmit) — clean.
  • node scripts/check-section-landing-index.mjs green, and by construction: that
    gate already filters 'index' out of the pages array it reads, so the
    landing-page index blocks are unaffected.

Every exit code was captured before any pipe.

Not in scope

The inbound-links work from the sibling card of the same sweep is untouched here.


Generated by Claude Code


Generated by Claude Code

…folder index (#12352)
Fumadocs attaches a folder's index.mdx as that folder's tree `index` node only
when the folder's meta.json does NOT list "index" in `pages`. Listing it makes
the page an ordinary child and leaves the folder node with a name and no url,
which degrades both surfaces built from that node: `getBreadcrumbItems()` drops
the un-linkable ancestor, and the sidebar renders an inert `SidebarFolderTrigger`
instead of a `SidebarFolderLink`.
Removes "index" from 16 of the 17 meta.json files that listed it.
content/docs/releases/ is fenced by AGENTS.md and is left untouched.
Measured on the real fumadocs-core 16.14.4 loader over content/docs:
short breadcrumb trails 172 -> 8 (the 8 are all under /docs/releases), and the
sidebar delta is exactly 16 folder headers TRIGGER -> LINK plus 16 index
children leaving the child list, with no other entry moved.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
@github-actionsgithub-actionsBot added size/s documentation Improvements or additions to documentation tooling labels Aug 31, 2026
@os-project-manager
os-project-manager marked this pull request as ready for review August 31, 2026 08:33
@os-project-manager
os-project-manager added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit 787d757Aug 31, 2026
37 of 38 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-12352-meta-index-breadcrumb branch August 31, 2026 08:51
os-project-manager added a commit that referenced this pull request Aug 31, 2026
…13711)
Fumadocs attaches a folder's index.mdx as that folder's tree `index` node only
when the folder's meta.json does NOT list "index" in `pages`. Listing it makes
the landing page an ordinary child and leaves the folder node with a name and
no url. In fumadocs-core 16.14.4 `buildFolder()`:
if (indexPath) {
if (excludedPaths.has(indexPath)) delete node.index; // "index" was listed
else excludedPaths.add(indexPath);
}
`getBreadcrumbItems()` still emits that ancestor, but with `url: undefined`, and
`docsTrail()` in apps/docs drops any crumb without a url (Google requires `item`
on every BreadcrumbList entry but the last). The sidebar reads the same node as
`node.index ? SidebarFolderLink : SidebarFolderTrigger`.
Measured on the real loader over content/docs (405 pages), removing this one
line and changing nothing else:
- short breadcrumb trails 8 -> 0; the 8 fixed are exactly the non-landing
pages under /docs/releases (v9, v12..v17, implementation-status)
- the Releases folder header goes TRIGGER -> LINK, indexUrl /docs/releases
- /docs/releases stays in getPages() and source.getPage(['releases']) still
resolves -- the landing page is not unlisted, it becomes the section link
- 405 pages before and after; exactly one sidebar folder header flips and
only the 9 /docs/releases trails change
This was the 17th and last meta.json still listing it; the other 16 were fixed
in #13710, which left this one alone because content/docs/releases/ is fenced
by AGENTS.md. This is the dedicated docs-only PR that fence names as the route.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 1, 2026
…bjectstack-ai#13711) (objectstack-ai#13946)
Fumadocs attaches a folder's index.mdx as that folder's tree `index` node only
when the folder's meta.json does NOT list "index" in `pages`. Listing it makes
the landing page an ordinary child and leaves the folder node with a name and
no url. In fumadocs-core 16.14.4 `buildFolder()`:
if (indexPath) {
if (excludedPaths.has(indexPath)) delete node.index; // "index" was listed
else excludedPaths.add(indexPath);
}
`getBreadcrumbItems()` still emits that ancestor, but with `url: undefined`, and
`docsTrail()` in apps/docs drops any crumb without a url (Google requires `item`
on every BreadcrumbList entry but the last). The sidebar reads the same node as
`node.index ? SidebarFolderLink : SidebarFolderTrigger`.
Measured on the real loader over content/docs (405 pages), removing this one
line and changing nothing else:
- short breadcrumb trails 8 -> 0; the 8 fixed are exactly the non-landing
pages under /docs/releases (v9, v12..v17, implementation-status)
- the Releases folder header goes TRIGGER -> LINK, indexUrl /docs/releases
- /docs/releases stays in getPages() and source.getPage(['releases']) still
resolves -- the landing page is not unlisted, it becomes the section link
- 405 pages before and after; exactly one sidebar folder header flips and
only the 9 /docs/releases trails change
This was the 17th and last meta.json still listing it; the other 16 were fixed
in objectstack-ai#13710, which left this one alone because content/docs/releases/ is fenced
by AGENTS.md. This is the dedicated docs-only PR that fence names as the route.
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/stooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

docs(nav): stop listing "index" in meta.json pages — it detaches the folder index from the tree - #13710

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-12352-meta-index-breadcrumb
Aug 31, 2026
Merged

docs(nav): stop listing "index" in meta.json pages — it detaches the folder index from the tree#13710
os-project-manager merged 1 commit into
mainfrom
claude/issue-12352-meta-index-breadcrumb

Conversation

@os-project-manager

@os-project-manageros-project-manager commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

Part of #12352

Removes "index" from the pages array of 16 of the 17 meta.json files under
content/docs that list it. Producer-side only: no consumer-side URL
reconstruction, in page.tsx or anywhere else.

Mechanism (fumadocs-core 16.14.4, buildFolder() in dist/loader-*.js)

A folder's index.mdx becomes that folder's tree index node only while the
folder's meta.json does not list "index" in pages. Listing it makes the
page an ordinary child and then deletes the folder's index:

if(indexPath){if(excludedPaths.has(indexPath))deletenode.index;// "index" was listedelseexcludedPaths.add(indexPath);}

Two surfaces read that one node:

surfaceconsumerwith "index" listed
breadcrumbgetBreadcrumbItems() links a folder crumb to item.index?.urlancestor has a name and no URL, so docsTrail() drops it
sidebarnode.index ? SidebarFolderLink : SidebarFolderTriggersection header is inert text; the overview page sits below it as a child

Re-derived on current origin/main (not inherited from the card)

Measured by driving the real pinned fumadocs-core loader over content/docs
and applying both consumers to the resulting tree.

  • 17 of 35meta.json files list "index" — unchanged from the card.
  • 172 short trails, and the per-top-folder breakdown matches the card's
    production-build numbers row for row (ai 8 · api 11 · automation 9 ·
    capabilities 10 · concepts 5 · data-modeling 17 · deployment 11 ·
    getting-started 8 · kernel 22 · permissions 20 · plugins 4 · protocol 24 ·
    releases 8 · ui 15).
  • Page count reads 404 where the card read 403 sitemap URLs; the extra one is
    /docs itself, whose trail was already complete. 404 = 232 + 172 against the
    card's 403 = 231 + 172, so the two harnesses agree exactly.

After: short trails 172 to 8. The remaining 8 are every page under
/docs/releases.

Per-file decisions — checked individually, not blanket-edited

"index" was the firstpages entry in 15 files and the first entry after
the ---Start Here--- separator in getting-started. No file placed it between
other entries, so no entry's position depends on it. Each edit asserted, per
file, that pages had exactly one "index", that the resulting array equals the
original minus that one element, and that no other key changed.

content/docs/releases/meta.json is not touched: content/docs/releases/ is
fenced by AGENTS.md and by this card's dispatch. It still lists "index", which
is why this is Part of and not a closing reference — #12352 stays open for that
one file, and whether it should be swept in a dedicated docs-only PR is the
maintainer's call.

Sidebar — verified rendered, before and after

The ruling requires the sidebar, not only the JSON-LD. Captured from a running
dev server, with content/docs reverted to this branch's parent commit for the
"before" pass and restored from HEAD afterwards (git diff HEAD empty, blob
hashes back to the HEAD blobs).

/docs/getting-started/glossary, sidebar entries in document order:

-FOLDER-TRIGGER | Get Started | None
+FOLDER-LINK | Get Started | /docs/getting-started
separator | Start Here | None
-link | What is ObjectStack? | /docs/getting-started
link | How AI Development Works | /docs/getting-started/how-ai-development-works

/docs/data-modeling/objects:

-FOLDER-TRIGGER | Data Modeling | None
-link | Data Modeling | /docs/data-modeling
+FOLDER-LINK | Data Modeling | /docs/data-modeling
link | Schema Design | /docs/data-modeling/schema-design

FOLDER-LINK | Reference | /docs/references renders identically in both passes —
a folder that never listed "index", i.e. the control.

Over the whole tree the delta is exactly 16 headers TRIGGER to LINK plus 16
index children leaving the child list, and nothing else
: every removed child's
URL is now its folder header's href (set equality, checked mechanically), and
no other line moved in either direction. SidebarFolderLink still renders the
collapse chevron, so no section loses collapsibility.

Six of the 16 removed a child whose label was identical to the section header
(Automation, What Can It Do?, Core Concepts, Data Modeling,
Kernel & Services, Permissions & Identity) — a duplicate row, now gone. The
other ten swap the page title for the folder title on the same link:

linksidebar label beforeafter
/docs/getting-startedWhat is ObjectStack?Get Started
/docs/uiUI EngineViews & Apps
/docs/pluginsPlugin SystemPlugins & Packages
/docs/protocol/kernelKernel: The System ProtocolSystem Protocol
/docs/protocolProtocol SpecificationProtocol Spec
/docs/deploymentDeployment OverviewDeployment & Operations
/docs/kernel/runtime-servicesRuntime Service APIsRuntime Services
/docs/kernel/contractsService Contracts OverviewService Contracts
/docs/aiAI OverviewAI
/docs/apiAPI OverviewAPI & SDK

No folder is judged worse: the page stays in the sidebar at the same tree
position, one click away, and this is already how the 18 folders that never
listed "index" render (references, protocol/objectql, protocol/objectui,
…). /docs/getting-started is the widest label gap and is flagged for review
rather than decided here.

Breadcrumb — rendered BreadcrumbList, after

/docs/data-modeling/objects ObjectStack > Documentation > Data Modeling(/docs/data-modeling) > Object Metadata
/docs/getting-started/glossary ObjectStack > Documentation > Get Started(/docs/getting-started) > Glossary
/docs/protocol/objectql/query-syntax ObjectStack > Documentation > Protocol Spec(/docs/protocol) > Data Protocol(/docs/protocol/objectql) > Query Syntax
/docs/releases/v17 ObjectStack > Documentation > v17.0.0 (still short — fenced folder)

The first is the card's own example, which shipped three crumbs with the section
missing. The third is the card's control: it was already linking
/docs/protocol/objectql and now also links /docs/protocol, because
content/docs/protocol/meta.json listed "index" too.

One comment-only edit outside content/

apps/docs/app/[lang]/docs/[[...slug]]/page.tsx carries a doc comment that
states this defect as live and quantifies it ("17 of the 35 …", "172 of 403 …").
Landing this would make that text false, so the paragraph is rewritten to the
post-fix state and to name the one folder still affected. No executable line
changes
— every changed line in that file starts with *, checked
mechanically. The ⛔ fence against reconstructing the URL consumer-side is kept
verbatim and is untouched.

Verification

At 1a5ba4fa4, the branch head:

  • The 43 gate families node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
    derives for this change set: 42 green, 1 NOT MEASURED. The exception is
    scripts/check-test-completeness.mjs, which grades a saved turbo run test
    log and exits 3 with PREREQUISITE NOT MET when the family names it with no
    argument — its own header says to record it as NOT MEASURED locally. Three
    gates first returned PREREQUISITE NOT MET for unbuilt workspace packages
    (@objectstack/formula, @objectstack/lint, packages/spec/json-schema) and
    one for a missing @objectstack/client-react build; all four were rebuilt and
    re-run green, and none of those first readings is reported as a result.
  • pnpm lint (eslint . --no-inline-config, whole repo, no narrowing) — clean.
  • pnpm --filter @objectstack/docs run typecheck
    (fumadocs-mdx && next typegen && tsc --noEmit) — clean.
  • node scripts/check-section-landing-index.mjs green, and by construction: that
    gate already filters 'index' out of the pages array it reads, so the
    landing-page index blocks are unaffected.

Every exit code was captured before any pipe.

Not in scope

The inbound-links work from the sibling card of the same sweep is untouched here.


Generated by Claude Code


Generated by Claude Code

…folder index (#12352)
Fumadocs attaches a folder's index.mdx as that folder's tree `index` node only
when the folder's meta.json does NOT list "index" in `pages`. Listing it makes
the page an ordinary child and leaves the folder node with a name and no url,
which degrades both surfaces built from that node: `getBreadcrumbItems()` drops
the un-linkable ancestor, and the sidebar renders an inert `SidebarFolderTrigger`
instead of a `SidebarFolderLink`.
Removes "index" from 16 of the 17 meta.json files that listed it.
content/docs/releases/ is fenced by AGENTS.md and is left untouched.
Measured on the real fumadocs-core 16.14.4 loader over content/docs:
short breadcrumb trails 172 -> 8 (the 8 are all under /docs/releases), and the
sidebar delta is exactly 16 folder headers TRIGGER -> LINK plus 16 index
children leaving the child list, with no other entry moved.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
@github-actionsgithub-actionsBot added size/s documentation Improvements or additions to documentation tooling labels Aug 31, 2026
@os-project-manager
os-project-manager marked this pull request as ready for review August 31, 2026 08:33
@os-project-manager
os-project-manager added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit 787d757Aug 31, 2026
37 of 38 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-12352-meta-index-breadcrumb branch August 31, 2026 08:51
os-project-manager added a commit that referenced this pull request Aug 31, 2026
…13711)
Fumadocs attaches a folder's index.mdx as that folder's tree `index` node only
when the folder's meta.json does NOT list "index" in `pages`. Listing it makes
the landing page an ordinary child and leaves the folder node with a name and
no url. In fumadocs-core 16.14.4 `buildFolder()`:
if (indexPath) {
if (excludedPaths.has(indexPath)) delete node.index; // "index" was listed
else excludedPaths.add(indexPath);
}
`getBreadcrumbItems()` still emits that ancestor, but with `url: undefined`, and
`docsTrail()` in apps/docs drops any crumb without a url (Google requires `item`
on every BreadcrumbList entry but the last). The sidebar reads the same node as
`node.index ? SidebarFolderLink : SidebarFolderTrigger`.
Measured on the real loader over content/docs (405 pages), removing this one
line and changing nothing else:
- short breadcrumb trails 8 -> 0; the 8 fixed are exactly the non-landing
pages under /docs/releases (v9, v12..v17, implementation-status)
- the Releases folder header goes TRIGGER -> LINK, indexUrl /docs/releases
- /docs/releases stays in getPages() and source.getPage(['releases']) still
resolves -- the landing page is not unlisted, it becomes the section link
- 405 pages before and after; exactly one sidebar folder header flips and
only the 9 /docs/releases trails change
This was the 17th and last meta.json still listing it; the other 16 were fixed
in #13710, which left this one alone because content/docs/releases/ is fenced
by AGENTS.md. This is the dedicated docs-only PR that fence names as the route.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 1, 2026
…bjectstack-ai#13711) (objectstack-ai#13946)
Fumadocs attaches a folder's index.mdx as that folder's tree `index` node only
when the folder's meta.json does NOT list "index" in `pages`. Listing it makes
the landing page an ordinary child and leaves the folder node with a name and
no url. In fumadocs-core 16.14.4 `buildFolder()`:
if (indexPath) {
if (excludedPaths.has(indexPath)) delete node.index; // "index" was listed
else excludedPaths.add(indexPath);
}
`getBreadcrumbItems()` still emits that ancestor, but with `url: undefined`, and
`docsTrail()` in apps/docs drops any crumb without a url (Google requires `item`
on every BreadcrumbList entry but the last). The sidebar reads the same node as
`node.index ? SidebarFolderLink : SidebarFolderTrigger`.
Measured on the real loader over content/docs (405 pages), removing this one
line and changing nothing else:
- short breadcrumb trails 8 -> 0; the 8 fixed are exactly the non-landing
pages under /docs/releases (v9, v12..v17, implementation-status)
- the Releases folder header goes TRIGGER -> LINK, indexUrl /docs/releases
- /docs/releases stays in getPages() and source.getPage(['releases']) still
resolves -- the landing page is not unlisted, it becomes the section link
- 405 pages before and after; exactly one sidebar folder header flips and
only the 9 /docs/releases trails change
This was the 17th and last meta.json still listing it; the other 16 were fixed
in objectstack-ai#13710, which left this one alone because content/docs/releases/ is fenced
by AGENTS.md. This is the dedicated docs-only PR that fence names as the route.
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/stooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

docs(nav): stop listing "index" in meta.json pages — it detaches the folder index from the tree - #13710

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-12352-meta-index-breadcrumb
Aug 31, 2026
Merged

docs(nav): stop listing "index" in meta.json pages — it detaches the folder index from the tree#13710
os-project-manager merged 1 commit into
mainfrom
claude/issue-12352-meta-index-breadcrumb

Conversation

@os-project-manager

@os-project-manageros-project-manager commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

Part of #12352

Removes "index" from the pages array of 16 of the 17 meta.json files under
content/docs that list it. Producer-side only: no consumer-side URL
reconstruction, in page.tsx or anywhere else.

Mechanism (fumadocs-core 16.14.4, buildFolder() in dist/loader-*.js)

A folder's index.mdx becomes that folder's tree index node only while the
folder's meta.json does not list "index" in pages. Listing it makes the
page an ordinary child and then deletes the folder's index:

if(indexPath){if(excludedPaths.has(indexPath))deletenode.index;// "index" was listedelseexcludedPaths.add(indexPath);}

Two surfaces read that one node:

surfaceconsumerwith "index" listed
breadcrumbgetBreadcrumbItems() links a folder crumb to item.index?.urlancestor has a name and no URL, so docsTrail() drops it
sidebarnode.index ? SidebarFolderLink : SidebarFolderTriggersection header is inert text; the overview page sits below it as a child

Re-derived on current origin/main (not inherited from the card)

Measured by driving the real pinned fumadocs-core loader over content/docs
and applying both consumers to the resulting tree.

  • 17 of 35meta.json files list "index" — unchanged from the card.
  • 172 short trails, and the per-top-folder breakdown matches the card's
    production-build numbers row for row (ai 8 · api 11 · automation 9 ·
    capabilities 10 · concepts 5 · data-modeling 17 · deployment 11 ·
    getting-started 8 · kernel 22 · permissions 20 · plugins 4 · protocol 24 ·
    releases 8 · ui 15).
  • Page count reads 404 where the card read 403 sitemap URLs; the extra one is
    /docs itself, whose trail was already complete. 404 = 232 + 172 against the
    card's 403 = 231 + 172, so the two harnesses agree exactly.

After: short trails 172 to 8. The remaining 8 are every page under
/docs/releases.

Per-file decisions — checked individually, not blanket-edited

"index" was the firstpages entry in 15 files and the first entry after
the ---Start Here--- separator in getting-started. No file placed it between
other entries, so no entry's position depends on it. Each edit asserted, per
file, that pages had exactly one "index", that the resulting array equals the
original minus that one element, and that no other key changed.

content/docs/releases/meta.json is not touched: content/docs/releases/ is
fenced by AGENTS.md and by this card's dispatch. It still lists "index", which
is why this is Part of and not a closing reference — #12352 stays open for that
one file, and whether it should be swept in a dedicated docs-only PR is the
maintainer's call.

Sidebar — verified rendered, before and after

The ruling requires the sidebar, not only the JSON-LD. Captured from a running
dev server, with content/docs reverted to this branch's parent commit for the
"before" pass and restored from HEAD afterwards (git diff HEAD empty, blob
hashes back to the HEAD blobs).

/docs/getting-started/glossary, sidebar entries in document order:

-FOLDER-TRIGGER | Get Started | None
+FOLDER-LINK | Get Started | /docs/getting-started
separator | Start Here | None
-link | What is ObjectStack? | /docs/getting-started
link | How AI Development Works | /docs/getting-started/how-ai-development-works

/docs/data-modeling/objects:

-FOLDER-TRIGGER | Data Modeling | None
-link | Data Modeling | /docs/data-modeling
+FOLDER-LINK | Data Modeling | /docs/data-modeling
link | Schema Design | /docs/data-modeling/schema-design

FOLDER-LINK | Reference | /docs/references renders identically in both passes —
a folder that never listed "index", i.e. the control.

Over the whole tree the delta is exactly 16 headers TRIGGER to LINK plus 16
index children leaving the child list, and nothing else
: every removed child's
URL is now its folder header's href (set equality, checked mechanically), and
no other line moved in either direction. SidebarFolderLink still renders the
collapse chevron, so no section loses collapsibility.

Six of the 16 removed a child whose label was identical to the section header
(Automation, What Can It Do?, Core Concepts, Data Modeling,
Kernel & Services, Permissions & Identity) — a duplicate row, now gone. The
other ten swap the page title for the folder title on the same link:

linksidebar label beforeafter
/docs/getting-startedWhat is ObjectStack?Get Started
/docs/uiUI EngineViews & Apps
/docs/pluginsPlugin SystemPlugins & Packages
/docs/protocol/kernelKernel: The System ProtocolSystem Protocol
/docs/protocolProtocol SpecificationProtocol Spec
/docs/deploymentDeployment OverviewDeployment & Operations
/docs/kernel/runtime-servicesRuntime Service APIsRuntime Services
/docs/kernel/contractsService Contracts OverviewService Contracts
/docs/aiAI OverviewAI
/docs/apiAPI OverviewAPI & SDK

No folder is judged worse: the page stays in the sidebar at the same tree
position, one click away, and this is already how the 18 folders that never
listed "index" render (references, protocol/objectql, protocol/objectui,
…). /docs/getting-started is the widest label gap and is flagged for review
rather than decided here.

Breadcrumb — rendered BreadcrumbList, after

/docs/data-modeling/objects ObjectStack > Documentation > Data Modeling(/docs/data-modeling) > Object Metadata
/docs/getting-started/glossary ObjectStack > Documentation > Get Started(/docs/getting-started) > Glossary
/docs/protocol/objectql/query-syntax ObjectStack > Documentation > Protocol Spec(/docs/protocol) > Data Protocol(/docs/protocol/objectql) > Query Syntax
/docs/releases/v17 ObjectStack > Documentation > v17.0.0 (still short — fenced folder)

The first is the card's own example, which shipped three crumbs with the section
missing. The third is the card's control: it was already linking
/docs/protocol/objectql and now also links /docs/protocol, because
content/docs/protocol/meta.json listed "index" too.

One comment-only edit outside content/

apps/docs/app/[lang]/docs/[[...slug]]/page.tsx carries a doc comment that
states this defect as live and quantifies it ("17 of the 35 …", "172 of 403 …").
Landing this would make that text false, so the paragraph is rewritten to the
post-fix state and to name the one folder still affected. No executable line
changes
— every changed line in that file starts with *, checked
mechanically. The ⛔ fence against reconstructing the URL consumer-side is kept
verbatim and is untouched.

Verification

At 1a5ba4fa4, the branch head:

  • The 43 gate families node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
    derives for this change set: 42 green, 1 NOT MEASURED. The exception is
    scripts/check-test-completeness.mjs, which grades a saved turbo run test
    log and exits 3 with PREREQUISITE NOT MET when the family names it with no
    argument — its own header says to record it as NOT MEASURED locally. Three
    gates first returned PREREQUISITE NOT MET for unbuilt workspace packages
    (@objectstack/formula, @objectstack/lint, packages/spec/json-schema) and
    one for a missing @objectstack/client-react build; all four were rebuilt and
    re-run green, and none of those first readings is reported as a result.
  • pnpm lint (eslint . --no-inline-config, whole repo, no narrowing) — clean.
  • pnpm --filter @objectstack/docs run typecheck
    (fumadocs-mdx && next typegen && tsc --noEmit) — clean.
  • node scripts/check-section-landing-index.mjs green, and by construction: that
    gate already filters 'index' out of the pages array it reads, so the
    landing-page index blocks are unaffected.

Every exit code was captured before any pipe.

Not in scope

The inbound-links work from the sibling card of the same sweep is untouched here.


Generated by Claude Code


Generated by Claude Code

…folder index (#12352)
Fumadocs attaches a folder's index.mdx as that folder's tree `index` node only
when the folder's meta.json does NOT list "index" in `pages`. Listing it makes
the page an ordinary child and leaves the folder node with a name and no url,
which degrades both surfaces built from that node: `getBreadcrumbItems()` drops
the un-linkable ancestor, and the sidebar renders an inert `SidebarFolderTrigger`
instead of a `SidebarFolderLink`.
Removes "index" from 16 of the 17 meta.json files that listed it.
content/docs/releases/ is fenced by AGENTS.md and is left untouched.
Measured on the real fumadocs-core 16.14.4 loader over content/docs:
short breadcrumb trails 172 -> 8 (the 8 are all under /docs/releases), and the
sidebar delta is exactly 16 folder headers TRIGGER -> LINK plus 16 index
children leaving the child list, with no other entry moved.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
@github-actionsgithub-actionsBot added size/s documentation Improvements or additions to documentation tooling labels Aug 31, 2026
@os-project-manager
os-project-manager marked this pull request as ready for review August 31, 2026 08:33
@os-project-manager
os-project-manager added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit 787d757Aug 31, 2026
37 of 38 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-12352-meta-index-breadcrumb branch August 31, 2026 08:51
os-project-manager added a commit that referenced this pull request Aug 31, 2026
…13711)
Fumadocs attaches a folder's index.mdx as that folder's tree `index` node only
when the folder's meta.json does NOT list "index" in `pages`. Listing it makes
the landing page an ordinary child and leaves the folder node with a name and
no url. In fumadocs-core 16.14.4 `buildFolder()`:
if (indexPath) {
if (excludedPaths.has(indexPath)) delete node.index; // "index" was listed
else excludedPaths.add(indexPath);
}
`getBreadcrumbItems()` still emits that ancestor, but with `url: undefined`, and
`docsTrail()` in apps/docs drops any crumb without a url (Google requires `item`
on every BreadcrumbList entry but the last). The sidebar reads the same node as
`node.index ? SidebarFolderLink : SidebarFolderTrigger`.
Measured on the real loader over content/docs (405 pages), removing this one
line and changing nothing else:
- short breadcrumb trails 8 -> 0; the 8 fixed are exactly the non-landing
pages under /docs/releases (v9, v12..v17, implementation-status)
- the Releases folder header goes TRIGGER -> LINK, indexUrl /docs/releases
- /docs/releases stays in getPages() and source.getPage(['releases']) still
resolves -- the landing page is not unlisted, it becomes the section link
- 405 pages before and after; exactly one sidebar folder header flips and
only the 9 /docs/releases trails change
This was the 17th and last meta.json still listing it; the other 16 were fixed
in #13710, which left this one alone because content/docs/releases/ is fenced
by AGENTS.md. This is the dedicated docs-only PR that fence names as the route.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 1, 2026
…bjectstack-ai#13711) (objectstack-ai#13946)
Fumadocs attaches a folder's index.mdx as that folder's tree `index` node only
when the folder's meta.json does NOT list "index" in `pages`. Listing it makes
the landing page an ordinary child and leaves the folder node with a name and
no url. In fumadocs-core 16.14.4 `buildFolder()`:
if (indexPath) {
if (excludedPaths.has(indexPath)) delete node.index; // "index" was listed
else excludedPaths.add(indexPath);
}
`getBreadcrumbItems()` still emits that ancestor, but with `url: undefined`, and
`docsTrail()` in apps/docs drops any crumb without a url (Google requires `item`
on every BreadcrumbList entry but the last). The sidebar reads the same node as
`node.index ? SidebarFolderLink : SidebarFolderTrigger`.
Measured on the real loader over content/docs (405 pages), removing this one
line and changing nothing else:
- short breadcrumb trails 8 -> 0; the 8 fixed are exactly the non-landing
pages under /docs/releases (v9, v12..v17, implementation-status)
- the Releases folder header goes TRIGGER -> LINK, indexUrl /docs/releases
- /docs/releases stays in getPages() and source.getPage(['releases']) still
resolves -- the landing page is not unlisted, it becomes the section link
- 405 pages before and after; exactly one sidebar folder header flips and
only the 9 /docs/releases trails change
This was the 17th and last meta.json still listing it; the other 16 were fixed
in objectstack-ai#13710, which left this one alone because content/docs/releases/ is fenced
by AGENTS.md. This is the dedicated docs-only PR that fence names as the route.
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/stooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

docs(nav): stop listing "index" in meta.json pages — it detaches the folder index from the tree - #13710

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-12352-meta-index-breadcrumb
Aug 31, 2026
Merged

docs(nav): stop listing "index" in meta.json pages — it detaches the folder index from the tree#13710
os-project-manager merged 1 commit into
mainfrom
claude/issue-12352-meta-index-breadcrumb

Conversation

@os-project-manager

@os-project-manageros-project-manager commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

Part of #12352

Removes "index" from the pages array of 16 of the 17 meta.json files under
content/docs that list it. Producer-side only: no consumer-side URL
reconstruction, in page.tsx or anywhere else.

Mechanism (fumadocs-core 16.14.4, buildFolder() in dist/loader-*.js)

A folder's index.mdx becomes that folder's tree index node only while the
folder's meta.json does not list "index" in pages. Listing it makes the
page an ordinary child and then deletes the folder's index:

if(indexPath){if(excludedPaths.has(indexPath))deletenode.index;// "index" was listedelseexcludedPaths.add(indexPath);}

Two surfaces read that one node:

surfaceconsumerwith "index" listed
breadcrumbgetBreadcrumbItems() links a folder crumb to item.index?.urlancestor has a name and no URL, so docsTrail() drops it
sidebarnode.index ? SidebarFolderLink : SidebarFolderTriggersection header is inert text; the overview page sits below it as a child

Re-derived on current origin/main (not inherited from the card)

Measured by driving the real pinned fumadocs-core loader over content/docs
and applying both consumers to the resulting tree.

  • 17 of 35meta.json files list "index" — unchanged from the card.
  • 172 short trails, and the per-top-folder breakdown matches the card's
    production-build numbers row for row (ai 8 · api 11 · automation 9 ·
    capabilities 10 · concepts 5 · data-modeling 17 · deployment 11 ·
    getting-started 8 · kernel 22 · permissions 20 · plugins 4 · protocol 24 ·
    releases 8 · ui 15).
  • Page count reads 404 where the card read 403 sitemap URLs; the extra one is
    /docs itself, whose trail was already complete. 404 = 232 + 172 against the
    card's 403 = 231 + 172, so the two harnesses agree exactly.

After: short trails 172 to 8. The remaining 8 are every page under
/docs/releases.

Per-file decisions — checked individually, not blanket-edited

"index" was the firstpages entry in 15 files and the first entry after
the ---Start Here--- separator in getting-started. No file placed it between
other entries, so no entry's position depends on it. Each edit asserted, per
file, that pages had exactly one "index", that the resulting array equals the
original minus that one element, and that no other key changed.

content/docs/releases/meta.json is not touched: content/docs/releases/ is
fenced by AGENTS.md and by this card's dispatch. It still lists "index", which
is why this is Part of and not a closing reference — #12352 stays open for that
one file, and whether it should be swept in a dedicated docs-only PR is the
maintainer's call.

Sidebar — verified rendered, before and after

The ruling requires the sidebar, not only the JSON-LD. Captured from a running
dev server, with content/docs reverted to this branch's parent commit for the
"before" pass and restored from HEAD afterwards (git diff HEAD empty, blob
hashes back to the HEAD blobs).

/docs/getting-started/glossary, sidebar entries in document order:

-FOLDER-TRIGGER | Get Started | None
+FOLDER-LINK | Get Started | /docs/getting-started
separator | Start Here | None
-link | What is ObjectStack? | /docs/getting-started
link | How AI Development Works | /docs/getting-started/how-ai-development-works

/docs/data-modeling/objects:

-FOLDER-TRIGGER | Data Modeling | None
-link | Data Modeling | /docs/data-modeling
+FOLDER-LINK | Data Modeling | /docs/data-modeling
link | Schema Design | /docs/data-modeling/schema-design

FOLDER-LINK | Reference | /docs/references renders identically in both passes —
a folder that never listed "index", i.e. the control.

Over the whole tree the delta is exactly 16 headers TRIGGER to LINK plus 16
index children leaving the child list, and nothing else
: every removed child's
URL is now its folder header's href (set equality, checked mechanically), and
no other line moved in either direction. SidebarFolderLink still renders the
collapse chevron, so no section loses collapsibility.

Six of the 16 removed a child whose label was identical to the section header
(Automation, What Can It Do?, Core Concepts, Data Modeling,
Kernel & Services, Permissions & Identity) — a duplicate row, now gone. The
other ten swap the page title for the folder title on the same link:

linksidebar label beforeafter
/docs/getting-startedWhat is ObjectStack?Get Started
/docs/uiUI EngineViews & Apps
/docs/pluginsPlugin SystemPlugins & Packages
/docs/protocol/kernelKernel: The System ProtocolSystem Protocol
/docs/protocolProtocol SpecificationProtocol Spec
/docs/deploymentDeployment OverviewDeployment & Operations
/docs/kernel/runtime-servicesRuntime Service APIsRuntime Services
/docs/kernel/contractsService Contracts OverviewService Contracts
/docs/aiAI OverviewAI
/docs/apiAPI OverviewAPI & SDK

No folder is judged worse: the page stays in the sidebar at the same tree
position, one click away, and this is already how the 18 folders that never
listed "index" render (references, protocol/objectql, protocol/objectui,
…). /docs/getting-started is the widest label gap and is flagged for review
rather than decided here.

Breadcrumb — rendered BreadcrumbList, after

/docs/data-modeling/objects ObjectStack > Documentation > Data Modeling(/docs/data-modeling) > Object Metadata
/docs/getting-started/glossary ObjectStack > Documentation > Get Started(/docs/getting-started) > Glossary
/docs/protocol/objectql/query-syntax ObjectStack > Documentation > Protocol Spec(/docs/protocol) > Data Protocol(/docs/protocol/objectql) > Query Syntax
/docs/releases/v17 ObjectStack > Documentation > v17.0.0 (still short — fenced folder)

The first is the card's own example, which shipped three crumbs with the section
missing. The third is the card's control: it was already linking
/docs/protocol/objectql and now also links /docs/protocol, because
content/docs/protocol/meta.json listed "index" too.

One comment-only edit outside content/

apps/docs/app/[lang]/docs/[[...slug]]/page.tsx carries a doc comment that
states this defect as live and quantifies it ("17 of the 35 …", "172 of 403 …").
Landing this would make that text false, so the paragraph is rewritten to the
post-fix state and to name the one folder still affected. No executable line
changes
— every changed line in that file starts with *, checked
mechanically. The ⛔ fence against reconstructing the URL consumer-side is kept
verbatim and is untouched.

Verification

At 1a5ba4fa4, the branch head:

  • The 43 gate families node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
    derives for this change set: 42 green, 1 NOT MEASURED. The exception is
    scripts/check-test-completeness.mjs, which grades a saved turbo run test
    log and exits 3 with PREREQUISITE NOT MET when the family names it with no
    argument — its own header says to record it as NOT MEASURED locally. Three
    gates first returned PREREQUISITE NOT MET for unbuilt workspace packages
    (@objectstack/formula, @objectstack/lint, packages/spec/json-schema) and
    one for a missing @objectstack/client-react build; all four were rebuilt and
    re-run green, and none of those first readings is reported as a result.
  • pnpm lint (eslint . --no-inline-config, whole repo, no narrowing) — clean.
  • pnpm --filter @objectstack/docs run typecheck
    (fumadocs-mdx && next typegen && tsc --noEmit) — clean.
  • node scripts/check-section-landing-index.mjs green, and by construction: that
    gate already filters 'index' out of the pages array it reads, so the
    landing-page index blocks are unaffected.

Every exit code was captured before any pipe.

Not in scope

The inbound-links work from the sibling card of the same sweep is untouched here.


Generated by Claude Code


Generated by Claude Code

…folder index (#12352)
Fumadocs attaches a folder's index.mdx as that folder's tree `index` node only
when the folder's meta.json does NOT list "index" in `pages`. Listing it makes
the page an ordinary child and leaves the folder node with a name and no url,
which degrades both surfaces built from that node: `getBreadcrumbItems()` drops
the un-linkable ancestor, and the sidebar renders an inert `SidebarFolderTrigger`
instead of a `SidebarFolderLink`.
Removes "index" from 16 of the 17 meta.json files that listed it.
content/docs/releases/ is fenced by AGENTS.md and is left untouched.
Measured on the real fumadocs-core 16.14.4 loader over content/docs:
short breadcrumb trails 172 -> 8 (the 8 are all under /docs/releases), and the
sidebar delta is exactly 16 folder headers TRIGGER -> LINK plus 16 index
children leaving the child list, with no other entry moved.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
@github-actionsgithub-actionsBot added size/s documentation Improvements or additions to documentation tooling labels Aug 31, 2026
@os-project-manager
os-project-manager marked this pull request as ready for review August 31, 2026 08:33
@os-project-manager
os-project-manager added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit 787d757Aug 31, 2026
37 of 38 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-12352-meta-index-breadcrumb branch August 31, 2026 08:51
os-project-manager added a commit that referenced this pull request Aug 31, 2026
…13711)
Fumadocs attaches a folder's index.mdx as that folder's tree `index` node only
when the folder's meta.json does NOT list "index" in `pages`. Listing it makes
the landing page an ordinary child and leaves the folder node with a name and
no url. In fumadocs-core 16.14.4 `buildFolder()`:
if (indexPath) {
if (excludedPaths.has(indexPath)) delete node.index; // "index" was listed
else excludedPaths.add(indexPath);
}
`getBreadcrumbItems()` still emits that ancestor, but with `url: undefined`, and
`docsTrail()` in apps/docs drops any crumb without a url (Google requires `item`
on every BreadcrumbList entry but the last). The sidebar reads the same node as
`node.index ? SidebarFolderLink : SidebarFolderTrigger`.
Measured on the real loader over content/docs (405 pages), removing this one
line and changing nothing else:
- short breadcrumb trails 8 -> 0; the 8 fixed are exactly the non-landing
pages under /docs/releases (v9, v12..v17, implementation-status)
- the Releases folder header goes TRIGGER -> LINK, indexUrl /docs/releases
- /docs/releases stays in getPages() and source.getPage(['releases']) still
resolves -- the landing page is not unlisted, it becomes the section link
- 405 pages before and after; exactly one sidebar folder header flips and
only the 9 /docs/releases trails change
This was the 17th and last meta.json still listing it; the other 16 were fixed
in #13710, which left this one alone because content/docs/releases/ is fenced
by AGENTS.md. This is the dedicated docs-only PR that fence names as the route.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 1, 2026
…bjectstack-ai#13711) (objectstack-ai#13946)
Fumadocs attaches a folder's index.mdx as that folder's tree `index` node only
when the folder's meta.json does NOT list "index" in `pages`. Listing it makes
the landing page an ordinary child and leaves the folder node with a name and
no url. In fumadocs-core 16.14.4 `buildFolder()`:
if (indexPath) {
if (excludedPaths.has(indexPath)) delete node.index; // "index" was listed
else excludedPaths.add(indexPath);
}
`getBreadcrumbItems()` still emits that ancestor, but with `url: undefined`, and
`docsTrail()` in apps/docs drops any crumb without a url (Google requires `item`
on every BreadcrumbList entry but the last). The sidebar reads the same node as
`node.index ? SidebarFolderLink : SidebarFolderTrigger`.
Measured on the real loader over content/docs (405 pages), removing this one
line and changing nothing else:
- short breadcrumb trails 8 -> 0; the 8 fixed are exactly the non-landing
pages under /docs/releases (v9, v12..v17, implementation-status)
- the Releases folder header goes TRIGGER -> LINK, indexUrl /docs/releases
- /docs/releases stays in getPages() and source.getPage(['releases']) still
resolves -- the landing page is not unlisted, it becomes the section link
- 405 pages before and after; exactly one sidebar folder header flips and
only the 9 /docs/releases trails change
This was the 17th and last meta.json still listing it; the other 16 were fixed
in objectstack-ai#13710, which left this one alone because content/docs/releases/ is fenced
by AGENTS.md. This is the dedicated docs-only PR that fence names as the route.
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/stooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

docs(nav): stop listing "index" in meta.json pages — it detaches the folder index from the tree - #13710

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-12352-meta-index-breadcrumb
Aug 31, 2026
Merged

docs(nav): stop listing "index" in meta.json pages — it detaches the folder index from the tree#13710
os-project-manager merged 1 commit into
mainfrom
claude/issue-12352-meta-index-breadcrumb

Conversation

@os-project-manager

@os-project-manageros-project-manager commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

Part of #12352

Removes "index" from the pages array of 16 of the 17 meta.json files under
content/docs that list it. Producer-side only: no consumer-side URL
reconstruction, in page.tsx or anywhere else.

Mechanism (fumadocs-core 16.14.4, buildFolder() in dist/loader-*.js)

A folder's index.mdx becomes that folder's tree index node only while the
folder's meta.json does not list "index" in pages. Listing it makes the
page an ordinary child and then deletes the folder's index:

if(indexPath){if(excludedPaths.has(indexPath))deletenode.index;// "index" was listedelseexcludedPaths.add(indexPath);}

Two surfaces read that one node:

surfaceconsumerwith "index" listed
breadcrumbgetBreadcrumbItems() links a folder crumb to item.index?.urlancestor has a name and no URL, so docsTrail() drops it
sidebarnode.index ? SidebarFolderLink : SidebarFolderTriggersection header is inert text; the overview page sits below it as a child

Re-derived on current origin/main (not inherited from the card)

Measured by driving the real pinned fumadocs-core loader over content/docs
and applying both consumers to the resulting tree.

  • 17 of 35meta.json files list "index" — unchanged from the card.
  • 172 short trails, and the per-top-folder breakdown matches the card's
    production-build numbers row for row (ai 8 · api 11 · automation 9 ·
    capabilities 10 · concepts 5 · data-modeling 17 · deployment 11 ·
    getting-started 8 · kernel 22 · permissions 20 · plugins 4 · protocol 24 ·
    releases 8 · ui 15).
  • Page count reads 404 where the card read 403 sitemap URLs; the extra one is
    /docs itself, whose trail was already complete. 404 = 232 + 172 against the
    card's 403 = 231 + 172, so the two harnesses agree exactly.

After: short trails 172 to 8. The remaining 8 are every page under
/docs/releases.

Per-file decisions — checked individually, not blanket-edited

"index" was the firstpages entry in 15 files and the first entry after
the ---Start Here--- separator in getting-started. No file placed it between
other entries, so no entry's position depends on it. Each edit asserted, per
file, that pages had exactly one "index", that the resulting array equals the
original minus that one element, and that no other key changed.

content/docs/releases/meta.json is not touched: content/docs/releases/ is
fenced by AGENTS.md and by this card's dispatch. It still lists "index", which
is why this is Part of and not a closing reference — #12352 stays open for that
one file, and whether it should be swept in a dedicated docs-only PR is the
maintainer's call.

Sidebar — verified rendered, before and after

The ruling requires the sidebar, not only the JSON-LD. Captured from a running
dev server, with content/docs reverted to this branch's parent commit for the
"before" pass and restored from HEAD afterwards (git diff HEAD empty, blob
hashes back to the HEAD blobs).

/docs/getting-started/glossary, sidebar entries in document order:

-FOLDER-TRIGGER | Get Started | None
+FOLDER-LINK | Get Started | /docs/getting-started
separator | Start Here | None
-link | What is ObjectStack? | /docs/getting-started
link | How AI Development Works | /docs/getting-started/how-ai-development-works

/docs/data-modeling/objects:

-FOLDER-TRIGGER | Data Modeling | None
-link | Data Modeling | /docs/data-modeling
+FOLDER-LINK | Data Modeling | /docs/data-modeling
link | Schema Design | /docs/data-modeling/schema-design

FOLDER-LINK | Reference | /docs/references renders identically in both passes —
a folder that never listed "index", i.e. the control.

Over the whole tree the delta is exactly 16 headers TRIGGER to LINK plus 16
index children leaving the child list, and nothing else
: every removed child's
URL is now its folder header's href (set equality, checked mechanically), and
no other line moved in either direction. SidebarFolderLink still renders the
collapse chevron, so no section loses collapsibility.

Six of the 16 removed a child whose label was identical to the section header
(Automation, What Can It Do?, Core Concepts, Data Modeling,
Kernel & Services, Permissions & Identity) — a duplicate row, now gone. The
other ten swap the page title for the folder title on the same link:

linksidebar label beforeafter
/docs/getting-startedWhat is ObjectStack?Get Started
/docs/uiUI EngineViews & Apps
/docs/pluginsPlugin SystemPlugins & Packages
/docs/protocol/kernelKernel: The System ProtocolSystem Protocol
/docs/protocolProtocol SpecificationProtocol Spec
/docs/deploymentDeployment OverviewDeployment & Operations
/docs/kernel/runtime-servicesRuntime Service APIsRuntime Services
/docs/kernel/contractsService Contracts OverviewService Contracts
/docs/aiAI OverviewAI
/docs/apiAPI OverviewAPI & SDK

No folder is judged worse: the page stays in the sidebar at the same tree
position, one click away, and this is already how the 18 folders that never
listed "index" render (references, protocol/objectql, protocol/objectui,
…). /docs/getting-started is the widest label gap and is flagged for review
rather than decided here.

Breadcrumb — rendered BreadcrumbList, after

/docs/data-modeling/objects ObjectStack > Documentation > Data Modeling(/docs/data-modeling) > Object Metadata
/docs/getting-started/glossary ObjectStack > Documentation > Get Started(/docs/getting-started) > Glossary
/docs/protocol/objectql/query-syntax ObjectStack > Documentation > Protocol Spec(/docs/protocol) > Data Protocol(/docs/protocol/objectql) > Query Syntax
/docs/releases/v17 ObjectStack > Documentation > v17.0.0 (still short — fenced folder)

The first is the card's own example, which shipped three crumbs with the section
missing. The third is the card's control: it was already linking
/docs/protocol/objectql and now also links /docs/protocol, because
content/docs/protocol/meta.json listed "index" too.

One comment-only edit outside content/

apps/docs/app/[lang]/docs/[[...slug]]/page.tsx carries a doc comment that
states this defect as live and quantifies it ("17 of the 35 …", "172 of 403 …").
Landing this would make that text false, so the paragraph is rewritten to the
post-fix state and to name the one folder still affected. No executable line
changes
— every changed line in that file starts with *, checked
mechanically. The ⛔ fence against reconstructing the URL consumer-side is kept
verbatim and is untouched.

Verification

At 1a5ba4fa4, the branch head:

  • The 43 gate families node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
    derives for this change set: 42 green, 1 NOT MEASURED. The exception is
    scripts/check-test-completeness.mjs, which grades a saved turbo run test
    log and exits 3 with PREREQUISITE NOT MET when the family names it with no
    argument — its own header says to record it as NOT MEASURED locally. Three
    gates first returned PREREQUISITE NOT MET for unbuilt workspace packages
    (@objectstack/formula, @objectstack/lint, packages/spec/json-schema) and
    one for a missing @objectstack/client-react build; all four were rebuilt and
    re-run green, and none of those first readings is reported as a result.
  • pnpm lint (eslint . --no-inline-config, whole repo, no narrowing) — clean.
  • pnpm --filter @objectstack/docs run typecheck
    (fumadocs-mdx && next typegen && tsc --noEmit) — clean.
  • node scripts/check-section-landing-index.mjs green, and by construction: that
    gate already filters 'index' out of the pages array it reads, so the
    landing-page index blocks are unaffected.

Every exit code was captured before any pipe.

Not in scope

The inbound-links work from the sibling card of the same sweep is untouched here.


Generated by Claude Code


Generated by Claude Code

…folder index (#12352)
Fumadocs attaches a folder's index.mdx as that folder's tree `index` node only
when the folder's meta.json does NOT list "index" in `pages`. Listing it makes
the page an ordinary child and leaves the folder node with a name and no url,
which degrades both surfaces built from that node: `getBreadcrumbItems()` drops
the un-linkable ancestor, and the sidebar renders an inert `SidebarFolderTrigger`
instead of a `SidebarFolderLink`.
Removes "index" from 16 of the 17 meta.json files that listed it.
content/docs/releases/ is fenced by AGENTS.md and is left untouched.
Measured on the real fumadocs-core 16.14.4 loader over content/docs:
short breadcrumb trails 172 -> 8 (the 8 are all under /docs/releases), and the
sidebar delta is exactly 16 folder headers TRIGGER -> LINK plus 16 index
children leaving the child list, with no other entry moved.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
@github-actionsgithub-actionsBot added size/s documentation Improvements or additions to documentation tooling labels Aug 31, 2026
@os-project-manager
os-project-manager marked this pull request as ready for review August 31, 2026 08:33
@os-project-manager
os-project-manager added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit 787d757Aug 31, 2026
37 of 38 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-12352-meta-index-breadcrumb branch August 31, 2026 08:51
os-project-manager added a commit that referenced this pull request Aug 31, 2026
…13711)
Fumadocs attaches a folder's index.mdx as that folder's tree `index` node only
when the folder's meta.json does NOT list "index" in `pages`. Listing it makes
the landing page an ordinary child and leaves the folder node with a name and
no url. In fumadocs-core 16.14.4 `buildFolder()`:
if (indexPath) {
if (excludedPaths.has(indexPath)) delete node.index; // "index" was listed
else excludedPaths.add(indexPath);
}
`getBreadcrumbItems()` still emits that ancestor, but with `url: undefined`, and
`docsTrail()` in apps/docs drops any crumb without a url (Google requires `item`
on every BreadcrumbList entry but the last). The sidebar reads the same node as
`node.index ? SidebarFolderLink : SidebarFolderTrigger`.
Measured on the real loader over content/docs (405 pages), removing this one
line and changing nothing else:
- short breadcrumb trails 8 -> 0; the 8 fixed are exactly the non-landing
pages under /docs/releases (v9, v12..v17, implementation-status)
- the Releases folder header goes TRIGGER -> LINK, indexUrl /docs/releases
- /docs/releases stays in getPages() and source.getPage(['releases']) still
resolves -- the landing page is not unlisted, it becomes the section link
- 405 pages before and after; exactly one sidebar folder header flips and
only the 9 /docs/releases trails change
This was the 17th and last meta.json still listing it; the other 16 were fixed
in #13710, which left this one alone because content/docs/releases/ is fenced
by AGENTS.md. This is the dedicated docs-only PR that fence names as the route.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 1, 2026
…bjectstack-ai#13711) (objectstack-ai#13946)
Fumadocs attaches a folder's index.mdx as that folder's tree `index` node only
when the folder's meta.json does NOT list "index" in `pages`. Listing it makes
the landing page an ordinary child and leaves the folder node with a name and
no url. In fumadocs-core 16.14.4 `buildFolder()`:
if (indexPath) {
if (excludedPaths.has(indexPath)) delete node.index; // "index" was listed
else excludedPaths.add(indexPath);
}
`getBreadcrumbItems()` still emits that ancestor, but with `url: undefined`, and
`docsTrail()` in apps/docs drops any crumb without a url (Google requires `item`
on every BreadcrumbList entry but the last). The sidebar reads the same node as
`node.index ? SidebarFolderLink : SidebarFolderTrigger`.
Measured on the real loader over content/docs (405 pages), removing this one
line and changing nothing else:
- short breadcrumb trails 8 -> 0; the 8 fixed are exactly the non-landing
pages under /docs/releases (v9, v12..v17, implementation-status)
- the Releases folder header goes TRIGGER -> LINK, indexUrl /docs/releases
- /docs/releases stays in getPages() and source.getPage(['releases']) still
resolves -- the landing page is not unlisted, it becomes the section link
- 405 pages before and after; exactly one sidebar folder header flips and
only the 9 /docs/releases trails change
This was the 17th and last meta.json still listing it; the other 16 were fixed
in objectstack-ai#13710, which left this one alone because content/docs/releases/ is fenced
by AGENTS.md. This is the dedicated docs-only PR that fence names as the route.
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/stooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

docs(nav): stop listing "index" in meta.json pages — it detaches the folder index from the tree - #13710

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-12352-meta-index-breadcrumb
Aug 31, 2026
Merged

docs(nav): stop listing "index" in meta.json pages — it detaches the folder index from the tree#13710
os-project-manager merged 1 commit into
mainfrom
claude/issue-12352-meta-index-breadcrumb

Conversation

@os-project-manager

@os-project-manageros-project-manager commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

Part of #12352

Removes "index" from the pages array of 16 of the 17 meta.json files under
content/docs that list it. Producer-side only: no consumer-side URL
reconstruction, in page.tsx or anywhere else.

Mechanism (fumadocs-core 16.14.4, buildFolder() in dist/loader-*.js)

A folder's index.mdx becomes that folder's tree index node only while the
folder's meta.json does not list "index" in pages. Listing it makes the
page an ordinary child and then deletes the folder's index:

if(indexPath){if(excludedPaths.has(indexPath))deletenode.index;// "index" was listedelseexcludedPaths.add(indexPath);}

Two surfaces read that one node:

surfaceconsumerwith "index" listed
breadcrumbgetBreadcrumbItems() links a folder crumb to item.index?.urlancestor has a name and no URL, so docsTrail() drops it
sidebarnode.index ? SidebarFolderLink : SidebarFolderTriggersection header is inert text; the overview page sits below it as a child

Re-derived on current origin/main (not inherited from the card)

Measured by driving the real pinned fumadocs-core loader over content/docs
and applying both consumers to the resulting tree.

  • 17 of 35meta.json files list "index" — unchanged from the card.
  • 172 short trails, and the per-top-folder breakdown matches the card's
    production-build numbers row for row (ai 8 · api 11 · automation 9 ·
    capabilities 10 · concepts 5 · data-modeling 17 · deployment 11 ·
    getting-started 8 · kernel 22 · permissions 20 · plugins 4 · protocol 24 ·
    releases 8 · ui 15).
  • Page count reads 404 where the card read 403 sitemap URLs; the extra one is
    /docs itself, whose trail was already complete. 404 = 232 + 172 against the
    card's 403 = 231 + 172, so the two harnesses agree exactly.

After: short trails 172 to 8. The remaining 8 are every page under
/docs/releases.

Per-file decisions — checked individually, not blanket-edited

"index" was the firstpages entry in 15 files and the first entry after
the ---Start Here--- separator in getting-started. No file placed it between
other entries, so no entry's position depends on it. Each edit asserted, per
file, that pages had exactly one "index", that the resulting array equals the
original minus that one element, and that no other key changed.

content/docs/releases/meta.json is not touched: content/docs/releases/ is
fenced by AGENTS.md and by this card's dispatch. It still lists "index", which
is why this is Part of and not a closing reference — #12352 stays open for that
one file, and whether it should be swept in a dedicated docs-only PR is the
maintainer's call.

Sidebar — verified rendered, before and after

The ruling requires the sidebar, not only the JSON-LD. Captured from a running
dev server, with content/docs reverted to this branch's parent commit for the
"before" pass and restored from HEAD afterwards (git diff HEAD empty, blob
hashes back to the HEAD blobs).

/docs/getting-started/glossary, sidebar entries in document order:

-FOLDER-TRIGGER | Get Started | None
+FOLDER-LINK | Get Started | /docs/getting-started
separator | Start Here | None
-link | What is ObjectStack? | /docs/getting-started
link | How AI Development Works | /docs/getting-started/how-ai-development-works

/docs/data-modeling/objects:

-FOLDER-TRIGGER | Data Modeling | None
-link | Data Modeling | /docs/data-modeling
+FOLDER-LINK | Data Modeling | /docs/data-modeling
link | Schema Design | /docs/data-modeling/schema-design

FOLDER-LINK | Reference | /docs/references renders identically in both passes —
a folder that never listed "index", i.e. the control.

Over the whole tree the delta is exactly 16 headers TRIGGER to LINK plus 16
index children leaving the child list, and nothing else
: every removed child's
URL is now its folder header's href (set equality, checked mechanically), and
no other line moved in either direction. SidebarFolderLink still renders the
collapse chevron, so no section loses collapsibility.

Six of the 16 removed a child whose label was identical to the section header
(Automation, What Can It Do?, Core Concepts, Data Modeling,
Kernel & Services, Permissions & Identity) — a duplicate row, now gone. The
other ten swap the page title for the folder title on the same link:

linksidebar label beforeafter
/docs/getting-startedWhat is ObjectStack?Get Started
/docs/uiUI EngineViews & Apps
/docs/pluginsPlugin SystemPlugins & Packages
/docs/protocol/kernelKernel: The System ProtocolSystem Protocol
/docs/protocolProtocol SpecificationProtocol Spec
/docs/deploymentDeployment OverviewDeployment & Operations
/docs/kernel/runtime-servicesRuntime Service APIsRuntime Services
/docs/kernel/contractsService Contracts OverviewService Contracts
/docs/aiAI OverviewAI
/docs/apiAPI OverviewAPI & SDK

No folder is judged worse: the page stays in the sidebar at the same tree
position, one click away, and this is already how the 18 folders that never
listed "index" render (references, protocol/objectql, protocol/objectui,
…). /docs/getting-started is the widest label gap and is flagged for review
rather than decided here.

Breadcrumb — rendered BreadcrumbList, after

/docs/data-modeling/objects ObjectStack > Documentation > Data Modeling(/docs/data-modeling) > Object Metadata
/docs/getting-started/glossary ObjectStack > Documentation > Get Started(/docs/getting-started) > Glossary
/docs/protocol/objectql/query-syntax ObjectStack > Documentation > Protocol Spec(/docs/protocol) > Data Protocol(/docs/protocol/objectql) > Query Syntax
/docs/releases/v17 ObjectStack > Documentation > v17.0.0 (still short — fenced folder)

The first is the card's own example, which shipped three crumbs with the section
missing. The third is the card's control: it was already linking
/docs/protocol/objectql and now also links /docs/protocol, because
content/docs/protocol/meta.json listed "index" too.

One comment-only edit outside content/

apps/docs/app/[lang]/docs/[[...slug]]/page.tsx carries a doc comment that
states this defect as live and quantifies it ("17 of the 35 …", "172 of 403 …").
Landing this would make that text false, so the paragraph is rewritten to the
post-fix state and to name the one folder still affected. No executable line
changes
— every changed line in that file starts with *, checked
mechanically. The ⛔ fence against reconstructing the URL consumer-side is kept
verbatim and is untouched.

Verification

At 1a5ba4fa4, the branch head:

  • The 43 gate families node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
    derives for this change set: 42 green, 1 NOT MEASURED. The exception is
    scripts/check-test-completeness.mjs, which grades a saved turbo run test
    log and exits 3 with PREREQUISITE NOT MET when the family names it with no
    argument — its own header says to record it as NOT MEASURED locally. Three
    gates first returned PREREQUISITE NOT MET for unbuilt workspace packages
    (@objectstack/formula, @objectstack/lint, packages/spec/json-schema) and
    one for a missing @objectstack/client-react build; all four were rebuilt and
    re-run green, and none of those first readings is reported as a result.
  • pnpm lint (eslint . --no-inline-config, whole repo, no narrowing) — clean.
  • pnpm --filter @objectstack/docs run typecheck
    (fumadocs-mdx && next typegen && tsc --noEmit) — clean.
  • node scripts/check-section-landing-index.mjs green, and by construction: that
    gate already filters 'index' out of the pages array it reads, so the
    landing-page index blocks are unaffected.

Every exit code was captured before any pipe.

Not in scope

The inbound-links work from the sibling card of the same sweep is untouched here.


Generated by Claude Code


Generated by Claude Code

…folder index (#12352)
Fumadocs attaches a folder's index.mdx as that folder's tree `index` node only
when the folder's meta.json does NOT list "index" in `pages`. Listing it makes
the page an ordinary child and leaves the folder node with a name and no url,
which degrades both surfaces built from that node: `getBreadcrumbItems()` drops
the un-linkable ancestor, and the sidebar renders an inert `SidebarFolderTrigger`
instead of a `SidebarFolderLink`.
Removes "index" from 16 of the 17 meta.json files that listed it.
content/docs/releases/ is fenced by AGENTS.md and is left untouched.
Measured on the real fumadocs-core 16.14.4 loader over content/docs:
short breadcrumb trails 172 -> 8 (the 8 are all under /docs/releases), and the
sidebar delta is exactly 16 folder headers TRIGGER -> LINK plus 16 index
children leaving the child list, with no other entry moved.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
@github-actionsgithub-actionsBot added size/s documentation Improvements or additions to documentation tooling labels Aug 31, 2026
@os-project-manager
os-project-manager marked this pull request as ready for review August 31, 2026 08:33
@os-project-manager
os-project-manager added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit 787d757Aug 31, 2026
37 of 38 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-12352-meta-index-breadcrumb branch August 31, 2026 08:51
os-project-manager added a commit that referenced this pull request Aug 31, 2026
…13711)
Fumadocs attaches a folder's index.mdx as that folder's tree `index` node only
when the folder's meta.json does NOT list "index" in `pages`. Listing it makes
the landing page an ordinary child and leaves the folder node with a name and
no url. In fumadocs-core 16.14.4 `buildFolder()`:
if (indexPath) {
if (excludedPaths.has(indexPath)) delete node.index; // "index" was listed
else excludedPaths.add(indexPath);
}
`getBreadcrumbItems()` still emits that ancestor, but with `url: undefined`, and
`docsTrail()` in apps/docs drops any crumb without a url (Google requires `item`
on every BreadcrumbList entry but the last). The sidebar reads the same node as
`node.index ? SidebarFolderLink : SidebarFolderTrigger`.
Measured on the real loader over content/docs (405 pages), removing this one
line and changing nothing else:
- short breadcrumb trails 8 -> 0; the 8 fixed are exactly the non-landing
pages under /docs/releases (v9, v12..v17, implementation-status)
- the Releases folder header goes TRIGGER -> LINK, indexUrl /docs/releases
- /docs/releases stays in getPages() and source.getPage(['releases']) still
resolves -- the landing page is not unlisted, it becomes the section link
- 405 pages before and after; exactly one sidebar folder header flips and
only the 9 /docs/releases trails change
This was the 17th and last meta.json still listing it; the other 16 were fixed
in #13710, which left this one alone because content/docs/releases/ is fenced
by AGENTS.md. This is the dedicated docs-only PR that fence names as the route.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 1, 2026
…bjectstack-ai#13711) (objectstack-ai#13946)
Fumadocs attaches a folder's index.mdx as that folder's tree `index` node only
when the folder's meta.json does NOT list "index" in `pages`. Listing it makes
the landing page an ordinary child and leaves the folder node with a name and
no url. In fumadocs-core 16.14.4 `buildFolder()`:
if (indexPath) {
if (excludedPaths.has(indexPath)) delete node.index; // "index" was listed
else excludedPaths.add(indexPath);
}
`getBreadcrumbItems()` still emits that ancestor, but with `url: undefined`, and
`docsTrail()` in apps/docs drops any crumb without a url (Google requires `item`
on every BreadcrumbList entry but the last). The sidebar reads the same node as
`node.index ? SidebarFolderLink : SidebarFolderTrigger`.
Measured on the real loader over content/docs (405 pages), removing this one
line and changing nothing else:
- short breadcrumb trails 8 -> 0; the 8 fixed are exactly the non-landing
pages under /docs/releases (v9, v12..v17, implementation-status)
- the Releases folder header goes TRIGGER -> LINK, indexUrl /docs/releases
- /docs/releases stays in getPages() and source.getPage(['releases']) still
resolves -- the landing page is not unlisted, it becomes the section link
- 405 pages before and after; exactly one sidebar folder header flips and
only the 9 /docs/releases trails change
This was the 17th and last meta.json still listing it; the other 16 were fixed
in objectstack-ai#13710, which left this one alone because content/docs/releases/ is fenced
by AGENTS.md. This is the dedicated docs-only PR that fence names as the route.
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/stooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

docs(nav): stop listing "index" in meta.json pages — it detaches the folder index from the tree - #13710

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-12352-meta-index-breadcrumb
Aug 31, 2026
Merged

docs(nav): stop listing "index" in meta.json pages — it detaches the folder index from the tree#13710
os-project-manager merged 1 commit into
mainfrom
claude/issue-12352-meta-index-breadcrumb

Conversation

@os-project-manager

@os-project-manageros-project-manager commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

Part of #12352

Removes "index" from the pages array of 16 of the 17 meta.json files under
content/docs that list it. Producer-side only: no consumer-side URL
reconstruction, in page.tsx or anywhere else.

Mechanism (fumadocs-core 16.14.4, buildFolder() in dist/loader-*.js)

A folder's index.mdx becomes that folder's tree index node only while the
folder's meta.json does not list "index" in pages. Listing it makes the
page an ordinary child and then deletes the folder's index:

if(indexPath){if(excludedPaths.has(indexPath))deletenode.index;// "index" was listedelseexcludedPaths.add(indexPath);}

Two surfaces read that one node:

surfaceconsumerwith "index" listed
breadcrumbgetBreadcrumbItems() links a folder crumb to item.index?.urlancestor has a name and no URL, so docsTrail() drops it
sidebarnode.index ? SidebarFolderLink : SidebarFolderTriggersection header is inert text; the overview page sits below it as a child

Re-derived on current origin/main (not inherited from the card)

Measured by driving the real pinned fumadocs-core loader over content/docs
and applying both consumers to the resulting tree.

  • 17 of 35meta.json files list "index" — unchanged from the card.
  • 172 short trails, and the per-top-folder breakdown matches the card's
    production-build numbers row for row (ai 8 · api 11 · automation 9 ·
    capabilities 10 · concepts 5 · data-modeling 17 · deployment 11 ·
    getting-started 8 · kernel 22 · permissions 20 · plugins 4 · protocol 24 ·
    releases 8 · ui 15).
  • Page count reads 404 where the card read 403 sitemap URLs; the extra one is
    /docs itself, whose trail was already complete. 404 = 232 + 172 against the
    card's 403 = 231 + 172, so the two harnesses agree exactly.

After: short trails 172 to 8. The remaining 8 are every page under
/docs/releases.

Per-file decisions — checked individually, not blanket-edited

"index" was the firstpages entry in 15 files and the first entry after
the ---Start Here--- separator in getting-started. No file placed it between
other entries, so no entry's position depends on it. Each edit asserted, per
file, that pages had exactly one "index", that the resulting array equals the
original minus that one element, and that no other key changed.

content/docs/releases/meta.json is not touched: content/docs/releases/ is
fenced by AGENTS.md and by this card's dispatch. It still lists "index", which
is why this is Part of and not a closing reference — #12352 stays open for that
one file, and whether it should be swept in a dedicated docs-only PR is the
maintainer's call.

Sidebar — verified rendered, before and after

The ruling requires the sidebar, not only the JSON-LD. Captured from a running
dev server, with content/docs reverted to this branch's parent commit for the
"before" pass and restored from HEAD afterwards (git diff HEAD empty, blob
hashes back to the HEAD blobs).

/docs/getting-started/glossary, sidebar entries in document order:

-FOLDER-TRIGGER | Get Started | None
+FOLDER-LINK | Get Started | /docs/getting-started
separator | Start Here | None
-link | What is ObjectStack? | /docs/getting-started
link | How AI Development Works | /docs/getting-started/how-ai-development-works

/docs/data-modeling/objects:

-FOLDER-TRIGGER | Data Modeling | None
-link | Data Modeling | /docs/data-modeling
+FOLDER-LINK | Data Modeling | /docs/data-modeling
link | Schema Design | /docs/data-modeling/schema-design

FOLDER-LINK | Reference | /docs/references renders identically in both passes —
a folder that never listed "index", i.e. the control.

Over the whole tree the delta is exactly 16 headers TRIGGER to LINK plus 16
index children leaving the child list, and nothing else
: every removed child's
URL is now its folder header's href (set equality, checked mechanically), and
no other line moved in either direction. SidebarFolderLink still renders the
collapse chevron, so no section loses collapsibility.

Six of the 16 removed a child whose label was identical to the section header
(Automation, What Can It Do?, Core Concepts, Data Modeling,
Kernel & Services, Permissions & Identity) — a duplicate row, now gone. The
other ten swap the page title for the folder title on the same link:

linksidebar label beforeafter
/docs/getting-startedWhat is ObjectStack?Get Started
/docs/uiUI EngineViews & Apps
/docs/pluginsPlugin SystemPlugins & Packages
/docs/protocol/kernelKernel: The System ProtocolSystem Protocol
/docs/protocolProtocol SpecificationProtocol Spec
/docs/deploymentDeployment OverviewDeployment & Operations
/docs/kernel/runtime-servicesRuntime Service APIsRuntime Services
/docs/kernel/contractsService Contracts OverviewService Contracts
/docs/aiAI OverviewAI
/docs/apiAPI OverviewAPI & SDK

No folder is judged worse: the page stays in the sidebar at the same tree
position, one click away, and this is already how the 18 folders that never
listed "index" render (references, protocol/objectql, protocol/objectui,
…). /docs/getting-started is the widest label gap and is flagged for review
rather than decided here.

Breadcrumb — rendered BreadcrumbList, after

/docs/data-modeling/objects ObjectStack > Documentation > Data Modeling(/docs/data-modeling) > Object Metadata
/docs/getting-started/glossary ObjectStack > Documentation > Get Started(/docs/getting-started) > Glossary
/docs/protocol/objectql/query-syntax ObjectStack > Documentation > Protocol Spec(/docs/protocol) > Data Protocol(/docs/protocol/objectql) > Query Syntax
/docs/releases/v17 ObjectStack > Documentation > v17.0.0 (still short — fenced folder)

The first is the card's own example, which shipped three crumbs with the section
missing. The third is the card's control: it was already linking
/docs/protocol/objectql and now also links /docs/protocol, because
content/docs/protocol/meta.json listed "index" too.

One comment-only edit outside content/

apps/docs/app/[lang]/docs/[[...slug]]/page.tsx carries a doc comment that
states this defect as live and quantifies it ("17 of the 35 …", "172 of 403 …").
Landing this would make that text false, so the paragraph is rewritten to the
post-fix state and to name the one folder still affected. No executable line
changes
— every changed line in that file starts with *, checked
mechanically. The ⛔ fence against reconstructing the URL consumer-side is kept
verbatim and is untouched.

Verification

At 1a5ba4fa4, the branch head:

  • The 43 gate families node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
    derives for this change set: 42 green, 1 NOT MEASURED. The exception is
    scripts/check-test-completeness.mjs, which grades a saved turbo run test
    log and exits 3 with PREREQUISITE NOT MET when the family names it with no
    argument — its own header says to record it as NOT MEASURED locally. Three
    gates first returned PREREQUISITE NOT MET for unbuilt workspace packages
    (@objectstack/formula, @objectstack/lint, packages/spec/json-schema) and
    one for a missing @objectstack/client-react build; all four were rebuilt and
    re-run green, and none of those first readings is reported as a result.
  • pnpm lint (eslint . --no-inline-config, whole repo, no narrowing) — clean.
  • pnpm --filter @objectstack/docs run typecheck
    (fumadocs-mdx && next typegen && tsc --noEmit) — clean.
  • node scripts/check-section-landing-index.mjs green, and by construction: that
    gate already filters 'index' out of the pages array it reads, so the
    landing-page index blocks are unaffected.

Every exit code was captured before any pipe.

Not in scope

The inbound-links work from the sibling card of the same sweep is untouched here.


Generated by Claude Code


Generated by Claude Code

…folder index (#12352)
Fumadocs attaches a folder's index.mdx as that folder's tree `index` node only
when the folder's meta.json does NOT list "index" in `pages`. Listing it makes
the page an ordinary child and leaves the folder node with a name and no url,
which degrades both surfaces built from that node: `getBreadcrumbItems()` drops
the un-linkable ancestor, and the sidebar renders an inert `SidebarFolderTrigger`
instead of a `SidebarFolderLink`.
Removes "index" from 16 of the 17 meta.json files that listed it.
content/docs/releases/ is fenced by AGENTS.md and is left untouched.
Measured on the real fumadocs-core 16.14.4 loader over content/docs:
short breadcrumb trails 172 -> 8 (the 8 are all under /docs/releases), and the
sidebar delta is exactly 16 folder headers TRIGGER -> LINK plus 16 index
children leaving the child list, with no other entry moved.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
@github-actionsgithub-actionsBot added size/s documentation Improvements or additions to documentation tooling labels Aug 31, 2026
@os-project-manager
os-project-manager marked this pull request as ready for review August 31, 2026 08:33
@os-project-manager
os-project-manager added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit 787d757Aug 31, 2026
37 of 38 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-12352-meta-index-breadcrumb branch August 31, 2026 08:51
os-project-manager added a commit that referenced this pull request Aug 31, 2026
…13711)
Fumadocs attaches a folder's index.mdx as that folder's tree `index` node only
when the folder's meta.json does NOT list "index" in `pages`. Listing it makes
the landing page an ordinary child and leaves the folder node with a name and
no url. In fumadocs-core 16.14.4 `buildFolder()`:
if (indexPath) {
if (excludedPaths.has(indexPath)) delete node.index; // "index" was listed
else excludedPaths.add(indexPath);
}
`getBreadcrumbItems()` still emits that ancestor, but with `url: undefined`, and
`docsTrail()` in apps/docs drops any crumb without a url (Google requires `item`
on every BreadcrumbList entry but the last). The sidebar reads the same node as
`node.index ? SidebarFolderLink : SidebarFolderTrigger`.
Measured on the real loader over content/docs (405 pages), removing this one
line and changing nothing else:
- short breadcrumb trails 8 -> 0; the 8 fixed are exactly the non-landing
pages under /docs/releases (v9, v12..v17, implementation-status)
- the Releases folder header goes TRIGGER -> LINK, indexUrl /docs/releases
- /docs/releases stays in getPages() and source.getPage(['releases']) still
resolves -- the landing page is not unlisted, it becomes the section link
- 405 pages before and after; exactly one sidebar folder header flips and
only the 9 /docs/releases trails change
This was the 17th and last meta.json still listing it; the other 16 were fixed
in #13710, which left this one alone because content/docs/releases/ is fenced
by AGENTS.md. This is the dedicated docs-only PR that fence names as the route.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 1, 2026
…bjectstack-ai#13711) (objectstack-ai#13946)
Fumadocs attaches a folder's index.mdx as that folder's tree `index` node only
when the folder's meta.json does NOT list "index" in `pages`. Listing it makes
the landing page an ordinary child and leaves the folder node with a name and
no url. In fumadocs-core 16.14.4 `buildFolder()`:
if (indexPath) {
if (excludedPaths.has(indexPath)) delete node.index; // "index" was listed
else excludedPaths.add(indexPath);
}
`getBreadcrumbItems()` still emits that ancestor, but with `url: undefined`, and
`docsTrail()` in apps/docs drops any crumb without a url (Google requires `item`
on every BreadcrumbList entry but the last). The sidebar reads the same node as
`node.index ? SidebarFolderLink : SidebarFolderTrigger`.
Measured on the real loader over content/docs (405 pages), removing this one
line and changing nothing else:
- short breadcrumb trails 8 -> 0; the 8 fixed are exactly the non-landing
pages under /docs/releases (v9, v12..v17, implementation-status)
- the Releases folder header goes TRIGGER -> LINK, indexUrl /docs/releases
- /docs/releases stays in getPages() and source.getPage(['releases']) still
resolves -- the landing page is not unlisted, it becomes the section link
- 405 pages before and after; exactly one sidebar folder header flips and
only the 9 /docs/releases trails change
This was the 17th and last meta.json still listing it; the other 16 were fixed
in objectstack-ai#13710, which left this one alone because content/docs/releases/ is fenced
by AGENTS.md. This is the dedicated docs-only PR that fence names as the route.
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/stooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-project-manager@claude
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

docs(nav): stop listing "index" in meta.json pages — it detaches the folder index from the tree - #13710

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-12352-meta-index-breadcrumb
Aug 31, 2026
Merged

docs(nav): stop listing "index" in meta.json pages — it detaches the folder index from the tree#13710
os-project-manager merged 1 commit into
mainfrom
claude/issue-12352-meta-index-breadcrumb

Conversation

@os-project-manager

@os-project-manageros-project-manager commented Aug 31, 2026

Copy link
Copy Markdown
Collaborator

Part of #12352

Removes "index" from the pages array of 16 of the 17 meta.json files under
content/docs that list it. Producer-side only: no consumer-side URL
reconstruction, in page.tsx or anywhere else.

Mechanism (fumadocs-core 16.14.4, buildFolder() in dist/loader-*.js)

A folder's index.mdx becomes that folder's tree index node only while the
folder's meta.json does not list "index" in pages. Listing it makes the
page an ordinary child and then deletes the folder's index:

if(indexPath){if(excludedPaths.has(indexPath))deletenode.index;// "index" was listedelseexcludedPaths.add(indexPath);}

Two surfaces read that one node:

surfaceconsumerwith "index" listed
breadcrumbgetBreadcrumbItems() links a folder crumb to item.index?.urlancestor has a name and no URL, so docsTrail() drops it
sidebarnode.index ? SidebarFolderLink : SidebarFolderTriggersection header is inert text; the overview page sits below it as a child

Re-derived on current origin/main (not inherited from the card)

Measured by driving the real pinned fumadocs-core loader over content/docs
and applying both consumers to the resulting tree.

  • 17 of 35meta.json files list "index" — unchanged from the card.
  • 172 short trails, and the per-top-folder breakdown matches the card's
    production-build numbers row for row (ai 8 · api 11 · automation 9 ·
    capabilities 10 · concepts 5 · data-modeling 17 · deployment 11 ·
    getting-started 8 · kernel 22 · permissions 20 · plugins 4 · protocol 24 ·
    releases 8 · ui 15).
  • Page count reads 404 where the card read 403 sitemap URLs; the extra one is
    /docs itself, whose trail was already complete. 404 = 232 + 172 against the
    card's 403 = 231 + 172, so the two harnesses agree exactly.

After: short trails 172 to 8. The remaining 8 are every page under
/docs/releases.

Per-file decisions — checked individually, not blanket-edited

"index" was the firstpages entry in 15 files and the first entry after
the ---Start Here--- separator in getting-started. No file placed it between
other entries, so no entry's position depends on it. Each edit asserted, per
file, that pages had exactly one "index", that the resulting array equals the
original minus that one element, and that no other key changed.

content/docs/releases/meta.json is not touched: content/docs/releases/ is
fenced by AGENTS.md and by this card's dispatch. It still lists "index", which
is why this is Part of and not a closing reference — #12352 stays open for that
one file, and whether it should be swept in a dedicated docs-only PR is the
maintainer's call.

Sidebar — verified rendered, before and after

The ruling requires the sidebar, not only the JSON-LD. Captured from a running
dev server, with content/docs reverted to this branch's parent commit for the
"before" pass and restored from HEAD afterwards (git diff HEAD empty, blob
hashes back to the HEAD blobs).

/docs/getting-started/glossary, sidebar entries in document order:

-FOLDER-TRIGGER | Get Started | None
+FOLDER-LINK | Get Started | /docs/getting-started
separator | Start Here | None
-link | What is ObjectStack? | /docs/getting-started
link | How AI Development Works | /docs/getting-started/how-ai-development-works

/docs/data-modeling/objects:

-FOLDER-TRIGGER | Data Modeling | None
-link | Data Modeling | /docs/data-modeling
+FOLDER-LINK | Data Modeling | /docs/data-modeling
link | Schema Design | /docs/data-modeling/schema-design

FOLDER-LINK | Reference | /docs/references renders identically in both passes —
a folder that never listed "index", i.e. the control.

Over the whole tree the delta is exactly 16 headers TRIGGER to LINK plus 16
index children leaving the child list, and nothing else
: every removed child's
URL is now its folder header's href (set equality, checked mechanically), and
no other line moved in either direction. SidebarFolderLink still renders the
collapse chevron, so no section loses collapsibility.

Six of the 16 removed a child whose label was identical to the section header
(Automation, What Can It Do?, Core Concepts, Data Modeling,
Kernel & Services, Permissions & Identity) — a duplicate row, now gone. The
other ten swap the page title for the folder title on the same link:

linksidebar label beforeafter
/docs/getting-startedWhat is ObjectStack?Get Started
/docs/uiUI EngineViews & Apps
/docs/pluginsPlugin SystemPlugins & Packages
/docs/protocol/kernelKernel: The System ProtocolSystem Protocol
/docs/protocolProtocol SpecificationProtocol Spec
/docs/deploymentDeployment OverviewDeployment & Operations
/docs/kernel/runtime-servicesRuntime Service APIsRuntime Services
/docs/kernel/contractsService Contracts OverviewService Contracts
/docs/aiAI OverviewAI
/docs/apiAPI OverviewAPI & SDK

No folder is judged worse: the page stays in the sidebar at the same tree
position, one click away, and this is already how the 18 folders that never
listed "index" render (references, protocol/objectql, protocol/objectui,
…). /docs/getting-started is the widest label gap and is flagged for review
rather than decided here.

Breadcrumb — rendered BreadcrumbList, after

/docs/data-modeling/objects ObjectStack > Documentation > Data Modeling(/docs/data-modeling) > Object Metadata
/docs/getting-started/glossary ObjectStack > Documentation > Get Started(/docs/getting-started) > Glossary
/docs/protocol/objectql/query-syntax ObjectStack > Documentation > Protocol Spec(/docs/protocol) > Data Protocol(/docs/protocol/objectql) > Query Syntax
/docs/releases/v17 ObjectStack > Documentation > v17.0.0 (still short — fenced folder)

The first is the card's own example, which shipped three crumbs with the section
missing. The third is the card's control: it was already linking
/docs/protocol/objectql and now also links /docs/protocol, because
content/docs/protocol/meta.json listed "index" too.

One comment-only edit outside content/

apps/docs/app/[lang]/docs/[[...slug]]/page.tsx carries a doc comment that
states this defect as live and quantifies it ("17 of the 35 …", "172 of 403 …").
Landing this would make that text false, so the paragraph is rewritten to the
post-fix state and to name the one folder still affected. No executable line
changes
— every changed line in that file starts with *, checked
mechanically. The ⛔ fence against reconstructing the URL consumer-side is kept
verbatim and is untouched.

Verification

At 1a5ba4fa4, the branch head:

  • The 43 gate families node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack
    derives for this change set: 42 green, 1 NOT MEASURED. The exception is
    scripts/check-test-completeness.mjs, which grades a saved turbo run test
    log and exits 3 with PREREQUISITE NOT MET when the family names it with no
    argument — its own header says to record it as NOT MEASURED locally. Three
    gates first returned PREREQUISITE NOT MET for unbuilt workspace packages
    (@objectstack/formula, @objectstack/lint, packages/spec/json-schema) and
    one for a missing @objectstack/client-react build; all four were rebuilt and
    re-run green, and none of those first readings is reported as a result.
  • pnpm lint (eslint . --no-inline-config, whole repo, no narrowing) — clean.
  • pnpm --filter @objectstack/docs run typecheck
    (fumadocs-mdx && next typegen && tsc --noEmit) — clean.
  • node scripts/check-section-landing-index.mjs green, and by construction: that
    gate already filters 'index' out of the pages array it reads, so the
    landing-page index blocks are unaffected.

Every exit code was captured before any pipe.

Not in scope

The inbound-links work from the sibling card of the same sweep is untouched here.


Generated by Claude Code


Generated by Claude Code

…folder index (#12352)
Fumadocs attaches a folder's index.mdx as that folder's tree `index` node only
when the folder's meta.json does NOT list "index" in `pages`. Listing it makes
the page an ordinary child and leaves the folder node with a name and no url,
which degrades both surfaces built from that node: `getBreadcrumbItems()` drops
the un-linkable ancestor, and the sidebar renders an inert `SidebarFolderTrigger`
instead of a `SidebarFolderLink`.
Removes "index" from 16 of the 17 meta.json files that listed it.
content/docs/releases/ is fenced by AGENTS.md and is left untouched.
Measured on the real fumadocs-core 16.14.4 loader over content/docs:
short breadcrumb trails 172 -> 8 (the 8 are all under /docs/releases), and the
sidebar delta is exactly 16 folder headers TRIGGER -> LINK plus 16 index
children leaving the child list, with no other entry moved.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
@github-actionsgithub-actionsBot added size/s documentation Improvements or additions to documentation tooling labels Aug 31, 2026
@os-project-manager
os-project-manager marked this pull request as ready for review August 31, 2026 08:33
@os-project-manager
os-project-manager added this pull request to the merge queueAug 31, 2026
Merged via the queue into main with commit 787d757Aug 31, 2026
37 of 38 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-12352-meta-index-breadcrumb branch August 31, 2026 08:51
os-project-manager added a commit that referenced this pull request Aug 31, 2026
…13711)
Fumadocs attaches a folder's index.mdx as that folder's tree `index` node only
when the folder's meta.json does NOT list "index" in `pages`. Listing it makes
the landing page an ordinary child and leaves the folder node with a name and
no url. In fumadocs-core 16.14.4 `buildFolder()`:
if (indexPath) {
if (excludedPaths.has(indexPath)) delete node.index; // "index" was listed
else excludedPaths.add(indexPath);
}
`getBreadcrumbItems()` still emits that ancestor, but with `url: undefined`, and
`docsTrail()` in apps/docs drops any crumb without a url (Google requires `item`
on every BreadcrumbList entry but the last). The sidebar reads the same node as
`node.index ? SidebarFolderLink : SidebarFolderTrigger`.
Measured on the real loader over content/docs (405 pages), removing this one
line and changing nothing else:
- short breadcrumb trails 8 -> 0; the 8 fixed are exactly the non-landing
pages under /docs/releases (v9, v12..v17, implementation-status)
- the Releases folder header goes TRIGGER -> LINK, indexUrl /docs/releases
- /docs/releases stays in getPages() and source.getPage(['releases']) still
resolves -- the landing page is not unlisted, it becomes the section link
- 405 pages before and after; exactly one sidebar folder header flips and
only the 9 /docs/releases trails change
This was the 17th and last meta.json still listing it; the other 16 were fixed
in #13710, which left this one alone because content/docs/releases/ is fenced
by AGENTS.md. This is the dedicated docs-only PR that fence names as the route.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
akarma-synetal pushed a commit to akarma-synetal/framework that referenced this pull request Sep 1, 2026
…bjectstack-ai#13711) (objectstack-ai#13946)
Fumadocs attaches a folder's index.mdx as that folder's tree `index` node only
when the folder's meta.json does NOT list "index" in `pages`. Listing it makes
the landing page an ordinary child and leaves the folder node with a name and
no url. In fumadocs-core 16.14.4 `buildFolder()`:
if (indexPath) {
if (excludedPaths.has(indexPath)) delete node.index; // "index" was listed
else excludedPaths.add(indexPath);
}
`getBreadcrumbItems()` still emits that ancestor, but with `url: undefined`, and
`docsTrail()` in apps/docs drops any crumb without a url (Google requires `item`
on every BreadcrumbList entry but the last). The sidebar reads the same node as
`node.index ? SidebarFolderLink : SidebarFolderTrigger`.
Measured on the real loader over content/docs (405 pages), removing this one
line and changing nothing else:
- short breadcrumb trails 8 -> 0; the 8 fixed are exactly the non-landing
pages under /docs/releases (v9, v12..v17, implementation-status)
- the Releases folder header goes TRIGGER -> LINK, indexUrl /docs/releases
- /docs/releases stays in getPages() and source.getPage(['releases']) still
resolves -- the landing page is not unlisted, it becomes the section link
- 405 pages before and after; exactly one sidebar folder header flips and
only the 9 /docs/releases trails change
This was the 17th and last meta.json still listing it; the other 16 were fixed
in objectstack-ai#13710, which left this one alone because content/docs/releases/ is fenced
by AGENTS.md. This is the dedicated docs-only PR that fence names as the route.
Claude-Session: https://claude.ai/code/session_01Pk26oZ12t5N1hwGW1m1MgC
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/stooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-project-manager@claude