Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions AI_AGENT_DEVELOPER_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,68 @@ When working on this project:

---

## Production Configuration for Agent Readiness

### Content Negotiation (text/markdown)

Every page — including the homepage — generates a markdown version at `{path}index.html.md`
(e.g. `/develop/index.html.md`). The `<link rel="alternate" type="text/markdown">` element is
already present in every page's `<head>` (generated by Hugo's `AlternativeOutputFormats` loop
in `baseof.html`).

To enable HTTP-level content negotiation so `Accept: text/markdown` returns the markdown file
with `Content-Type: text/markdown`, configure nginx as follows:

```nginx
# Honour q-values: match only when text/markdown appears without q=0.
# This is still a heuristic — full RFC 7231 q-value parsing requires Lua
# or a compiled module. For most clients this is sufficient.
map $http_accept $use_markdown {
"~*text/markdown(?!;q=0)" 1;
default 0;
}

server {
# Declare the .md extension → MIME mapping so the type is set from the
# types table, not via add_header (which can't override Content-Type on
# files nginx serves directly).
types {
text/markdown md;
}
default_type application/octet-stream;

# .html.md files served directly: MIME comes from the types block above.
# add_header always ensures Vary is sent on error responses too.
location ~ \.html\.md$ {
add_header Vary "Accept" always;
}

location / {
add_header Vary "Accept" always;

# Content negotiation: rewrite to .html.md when Accept: text/markdown.
# The first rewrite handles trailing-slash paths (/develop/).
# The second handles slash-terminated paths without a dot segment.
# Paths containing a dot (e.g. index.html, version dirs like 7.4/)
# are intentionally left to try_files so they resolve normally.
if ($use_markdown) {
rewrite ^(.*/)$ $1index.html.md last;
rewrite ^(/[^.]*[^/])$ $1/index.html.md last;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Trailing-slash rewrite ignores dot exclusion

Medium Severity

The guide states paths with a dot (e.g. version segments like 7.4/) skip negotiation and use try_files, but the first rewrite ^(.*/)$ runs for every trailing-slash URL when Accept selects markdown. Those dotted paths still rewrite to index.html.md, which can 404 or return markdown where normal HTML index resolution was intended.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 951b34e. Configure here.

}

try_files $uri $uri/ =404;
}
}
```

**`hugo serve` note:** The `[server]` config in `config.toml` sets `Vary: Accept` and the
correct `Content-Type` for `.html.md` files during local development, but it does **not**
perform Accept-based rewriting. Requesting a page with `Accept: text/markdown` against
`hugo serve` still returns HTML. Full content negotiation requires the nginx config above
(or equivalent CDN rules) in production.

---

**Last Updated**: 2026-01-08
**Purpose**: Help AI agents discover and understand Redis documentation architecture

18 changes: 17 additions & 1 deletion config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -123,5 +123,21 @@ rdi_current_version = "1.18.1"

# Comment out if you don't want the "print entire section" link enabled.
[outputs]
home = ["HTML", "RSS", "Markdown", "JSON"]
section = ["HTML", "RSS", "Markdown", "JSON"]
page = ["HTML", "Markdown", "JSON"]
page = ["HTML", "Markdown", "JSON"]

# Content negotiation headers (hugo serve only).
# For production nginx config see AI_AGENT_DEVELOPER_GUIDE.md.
# Must be at end of file — TOML [table] sections apply to all following keys.
[server]
[[server.headers]]
for = "/**"
[server.headers.values]
Vary = "Accept"

[[server.headers]]
for = "**.html.md"
[server.headers.values]
Content-Type = "text/markdown; charset=utf-8"
Vary = "Accept"
Loading