Release v0.20.0: /docs/specs surface + nested flyout nav + session body-list removal

Wave 9 follow-up to roadmap item #30 (Session 0017.0 shipped #30 as v0.19.0;
v0.20.0 lands the operator-feedback follow-ups on top).

1. Specs on /docs/specs/<name> (backend docs_specs.py + frontend DocsSpec.jsx
   + DocsSpecsIndex.jsx). Configured via OHM_DOCS_SPECS; framework default
   carries OHM's two specs (rfc-app/SPEC.md + flotilla SPEC.md). Runtime
   fetch from gitea raw with 5-min TTL cache, mirroring docs_sessions.py.

2. Nested flyout nav hierarchy (DocsLayout.jsx). Sessions render as a tree
   with transcripts nested under each session row (labeled by .N ordinal).
   New Specs section between User Guide and Sessions.

3. /docs/sessions/<NNNN> body-list removed (DocsSessionIndex.jsx). Body
   becomes a session-overview card; navigation lives in the left nav.

19 new pytest cases for docs_specs (332 backend total green). Frontend
build clean. Sync frontend/package-lock.json version drift (0.15.0 → 0.20.0)
alongside the VERSION + package.json bump.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ben Stull
2026-05-28 10:48:31 -07:00
parent 69a166a6f2
commit e0d9ed7c5a
14 changed files with 1328 additions and 44 deletions
+19 -24
View File
@@ -1,14 +1,16 @@
// DocsSessionIndex.jsx — v0.19.0 / roadmap item #30.
// DocsSessionIndex.jsx — v0.20.0 (was v0.19.0 / roadmap item #30).
//
// Per-session index page at `/docs/sessions/:nnnn`. Lists every
// transcript published under the session's NNNN/ folder, linked to
// the per-transcript view.
// Per-session landing at `/docs/sessions/:nnnn`. v0.19.0 listed the
// transcripts as body links; v0.20.0 drops the body list — navigation
// is via the left flyout nav (which renders each session's transcripts
// nested under the session row). The body now serves as a
// session-overview card with the title, file count, and a hint to
// pick a transcript from the nav.
//
// The transcript list comes from `/api/docs/sessions/:nnnn/index`,
// which the framework derives via the gitea contents API (see
// backend/app/docs_sessions.fetch_session_index). We also read the
// session's `title` from the manifest fetch so the page header
// matches the flyout nav entry.
// The transcript count still comes from `/api/docs/sessions/:nnnn/index`
// so the empty-state ("no transcripts yet"), not-found, and error
// paths remain meaningful — the page still does something useful when
// the upstream is mid-publish or unreachable.
import { useEffect, useState, useCallback } from 'react'
import { Link, useParams } from 'react-router-dom'
@@ -105,21 +107,14 @@ export default function DocsSessionIndex() {
</div>
)}
{status === 'ok' && files.length > 0 && (
<ul className="docs-session-files">
{files.map(f => (
<li key={f}>
<Link
to={`/docs/sessions/${nnnn}/${f}`}
aria-label={`Open transcript ${f}`}
data-amp-track-name="Docs Session Transcript Open"
data-amp-track-session={nnnn}
data-amp-track-filename={f}
>
{f}
</Link>
</li>
))}
</ul>
<div className="docs-session-overview">
<p>
{files.length === 1
? '1 transcript in this session.'
: `${files.length} transcripts in this session.`}{' '}
Select one from the navigation on the left.
</p>
</div>
)}
</article>
)