Skip to content

Commit 574d3ec

Browse files
committed
fix(docs): verify nested tabs and fresh-checkout CI setup
1 parent 6341d35 commit 574d3ec

13 files changed

Lines changed: 246 additions & 131 deletions

File tree

‎.gitattributes‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,7 @@ Dockerfile* text eol=lf
4141
*.ttf binary
4242
*.eot binary
4343
*.pdf binary
44+
45+
apps/docs/migration/baseline.json linguist-generated=true
46+
apps/docs/migration/report.json linguist-generated=true
47+
apps/docs/migration/verification/** linguist-generated=true

‎.github/workflows/checks.yml‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,9 @@ jobs:
681681
tar -xzf "$archive" -C "$RUNNER_TEMP" actionlint
682682
"$RUNNER_TEMP/actionlint" -color -shellcheck= -pyflakes=
683683
684+
- name: Generate documentation modules for import audits
685+
run: bun run --cwd apps/docs content:generate
686+
684687
# Every zero-argument `check:*` script, run concurrently. The list is derived in
685688
# scripts/run-audits.ts, which also writes the per-audit timing table to the job
686689
# summary and annotates failures. Audits needing a base ref stay separate below.

‎apps/docs/ARCHITECTURE.md‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Sim owns the content catalog, page rendering, navigation, search, and machine in
2929
4. Use ordinary Markdown and shared components. MDX imports must be named imports registered in `lib/content/compile.ts`. Unknown modules, components, metadata, duplicate routes/anchors, broken internal links, or missing local media fail compilation with source context.
3030
5. Run `bun run --cwd apps/docs content:generate`, inspect the affected page and Markdown, and run the applicable repository gates.
3131

32-
The compiler records source hashes, heading IDs, code-fence hashes and media references. Run `content:verify-migration` to enforce preservation against the frozen replacement baseline; ordinary authoring permits intentional prose and code updates. It serializes every tab and disclosure into meaningful Markdown, including workflow configuration and outputs. The introductory paragraph is hidden in HTML only when it exactly repeats the page description; machine-readable prose remains intact.
32+
The compiler records source hashes, heading IDs, code-fence hashes and media references. Run `content:verify-migration` to enforce preservation against the frozen replacement baseline and regenerate `migration/report.json`. Ordinary builds validate current content without loading the historical inventory or rewriting the migration report, allowing intentional prose and code updates. The compiler serializes every tab and disclosure into meaningful Markdown, including workflow configuration and outputs. The introductory paragraph is hidden in HTML only when it exactly repeats the page description; machine-readable prose remains intact.
3333

3434
## Extend the component library
3535

@@ -55,6 +55,8 @@ One catalog owns HTML, canonical metadata, heading citations, per-page Markdown,
5555

5656
Reading routes use `generateStaticParams` and reject unknown slugs. Compilation and highlighting happen before or during the production build, not in the browser. The compiler caches unchanged MDX and emits separate modules per page; it bounds aggregate catalog size. The shell receives only navigation and headings, while article content remains server rendered. Fonts are local Geist/Geist Mono assets; only the reading font is preloaded. Code and tables scroll within their own surfaces. Reduced-motion preferences suppress decorative transitions.
5757

58+
Markdown GET handlers explicitly use `dynamic = 'force-static'` because their complete responses depend only on the build-time catalog. `generateStaticParams` and `dynamicParams = false` restrict HTML and Markdown to known routes; unknown slugs return 404. Search and MCP remain request-time handlers. This follows the installed Next.js caching model; Cache Components is not enabled, and adopting it would require migrating these route settings together.
59+
5860
Metadata uses canonical URLs, descriptive titles, Open Graph/Twitter images, real Git modification dates when available, `TechArticle`/`BreadcrumbList` structured data, and a generated sitemap. JSON-LD is safely serialized. Robots permit public docs and machine-readable discovery. There is no special AI-ranking markup or indexing guarantee: clear HTML, preserved citations, useful text, crawlable links, and consistent public representations are the supported foundations.
5961

6062
The implementation was checked against the installed Next.js 16.4.1 docs and current official [project structure](https://nextjs.org/docs/app/getting-started/project-structure), [server/client boundaries](https://nextjs.org/docs/app/getting-started/server-and-client-components), [MDX](https://nextjs.org/docs/app/guides/mdx), and [metadata](https://nextjs.org/docs/app/getting-started/metadata-and-og-images) guidance. The monorepo's patched Next version is retained. Google's [AI search guidance](https://developers.google.com/search/docs/appearance/ai-features) remains the basis for crawlability; no separate AI-specific schema is required.
@@ -64,3 +66,5 @@ The implementation was checked against the installed Next.js 16.4.1 docs and cur
6466
Review the migration report first, then the library gallery and representative pages, then the HTTP/browser reports. Run a production build without embedding credentials to verify the local fallback. Production search index publication remains a separate deployment step and must pass its freshness check.
6567

6668
This change preserves the docs workspace, origin, deployment identity, product generators, and content paths. Reverting the docs replacement commit and its lockfile/workflow changes restores the prior implementation; it requires no database migration or product-data rollback. Keep the PR in draft until the visual and behavior evidence has been reviewed.
69+
70+
The requested move to `https://sim.ai/docs` requires a coordinated path-aware build and infrastructure cutover. See [the domain migration plan](migration/domain-move.md); the current build remains on `docs.sim.ai` until that work is verified.

‎apps/docs/MACHINE_ACCESS.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ After PostgreSQL reports ready, run from `apps/docs`:
9090
```sh
9191
DOCS_SEARCH_E2E_DATABASE_URL=postgresql://docs_test:docs_test@127.0.0.1:15432/docs_search_test \
9292
DOCS_SEARCH_E2E_REPORT_PATH=test-results/search-postgres/report.json \
93-
bun --no-env-file --conditions=react-server scripts/test-search-postgres-e2e.ts
93+
bun run test:search:postgres:e2e
9494
```
9595

9696
Then stop the container using the ID returned by `docker run`; `--rm` removes its temporary data. Successful vector generation and provider ranking still require separate verification with an embedding provider. The local suite makes no paid provider calls.

‎apps/docs/components/ui/tabs.tsx‎

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ export function Tabs({
7070
const [selected, setSelected] = useState(first)
7171
const value = controlledValue ?? selected
7272
const reveal = useEffectEvent((next: string) => {
73+
if (!panelIds.has(next)) return
7374
setSelected(next)
7475
onValueChange?.(next)
7576
})
@@ -89,6 +90,17 @@ export function Tabs({
8990
useEffect(() => {
9091
const element = root.current
9192
if (!element) return
93+
function revealPanel(target: HTMLElement) {
94+
let panel = target.closest<HTMLElement>('[data-docs-tab-value]')
95+
while (panel && element?.contains(panel)) {
96+
if (panel.closest('[data-docs-tabs-root]') === element) {
97+
const next = panel.dataset.docsTabValue
98+
if (next !== undefined) reveal(next)
99+
return
100+
}
101+
panel = panel.parentElement?.closest<HTMLElement>('[data-docs-tab-value]') ?? null
102+
}
103+
}
92104
function revealTarget() {
93105
let target: HTMLElement | null = null
94106
try {
@@ -97,15 +109,11 @@ export function Tabs({
97109
return
98110
}
99111
if (!target || !element?.contains(target)) return
100-
const panel = target.closest<HTMLElement>('[data-docs-tab-value]')
101-
const next = panel?.dataset.docsTabValue
102-
if (next) reveal(next)
112+
revealPanel(target)
103113
}
104114
function revealMatch(event: Event) {
105115
if (!(event.target instanceof HTMLElement)) return
106-
const panel = event.target.closest<HTMLElement>('[data-docs-tab-value]')
107-
const next = panel?.dataset.docsTabValue
108-
if (next) reveal(next)
116+
revealPanel(event.target)
109117
}
110118
if (persist && groupId) {
111119
try {
@@ -132,6 +140,7 @@ export function Tabs({
132140
aria-label={label}
133141
className={cn('not-prose my-6 min-w-0 scroll-mt-[var(--docs-sticky-offset)]', className)}
134142
{...props}
143+
data-docs-tabs-root=''
135144
>
136145
{items && (
137146
<TabsList aria-label={label}>

‎apps/docs/migration/VERIFICATION.md‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,16 @@ The full machine-readable accounting is in [report.json](report.json), with the
2929
| HTTP and real MCP client | 15 passed | [Interface report](verification/interfaces.json) |
3030
| Content negotiation | 7 passed | [Negotiation report](verification/negotiation.json) |
3131
| Native header merger | 16 passed | [Header report](verification/headers.json) |
32-
| Browser behavior and responsive layouts | 40 passed | [Browser report](verification/browser.json) |
32+
| Browser behavior and responsive layouts | 42 passed | [Browser report](verification/browser.json) |
3333
| Isolated PostgreSQL search and publication | 7 passed | [Database report](verification/search-postgres.json) |
3434
| Workflow SSR and hydrated inspector | Authored node text in initial HTML; inspector, Escape, focus restoration, and zero browser errors passed | [SSR report](verification/workflow-ssr.json), [interaction report](verification/workflow-inspector.json) |
3535

3636
The corpus suite found duplicate React Flow accessibility IDs on 15 pages and unusable authentication links on all 308 API pages. Both regressions were observed before their fixes and passed afterward. The retained failure-only reports are [duplicate IDs](verification/corpus-before-preview-fix.json) and [authentication links](verification/corpus-before-auth-fix.json).
3737

38+
A subsequent production-browser review reproduced nested API tabs selecting a media type as the outer response status, hiding the response panels. Both fragment navigation and `beforematch` failed before ownership-aware ancestor selection was added; the complete 42-check browser suite passes afterward. The [pre-fix report](verification/nested-tabs-before-fix.json) records both failures.
39+
40+
The first PR CI run exposed two setup differences from the local verification: the PostgreSQL package command omitted Bun's `react-server` condition, and import audits ran before the ignored MDX registry was generated. The package command now matches the documented local invocation, and CI generates documentation modules before auditing imports. The exact PostgreSQL package command passed all seven checks against a fresh disposable database. Normal content builds no longer load the historical migration inventory or rewrite its report; explicit migration verification still passes and reproduces the report byte-for-byte.
41+
3842
### Performance evidence
3943

4044
SSR-enabled dynamic boundaries keep the workflow renderer out of ordinary reading-page downloads. The introduction page's measured JavaScript fell from 628,703 to approximately 236,000 compressed bytes, a 62% reduction. All seven captured scenes have no document-level horizontal overflow. [Navigation timings and resource sizes](verification/performance.json) include the measurements and runtime context.

‎apps/docs/migration/domain-move.md‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Move documentation to sim.ai/docs
2+
3+
Status: planned, not deployed. The replacement continues to serve `docs.sim.ai`. Changing `NEXT_PUBLIC_DOCS_URL` alone is insufficient; routing, generated URLs, assets and search citations must move together.
4+
5+
## Current routing
6+
7+
Read-only checks on October 29, 2026 found that `https://sim.ai/docs/introduction` receives an AWS Application Load Balancer redirect to `https://www.sim.ai:443/docs/introduction`, which returns 404. The independent landing site runs on Astro/ECS; its README assigns path routing to `simstudioai/infra`. Documentation runs in the Vercel `sim-studio/docs` project, with `docs-sim-studio.vercel.app` available as a separate origin alias.
8+
9+
The requested canonical destination is the apex `https://sim.ai/docs`, so its routing rule must precede the existing apex-to-www redirect. Keep the docs application independently deployed. Next.js documents this arrangement through [multi-zones and external rewrites](https://nextjs.org/docs/app/guides/multi-zones). An ALB cannot directly use Vercel's public addresses as IP targets; route through a reverse-proxy-capable service or edge layer managed by infrastructure. See [AWS target restrictions](https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-target-groups.html).
10+
11+
## Implementation requirements
12+
13+
| Owner | Required change |
14+
| --- | --- |
15+
| Docs build | Build with `basePath: '/docs'`. This is compiled into browser bundles and requires a rebuild; `assetPrefix` alone does not mount an application under a path. Follow [Next basePath guidance](https://nextjs.org/docs/app/api-reference/config/next-config-js/basePath). |
16+
| Docs URL module | Centralize public path and absolute URL generation while retaining catalog identifiers such as `/introduction`. `new URL('/introduction', 'https://sim.ai/docs')` discards `/docs`. Internal Next links receive the base path automatically; native links, images and fetches need explicit handling. |
17+
| Public representations | Update canonical URLs, Open Graph, JSON-LD, sitemap, Markdown links, discovery JSON and llms manifests together. Preserve `.md`, `.mdx`, `llms.mdx`, redirects and `Vary: Accept`. |
18+
| Search | Rebuild local artifacts and update PostgreSQL citation URLs without reembedding unchanged text. Current freshness checks compare content hashes and chunk counts, so a domain-only change would otherwise retain old citations. Normalize public citation paths before catalog lookup. |
19+
| MCP and proxy | Serve `/docs/api/mcp` with the existing method, body, Origin and cache semantics. Verify proxy Host/Origin behavior. Route upstream requests to a stable alias separate from the redirected hostname to prevent loops; avoid forwarding platform session credentials. |
20+
| Infrastructure | Route apex `/docs` and `/docs/*` before apex redirection and platform authentication/indexing rules. Preserve docs headers, static asset paths, RSC query parameters and content negotiation. The root `sim.ai/robots.txt` must advertise the docs sitemap; `/docs/robots.txt` does not govern the parent origin. |
21+
| Existing consumers | Update `SIM_DOCS_URL`, landing links and the platform docs VFS base in `apps/sim/lib/mothership/docs/corpus.ts`. Keep existing VFS document identities and `.mdx` requests compatible. |
22+
23+
## Cutover and verification
24+
25+
1. Implement the path-aware build and verify it on a separate origin. Run the complete corpus, assets, browser, content-negotiation, search and official MCP-client suites under `/docs`.
26+
2. Configure the AWS path route to the verified Vercel origin. Repeat the checks through `https://sim.ai/docs`, including root redirects, unknown-path 404s, nested assets, query preservation and cache variants.
27+
3. Update search citations and outbound links, then permanently redirect `docs.sim.ai/:path*` to `https://sim.ai/docs/:path*` with status 308. Match the old hostname explicitly, preserve queries and methods, and test existing MCP clients. Do not redirect the upstream alias.
28+
4. Verify the apex robots sitemap entry, canonical URLs and old-host deep links. Keep the previous deployment and routing configuration available for rollback.
29+
30+
No domain, deployment, redirect or infrastructure settings were changed during this review. The path-aware build remains follow-up implementation, not an environment-variable-only switch.

0 commit comments

Comments
 (0)