Skip to content

feat(docs): add a bilingual documentation site and its deploy pipeline - #507

Merged
LivXue merged 25 commits into
mainfrom
docs/docs_site_scaffold
Sep 19, 2026
Merged

LivXue merged 25 commits into
mainfrom
docs/docs_site_scaffold

Conversation

@LivXue

@LivXue LivXue commented Sep 18, 2026 •

Copy link
Copy Markdown
Member

Summary

Stands up a bilingual MkDocs site under docs-site/, published to GitHub Pages
by a workflow that builds on every pull request and deploys only from main.
Fourteen topics ship in English and Chinese: the quick start, self-hosting,
Docker, the WebUI, the sandbox manual, the runtime architecture, the command
reference, the repository layout, the developer workflow, the proactivity design
and reference pair, the self-evolution map and the tracing API.

This pull request is additive. Both READMEs keep their prose and gain one link
to the site in the header row; trimming them is deliberately a second pull
request, so the site can be confirmed live before anything depends on it.

Key decisions:

  • docs-site/, not docs/. docs/README.md defines docs/ as design
    notes and dated records, and docs/plans/ is an archive explicitly not held
    to today's layout. A user manual has the opposite lifecycle.
  • Full bilingual parity, via the mkdocs-static-i18n suffix structure
    (page.md beside page.zh.md). Parity decays immediately without a guard, so
    a test fails when a page has no twin.
  • Anchors are pinned on the Chinese side. A CJK heading slugifies to
    nothing, so Markdown numbers it _1, _2, and inserting one heading
    renumbers every anchor below it, silently breaking links readers already hold.
    Each Chinese heading therefore states the anchor its English twin gets for
    free: 133 heading anchors, identical across the two languages, none
    positional.
  • A left rail, not a top bar. The header carries the brand, the search box
    and the navigation tree down the left; the table of contents on the right is
    one continuous polyline clipped to the headings currently on screen.
  • No template overrides. Material customises through theme.custom_dir with
    Jinja partials, those partials are .html, and scripts/check_large_files.py
    holds .html in BLOCKED_ASSET_EXTENSIONS. The skin is therefore one
    stylesheet remapping Material's own custom properties, plus one script for the
    table-of-contents rail. The brand mark is a data: URI for the same reason.
  • An owner-signed docs-site/ exemption zone in AGENTS.md section 1.3. The
    i18n config carries the Chinese navigation labels, and mkdocs.yml is not
    *.md, so the blanket suffix exemption does not reach it. A contract test
    holds the AGENTS.md list and EXEMPT_PREFIXES equal, so both moved together.
  • The site never links out to a file it does not host. Documents that stay
    in the repository are named as plain paths rather than linked, and a guard
    fails a page that links to an external webpage or a repository-relative path.

Two defects found and closed here rather than after publication. The Chinese
site rendered an English sidebar: navigation labels live in mkdocs.yml, not in
any page, so comparing the two languages' Markdown finds nothing and only the
rendered output shows it. And a control could be present, sized, animated and
answer querySelector while being unclickable, because a popup Material sized
for a top bar landed outside the viewport once the header became a rail.

Type

  • Fix
  • Feature
  • Docs
  • CI / tooling
  • Refactor
  • Other

Verification

All commands run at the pushed head, rebased onto origin/main.

make check-large-files                                         exit 0
make check-source-language                                     exit 0
make docs-build                                                exit 0, 0 errors
  docs-site/site/index.html and docs-site/site/zh/index.html   both present
  mkdocs_static_i18n: Translated 17 navigation elements to zh
uv run pytest tests/test_docs_site.py tests/test_source_language_check.py \
  tests/test_readme_scope_canon.py tests/test_docker_runtime.py \
  tests/test_living_docs.py                                    44 passed
uv run pytest tests/integration/test_docs_site_controls_e2e.py 13 passed
pre-commit run --files <changed files>                         all hooks pass
git diff --check                                               clean

Anchor parity was measured on the built HTML rather than assumed: 14 page pairs,
133 heading anchors compared at h2 through h6, every one identical between the
two languages and none positional.

The controls are hit-tested rather than queried. document.elementFromPoint at
the exact point a reader clicks must return the control itself, which is the
only check that catches an off-viewport popup; the language switcher, the search
panel, the repository link and the table-of-contents rail each pass it.

The brand lockup is measured, not eyeballed. A failing test was written first
and reported the wordmark sitting 4.00px below the mark's centre line; it is
0.00px after the fix, and a 4x screenshot puts the residual optical offset at
0.25px.

Every guard this branch adds was confirmed to fail first. The brand-class guard
was mutated from both sides in turn: renaming .em-eyebrow in the stylesheet
turned it red, renaming class="em-standfirst" in index.md turned it red, and
it is green again with both restored. The anchor guard was shown to catch both
failure modes, a missing anchor and a wrong one.

  • Relevant tests pass locally
  • Relevant lint / type checks pass locally
  • User-facing docs or screenshots are updated when needed

Risk

No behaviour change to any shipped code path. Nothing imports docs-site/, the
new Makefile targets are additive, and the only change outside docs-site/ and
its tests is one link added to each README's header row.

Rollback is deleting the branch: the site does not exist until Pages is enabled
with Source: GitHub Actions, and every other change is inert without it.

Disclosed residue, checked and deliberately not changed:

  • The README header now links to https://evermind-ai.github.io/Raven/, which
    404s until Pages is enabled. It is the same URL the Documentation section
    already carries.
  • The deploy job is skipped on pull requests by design, so a green check here
    does not prove deployment works. The first real evidence is the deploy job's
    own conclusion on the main run after merge.
  • GitHub Pages is not enabled on this repository. Measured: GET /repos/.../pages
    returns 404. Under Deploy from a branch the uploaded artifact is silently
    ignored while the workflow still reports success, so the source must be set to
    GitHub Actions.
  • tests/integration/ is excluded by norecursedirs, so the browser suite is a
    local guard and never runs in CI.
  • The self-hosting page's Makefile target, docker path and port claims are
    unguarded. They were equally unguarded in the README they came from, and the
    owner ruled against adding a guard for them.
  • The self-evolution SOP and the benchmark contract stay in the repository and
    are named as plain paths from the pages that reference them. The SOP is a
    translation of an upstream project's internal document and carries redaction
    placeholders, which is a publication decision rather than a formatting one.
  • docs/plans/ still shows the pre-restyle mkdocs.yml. CONTEXT.md defines that
    directory as an archive of the tree as it stood, with an explicit rule against
    updating an old plan to match a later change.
  • The Chinese pages drop the brand's italic serif accent word. Cormorant Garamond
    carries no CJK glyphs, so it would fall back to another face without any sign.
  • The code font is Material's default Roboto Mono. The brand names four
    typefaces and no monospace, so none was invented for it.

Related Issues

N/A

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Blocking: remove or replace the stylesheet's untracked local-only source reference.

I found one blocking repository-rule violation, marked inline.

I reviewed the full github/main...7f9aa66a3b97 diff, including the commit added while this review was in progress; the relevant site/config callers and generated link behavior; commit history and action tag availability; AGENTS.md, CLAUDE.md, and CONTEXT routing; backward compatibility; test changes for weakening; and the documented two-stage Pages deployment architecture.

Verification at the latest head: the strict bilingual MkDocs build succeeded and generated both locales with seven translated navigation elements; 40 relevant pytest cases passed; the source-language and large-file gates passed against github/main...HEAD; and git diff --check passed. GNU make is unavailable in this environment, so I ran the exact uv commands behind the Makefile targets. The PR's docs build GitHub check is also green. I found no other blockers.

Comment thread docs-site/docs/stylesheets/evermind.css Resolved

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No blockers; this can merge as far as I am concerned.

The only delta from the previously reviewed head replaces the inaccessible source citation with tracked, accurate references, so the prior blocker is resolved; I replied on and resolved that thread. I reviewed the new commit in the context of the full diff, repository rules and context documents, surrounding site/build configuration, history, backward compatibility, unchanged test strength, and the two-stage deployment architecture. No new issue emerged.

Verification at 33d8d6198534: strict bilingual MkDocs build passed with both locale trees and seven translated navigation elements; 40 focused pytest cases passed; source-language and large-file checks passed against github/main...HEAD; the replacement spec path is tracked; and git diff --check passed.

LivXue and others added 24 commits September 19, 2026 05:54
The design moves self-hosting, the web UI launch, the command reference and
the repo layout out of both READMEs into a bilingual MkDocs site, leaving
Quick Start where it is. The spec records what moves, which four contracts
read the moved content, and which of them fails silently rather than loudly.

Co-authored-by: Claude (claude-opus-5[1m]) <noreply@anthropic.com>
Add a docs dependency group, an MkDocs Material config with English at the
root and Chinese at /zh/, and two Makefile targets. The build output is
gitignored because AGENTS.md section 7 forbids committing HTML.

Co-authored-by: Claude (claude-sonnet-5) <noreply@anthropic.com>
The previous commit set the i18n plugin's zh language name to the native
label, which scripts/check_source_language.py rejects: docs-site/ is not
one of the AGENTS.md 1.3 exemption zones and mkdocs.yml is not Markdown, so
the CJK label would fail CI's repository files job. Use the English label
instead; the switcher still renders both locales.

Adding docs-site/ as an exemption zone is the owner's call per AGENTS.md
1.3, not this branch's; if that lands later, the native label is a
one-line follow-up.

Co-authored-by: Claude (claude-sonnet-5) <noreply@anthropic.com>
The Global Constraints and all nine commit blocks named a literal trailer id.
AGENTS.md 3.3 requires the id of the model actually writing the commit, so a
literal copied from a plan is wrong for every task a different model runs, and
the repository's own convention already splits them: the plan-authoring commit
carries the driver's id, each delegated task carries the implementer's. Task 3
also picks up a bilingual anchor asymmetry the review found in the landing pages.

Co-authored-by: Claude (claude-opus-5[1m]) <noreply@anthropic.com>
Both defects are silent: a repo-relative link renders and 404s only on
click, and a one-language page is indistinguishable from a translation
behind the i18n fallback. Each guard was confirmed to fail on its defect
before this landed.

Co-authored-by: Claude (claude-haiku-4-5-20251001) <noreply@anthropic.com>
The Task 2 review built the site with each link planted and read the strict
build's exit code rather than reasoning about it. A relative link whose target
ends in .md aborts mkdocs build --strict with the file and the link named; one
that does not end in .md exits 0 and 404s only on a click. Four of the eight
links the migration must absolutise fall on each side, so the blanket claim was
wrong for half of them. The guard is still worth having, for reasons the section
now states instead of the one it had.

Co-authored-by: Claude (claude-opus-5[1m]) <noreply@anthropic.com>
Finding 1: Add guard to link test so it fails when no pages are found,
matching the parity test's protection. Proved the guard works by renaming
docs-site/docs away and confirming the test fails with the expected message.

Finding 2: Rewrite docstring so it states what is actually true. The guard
catches non-.md repository-relative links (e.g., docker/.env) that mkdocs
build --strict does NOT abort on; .md-suffixed links are already caught by
--strict itself. The guard also runs in the ordinary test suite without
needing the docs uv group.

Co-authored-by: Claude (claude-haiku-4-5-20251001) <noreply@anthropic.com>
The plan still carried the pre-review docstring and link test, so a re-run
would have reintroduced the claim the review falsified and the vacuous pass
it found. Both now mirror tests/test_docs_site.py byte for byte, and the
task's commit-message template states the scoped rationale rather than the
blanket one.

Co-authored-by: Claude (claude-opus-5[1m]) <noreply@anthropic.com>
The documentation site is bilingual by design, and its i18n config carries the
language-switcher display name. mkdocs.yml is not *.md, so the blanket suffix
exemption did not reach it and the gate refused the native label. The owner
signed the zone; the gate list, the AGENTS.md list and the pin table move
together because a contract test holds all three equal.

Co-authored-by: Claude (claude-sonnet-5) <noreply@anthropic.com>
Copied from both READMEs unchanged apart from three mechanical edits:
dropping any trailing Back-to-top block, demoting headings one level under
each page's new H1, and making the eight repository-relative links
absolute so they resolve from a published page rather than only from the
repository root. The nav and the two index pages gain their entries here
rather than in the scaffolding commit, because --strict rejects a nav
entry whose file does not exist.

Co-authored-by: Claude (claude-sonnet-5) <noreply@anthropic.com>
Co-authored-by: Claude (claude-haiku-4-5-20251001) <noreply@anthropic.com>
Update upload-pages-artifact to v5.0.0 and deploy-pages to v5.0.1 for
node20 runtime compatibility past 2026-09-23. Add Python 3.12 install
step to match repository CI standards. Give deploy job its own
non-cancellable concurrency group to prevent API wedges.

Co-authored-by: Claude (claude-haiku-4-5-20251001) <noreply@anthropic.com>
The plan specified actions/upload-pages-artifact@v3 and actions/deploy-pages@v4,
both of which declare the node20 runtime that GitHub removes from its runners on
2026-09-23. upload-pages-artifact runs in the build job, so past that date every
pull request's docs build would fail rather than only the deploy. The plan now
pins the v5 pair, carries the deploy job's own non-cancelling concurrency group,
and adds the Python pin that every other job in ci.yml already runs, with the
reasoning beside them so a re-run does not restore any of the three.

Co-authored-by: Claude (claude-opus-5[1m]) <noreply@anthropic.com>
The docs-serve and docs-build targets called uv without --frozen or
--python, the only Makefile uv run lines besides beta that did not.
A bare uv run resolves 3.13 on a developer box while the repository
and its CI use 3.12, so a local site build could diverge from the one
the workflow produces. The workflow itself is unaffected: it inlines
its own pinned command instead of calling these targets.

Co-authored-by: Claude (claude-opus-5[1m]) <noreply@anthropic.com>
The site now follows the shape taken from docs.newapi.pro: a top row of
section tabs, a grouped sidebar, and a landing page that is a directory
of linked cards rather than prose. Five pages do not justify seven tabs,
so the sections are three.

The brand skin remaps Material's own CSS custom properties onto the
EverMind token set, then resets the corners Material hardcodes, because
square geometry is the strongest identity cue in that system. It has to
work this way: overrides/*.html is how Material is normally customised,
and scripts/check_large_files.py blocks committing HTML.

This also closes a defect no page-level review could see. The navigation
labels live in mkdocs.yml, not in any page, so comparing the two
languages' Markdown found nothing while the Chinese site rendered an
English sidebar. nav_translations fixes it and the build now reports
seven translated navigation elements. The Chinese title drops the italic
serif accent word: Cormorant Garamond has no CJK glyphs, so it would
fall back to another face.

Co-authored-by: Claude (claude-opus-5[1m]) <noreply@anthropic.com>
The class names in the pages and the rules in evermind.css are a contract
with no build step between them: a rename on either side errors nowhere and
reddens nothing, and the only sign is an element rendering unstyled on a page
nobody happens to be looking at. The guard compares the two sets in both
directions, so it also catches a rule no page reaches.

Proved load-bearing by mutating each side in turn. Renaming .em-eyebrow in the
stylesheet and class="em-standfirst" in index.md each turned it red, and it is
green again with both restored.

Co-authored-by: Claude (claude-opus-5[1m]) <noreply@anthropic.com>
evermind.css imported Bungee Shade and held it in a token no rule read, so
every page load paid for a face that never reached a glyph. The brand reserves
it for a single large wordmark, and this site takes its layout from a reference
that has no hero to carry one. Re-add the import and the token together if a
wordmark band is ever wanted.

Co-authored-by: Claude (claude-opus-5[1m]) <noreply@anthropic.com>
The spec described the site's files and its deploy path and said nothing about
how it looks or navigates, so the sectioned layout, the card directory and the
brand skin had no design record at all.

The load-bearing entry is the constraint. Material customises through
theme.custom_dir with Jinja partials under overrides/, those partials are .html,
and check_large_files.py holds .html in BLOCKED_ASSET_EXTENSIONS and fails any
commit adding one outside bridge/, ui-web/ and ui-tui/. A maintainer reaching
for a template override meets a gate rather than a review comment, so the rule
belongs in the design document and not in the stylesheet's own header.

The section also records why navigation labels need nav_translations: they are
the one piece of reader-visible text living in mkdocs.yml, so comparing the two
languages' Markdown cannot detect their absence. The guard list gains the sixth
entry it was missing.

Co-authored-by: Claude (claude-opus-5[1m]) <noreply@anthropic.com>
mkdocs.yml configured pymdownx.emoji through !!python/name:, a tag PyYAML's safe
loader has no constructor for, so pre-commit's check-yaml failed the pull
request while mkdocs itself built clean.

The extension was there to render one arrow glyph in front of each card link.
The reference layout this site follows carries no icon on its cards at all, so
removing the extension and the glyphs closes the conflict at its source instead
of excluding the file from a repository-wide check or passing --unsafe to a
hook every other yaml file benefits from.

Co-authored-by: Claude (claude-opus-5[1m]) <noreply@anthropic.com>
The header pointed at .claude/skills/evermind-web-design/references/tokens.css
for the brand tokens. That path is gitignored and git ls-files returns nothing
under it, so no reviewer or maintainer can open it, and AGENTS.md section 1.1
forbids a source comment that references a locally-visible-only path.

The tokens this skin uses are declared in the file's own :root block, and the
Presentation section of docs/specs/2026-09-18-docs-site-design.md already
carries the CSS-only constraint and its rationale. The header names those two
instead. Both are tracked, checked with git ls-files --error-unmatch.

Co-authored-by: Claude (claude-opus-5[1m]) <noreply@anthropic.com>
The revision-date plugin reads the log of the file it is rendering, so the
deploy checkout needs full history rather than the default shallow clone.

Co-authored-by: Claude (claude-opus-5[1m]) <noreply@anthropic.com>
The header becomes a fixed left rail carrying the brand, the search box and
the navigation tree, and the table of contents gets one continuous polyline
clipped to the headings on screen. The mark is a data URI because section 7
keeps image files out of the repository.

The wordmark next to it shares the mark's centre line: both hang from the same
row top, so the wordmark's line box is the mark's box and the leading it drops
is added back below, which leaves the search box where it was.

Controls relocated out of the header keep the popups Material sized for a top
bar, and a popup that lands off-viewport still answers querySelector, so the
new integration file hit-tests each control at the point a reader clicks.

Co-authored-by: Claude (claude-opus-5[1m]) <noreply@anthropic.com>
Quick start, self-hosting, Docker, the WebUI, the command reference, the
runtime architecture, the repository layout, the sandbox manual, the developer
workflow, the proactivity pair, the self-evolution map and the tracing API now
live on the site, each with a Chinese twin.

A CJK heading slugifies to nothing, so Markdown numbers it positionally and an
inserted heading silently renumbers every anchor below it. Each Chinese
heading therefore pins the anchor its English twin gets for free, and a new
guard fails when one does not.

Documents that stay in the repository are named as plain paths rather than
linked, so the site never sends a reader out to a file it does not host.

Co-authored-by: Claude (claude-opus-5[1m]) <noreply@anthropic.com>
Each readme points at the language a reader of that readme wants.

Co-authored-by: Claude (claude-opus-5[1m]) <noreply@anthropic.com>
@LivXue
LivXue force-pushed the docs/docs_site_scaffold branch from 33d8d61 to 5887e91 Compare September 19, 2026 05:55

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Blocking: make the docs-site unit test importable in the standard unit environment.

I found one new blocker in the expanded revision, marked inline.

I reviewed the full github/main...5887e91b0a64 diff and the four post-review commits, including repository rules and context documents, the generated site and build/deploy callers, relevant history, backward compatibility, test strength and discovery, and the rail/browser architecture. The previously resolved stylesheet thread remains resolved.

Verification: the strict bilingual MkDocs build passed and translated 17 navigation elements; the source-language and large-file gates plus git diff --check passed; and 44 focused tests passed after the docs group had been installed. That last environment masks the blocker: an isolated reproduction matching the unit job (--all-extras --dev --no-group docs) aborts collection of tests/test_docs_site.py with ModuleNotFoundError: markdown. The browser suite collected 13 tests locally but all 13 skipped because this runner has no Chrome binary; the PR records a separate 13-pass run.

This is a round-five blocker on all three required grounds: this revision adds the import, the ordinary unit shard reaches it, and the shard cannot run past collection without a code or dependency correction. I found no other new blocker.

Comment thread tests/test_docs_site.py Resolved
The guard imports the slugify the build itself calls, and markdown reached the
venv only through the docs group. The unit job syncs --all-extras --dev without
it, so collection aborted there and the guard protected nothing: importing the
real function is the point, since a hand-rolled copy can drift from what mkdocs
actually does.

Declaring markdown as a dev dependency makes the module import in the unit job.
Proved by mutating an anchor under the job's own sync flags: the guard fails on
the mutation and passes with it restored.

Co-authored-by: Claude (claude-opus-5[1m]) <noreply@anthropic.com>

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No blockers; this can merge as far as I am concerned.

The dependency fix at 4a2c0f75e7c8 is correct: markdown is now a direct dev dependency, so the docs-site guard imports and runs in the ordinary unit environment without relying on a prior docs build. The exact isolated reproduction passes 5 tests; the 44-test focused set, strict bilingual MkDocs build, source-language gate, large-file gate, and git diff --check all pass. I replied to and resolved the settled unit-environment finding.

This round covered the revision delta and lock metadata, the standard CI unit caller, dependency/backward-compatibility impact, and whether test coverage was weakened; it was not. The prior full-diff review covered the repository rules and context files, docs/deploy callers and history, compatibility, and architectural constraints. I found no new plain error or blocker.

@ZuyiZhou ZuyiZhou left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Approving. Reviewed independently rather than taking the description at its word.

Verified locally at the pushed head:

  • make docs-build (--strict) builds clean; both site/index.html and site/zh/index.html are produced.
  • Anchor parity measured on the built HTML, not the Markdown: 14 page pairs, 133 h2-h6 anchors, every one identical across the two languages, and none of the positional _1 / _2 form the pinning exists to prevent.
  • The Chinese sidebar renders Chinese and the document carries lang="zh", so the navigation-label defect is actually closed.
  • 31 unit tests pass (test_docs_site.py, test_source_language_check.py), and make check-large-files / make check-source-language both exit 0.
  • Every raven <command> in the command reference exists in the real CLI, and the subcommands were checked against --help too: gateway status|reload|stop, agents new, plugin auth, mcp bridge, ops connection, and tracing compact (an action argument, correctly documented as one).
  • Section 7 is clean: no new file over 100 KiB, no .html or .svg committed, the brand mark is a 35-character data: URI.
  • The AGENTS.md zone and EXEMPT_PREFIXES moved together and the contract test holds them equal. Action versions match the other 18 workflows.

Three non-blocking notes, none worth another round:

  1. The docs-site/ exemption prefix is wider than the AGENTS.md entry describes. The stated need is the navigation labels in mkdocs.yml; the prefix also quietly exempts stylesheets/ and javascripts/. docs-site/mkdocs.yml would match the text exactly.
  2. Two guards in tests/test_docs_site.py disagree in their remediation advice: the relative-link test tells you to make the target an absolute github.com URL, and the external-link test then rejects exactly that. Both pass today because no page has a link of either kind, so it is only the message that misleads.
  3. CHROME = "/usr/bin/google-chrome" means the browser suite silently skips on macOS rather than running. Combined with norecursedirs excluding tests/integration/, the rail's coupling to Material's internal class names has no guard that runs anywhere by default.

One thing to know at merge time, already disclosed: the workflow-level cancel-in-progress: true outranks the deploy job's own cancel-in-progress: false, so two merges landing close together can cancel a deploy in flight. The following push redeploys, so it self-corrects.

Pages still needs Source: GitHub Actions before the README link resolves.

@LivXue
LivXue merged commit 3dd06f6 into main Sep 19, 2026
25 checks passed
@LivXue
LivXue deleted the docs/docs_site_scaffold branch September 19, 2026 07:06
LivXue added a commit that referenced this pull request Oct 12, 2026
#507)

## Summary

Stands up a bilingual MkDocs site under `docs-site/`, published to
GitHub Pages
by a workflow that builds on every pull request and deploys only from
`main`.
Fourteen topics ship in English and Chinese: the quick start,
self-hosting,
Docker, the WebUI, the sandbox manual, the runtime architecture, the
command
reference, the repository layout, the developer workflow, the
proactivity design
and reference pair, the self-evolution map and the tracing API.

This pull request is additive. Both READMEs keep their prose and gain
one link
to the site in the header row; trimming them is deliberately a second
pull
request, so the site can be confirmed live before anything depends on
it.

Key decisions:

- **`docs-site/`, not `docs/`.** `docs/README.md` defines `docs/` as
design
notes and dated records, and `docs/plans/` is an archive explicitly not
held
  to today's layout. A user manual has the opposite lifecycle.
- **Full bilingual parity**, via the `mkdocs-static-i18n` suffix
structure
(`page.md` beside `page.zh.md`). Parity decays immediately without a
guard, so
  a test fails when a page has no twin.
- **Anchors are pinned on the Chinese side.** A CJK heading slugifies to
  nothing, so Markdown numbers it `_1`, `_2`, and inserting one heading
renumbers every anchor below it, silently breaking links readers already
hold.
Each Chinese heading therefore states the anchor its English twin gets
for
  free: 133 heading anchors, identical across the two languages, none
  positional.
- **A left rail, not a top bar.** The header carries the brand, the
search box
and the navigation tree down the left; the table of contents on the
right is
  one continuous polyline clipped to the headings currently on screen.
- **No template overrides.** Material customises through
`theme.custom_dir` with
Jinja partials, those partials are `.html`, and
`scripts/check_large_files.py`
  holds `.html` in `BLOCKED_ASSET_EXTENSIONS`. The skin is therefore one
stylesheet remapping Material's own custom properties, plus one script
for the
table-of-contents rail. The brand mark is a `data:` URI for the same
reason.
- **An owner-signed `docs-site/` exemption zone** in AGENTS.md section
1.3. The
i18n config carries the Chinese navigation labels, and `mkdocs.yml` is
not
`*.md`, so the blanket suffix exemption does not reach it. A contract
test
holds the AGENTS.md list and `EXEMPT_PREFIXES` equal, so both moved
together.
- **The site never links out to a file it does not host.** Documents
that stay
in the repository are named as plain paths rather than linked, and a
guard
fails a page that links to an external webpage or a repository-relative
path.

Two defects found and closed here rather than after publication. The
Chinese
site rendered an English sidebar: navigation labels live in
`mkdocs.yml`, not in
any page, so comparing the two languages' Markdown finds nothing and
only the
rendered output shows it. And a control could be present, sized,
animated and
answer `querySelector` while being unclickable, because a popup Material
sized
for a top bar landed outside the viewport once the header became a rail.

## Type

- [ ] Fix
- [x] Feature
- [ ] Docs
- [ ] CI / tooling
- [ ] Refactor
- [ ] Other

## Verification

All commands run at the pushed head, rebased onto `origin/main`.

```
make check-large-files                                         exit 0
make check-source-language                                     exit 0
make docs-build                                                exit 0, 0 errors
  docs-site/site/index.html and docs-site/site/zh/index.html   both present
  mkdocs_static_i18n: Translated 17 navigation elements to zh
uv run pytest tests/test_docs_site.py tests/test_source_language_check.py \
  tests/test_readme_scope_canon.py tests/test_docker_runtime.py \
  tests/test_living_docs.py                                    44 passed
uv run pytest tests/integration/test_docs_site_controls_e2e.py 13 passed
pre-commit run --files <changed files>                         all hooks pass
git diff --check                                               clean
```

Anchor parity was measured on the built HTML rather than assumed: 14
page pairs,
133 heading anchors compared at h2 through h6, every one identical
between the
two languages and none positional.

The controls are hit-tested rather than queried.
`document.elementFromPoint` at
the exact point a reader clicks must return the control itself, which is
the
only check that catches an off-viewport popup; the language switcher,
the search
panel, the repository link and the table-of-contents rail each pass it.

The brand lockup is measured, not eyeballed. A failing test was written
first
and reported the wordmark sitting 4.00px below the mark's centre line;
it is
0.00px after the fix, and a 4x screenshot puts the residual optical
offset at
0.25px.

Every guard this branch adds was confirmed to fail first. The
brand-class guard
was mutated from both sides in turn: renaming `.em-eyebrow` in the
stylesheet
turned it red, renaming `class="em-standfirst"` in `index.md` turned it
red, and
it is green again with both restored. The anchor guard was shown to
catch both
failure modes, a missing anchor and a wrong one.

- [x] Relevant tests pass locally
- [x] Relevant lint / type checks pass locally
- [x] User-facing docs or screenshots are updated when needed

## Risk

No behaviour change to any shipped code path. Nothing imports
`docs-site/`, the
new Makefile targets are additive, and the only change outside
`docs-site/` and
its tests is one link added to each README's header row.

Rollback is deleting the branch: the site does not exist until Pages is
enabled
with `Source: GitHub Actions`, and every other change is inert without
it.

Disclosed residue, checked and deliberately not changed:

- The README header now links to `https://evermind-ai.github.io/Raven/`,
which
404s until Pages is enabled. It is the same URL the Documentation
section
  already carries.
- The `deploy` job is skipped on pull requests by design, so a green
check here
does not prove deployment works. The first real evidence is the `deploy`
job's
  own conclusion on the `main` run after merge.
- GitHub Pages is not enabled on this repository. Measured: `GET
/repos/.../pages`
returns 404. Under `Deploy from a branch` the uploaded artifact is
silently
ignored while the workflow still reports success, so the source must be
set to
  `GitHub Actions`.
- `tests/integration/` is excluded by `norecursedirs`, so the browser
suite is a
  local guard and never runs in CI.
- The self-hosting page's Makefile target, docker path and port claims
are
unguarded. They were equally unguarded in the README they came from, and
the
  owner ruled against adding a guard for them.
- The self-evolution SOP and the benchmark contract stay in the
repository and
are named as plain paths from the pages that reference them. The SOP is
a
translation of an upstream project's internal document and carries
redaction
placeholders, which is a publication decision rather than a formatting
one.
- `docs/plans/` still shows the pre-restyle `mkdocs.yml`. CONTEXT.md
defines that
directory as an archive of the tree as it stood, with an explicit rule
against
  updating an old plan to match a later change.
- The Chinese pages drop the brand's italic serif accent word. Cormorant
Garamond
carries no CJK glyphs, so it would fall back to another face without any
sign.
- The code font is Material's default Roboto Mono. The brand names four
  typefaces and no monospace, so none was invented for it.

## Related Issues

N/A

---------

Co-authored-by: Claude (claude-opus-5[1m]) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants