Testing of the @doc-json output

Fix odoc documentation warnings across day10, js_top_worker, and extensions

Escape curly braces and fix odoc reference syntax to eliminate warnings
with (warnings fatal):
- Replace {dir}, {pkg}, {hash} etc with [dir], [<pkg>], [<hash>] or
{[ code blocks ]} as appropriate
- Fix {!Stdlib.Out_channel.with_open_gen} -> [Out_channel.with_open_gen]
in sherlodoc type_polarity.mli
- Fix @scrolly.<theme> -> [@scrolly.<theme>] in scrollycode extension
- Fix JSON format in findlibish.ml doc comment

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

+45 -37
+2 -2
day10/bin/combine_docs.ml
··· 85 85 end 86 86 87 87 (** Extract universe hash from html_path. 88 - Path format: .../prep/universes/{universe}/{pkg}/{version}/html *) 88 + Path format: [.../prep/universes/<universe>/<pkg>/<version>/html] *) 89 89 let extract_universe html_path = 90 90 let parts = String.split_on_char '/' html_path in 91 91 let rec find_after_universes = function ··· 120 120 None 121 121 with _ -> None 122 122 123 - (** Check if a directory name is a doc layer (doc-{hash}, but not doc-driver- or doc-odoc-) *) 123 + (** Check if a directory name is a doc layer ([doc-<hash>], but not [doc-driver-] or [doc-odoc-]) *) 124 124 let is_doc_layer_dir name = 125 125 let len = String.length name in 126 126 len > 4 && String.sub name 0 4 = "doc-"
+1 -1
day10/bin/jtw_tools.ml
··· 3 3 Per OCaml version: installs js_of_ocaml and js_top_worker (pinned from 4 4 a git repo), builds worker.js, and extracts stdlib cmis + dynamic_cmis.json. 5 5 6 - Cached as jtw-tools-{hash}/ per OCaml version + repo + branch. *) 6 + Cached as [jtw-tools-<hash>/] per OCaml version + repo + branch. *) 7 7 8 8 (** Compute hash for the jtw-tools layer. 9 9 Depends on OCaml version, repo, and branch. *)
+1 -1
day10/bin/linux.ml
··· 396 396 (** Run odoc_driver_voodoo in a container. 397 397 398 398 This runs odoc_driver_voodoo which: 399 - - Finds packages in prep/universes/{u}/{pkg}/{v}/ 399 + - Finds packages in [prep/universes/<u>/<pkg>/<v>/] 400 400 - Compiles .cmti/.cmt to .odoc with marker files 401 401 - Links and generates HTML 402 402 - Uses marker files to find dependencies
+7 -5
day10/bin/odoc_gen.ml
··· 5 5 compile/link/html steps internally. 6 6 7 7 Per-package prep structure (deleted after odoc compile): 8 - layer/prep/universes/{u}/{p}/{v}/ 9 - lib/ # .cmti/.cmt/.ml/.mli files (copied from opam lib) 10 - doc/ # .mld files (copied from opam doc) 8 + [layer/prep/universes/<u>/<p>/<v>/] 9 + [lib/] - .cmti/.cmt/.ml/.mli files (copied from opam lib) 10 + [doc/] - .mld files (copied from opam doc) 11 11 12 12 Accumulated odoc output (persists in layer overlay): 13 13 /home/opam/compile/ # .odoc files, visible to downstream packages 14 14 15 15 HTML output (bind-mounted shared directory): 16 - /html/p/{pkg}/{version}/ # blessed packages 17 - /html/u/{universe}/{pkg}/{version}/ # non-blessed packages *) 16 + {[ 17 + /html/p/<pkg>/<version>/ (blessed packages) 18 + /html/u/<universe>/<pkg>/<version>/ (non-blessed packages) 19 + ]} *) 18 20 19 21 type doc_result = 20 22 | Doc_success of { html_path : string; blessed : bool }
+2 -2
day10/bin/os.ml
··· 529 529 530 530 This module provides atomic swap operations for documentation directories, 531 531 implementing the "fresh docs with graceful degradation" pattern: 532 - - Write new docs to a staging directory ({dir}.new) 533 - - On success, atomically swap: old -> .old, new -> current, remove .old 532 + - Write new docs to a staging directory ([dir.new]) 533 + - On success, atomically swap: old -> [.old], new -> current, remove [.old] 534 534 - On failure, leave original docs untouched 535 535 536 536 Recovery: On startup, clean up any stale .new or .old directories left
+2 -2
day10/bin/sync_docs.ml
··· 11 11 } 12 12 13 13 (** Extract universe hash from html_path. 14 - Path format: .../prep/universes/{universe}/{pkg}/{version}/html *) 14 + Path format: [.../prep/universes/<universe>/<pkg>/<version>/html] *) 15 15 let extract_universe html_path = 16 16 let parts = String.split_on_char '/' html_path in 17 17 let rec find_after_universes = function ··· 42 42 with 43 43 | _ -> None 44 44 45 - (** Check if a directory name is a doc layer (doc-{hash}, but not doc-driver- or doc-odoc-) *) 45 + (** Check if a directory name is a doc layer ([doc-<hash>], but not [doc-driver-] or [doc-odoc-]) *) 46 46 let is_doc_layer_dir name = 47 47 let len = String.length name in 48 48 len > 4 && String.sub name 0 4 = "doc-"
+1 -1
day10/bin/util.ml
··· 41 41 in 42 42 Yojson.Safe.to_file name (`Assoc fields) 43 43 44 - (** Ensure a symlink exists from packages/{pkg_str}/{layer_name} -> ../../{layer_name} 44 + (** Ensure a symlink exists from [packages/<pkg_str>/<layer_name>] to [../../<layer_name>]. 45 45 This enables tracking all builds/docs for a package.version. *) 46 46 let ensure_package_layer_symlink ~cache_dir ~os_key ~pkg_str ~layer_name = 47 47 let pkg_dir = Path.(cache_dir / os_key / "packages" / pkg_str) in
+5 -5
day10/lib/atomic_swap.ml
··· 2 2 3 3 This module provides atomic swap operations for documentation directories, 4 4 implementing the "fresh docs with graceful degradation" pattern: 5 - - Write new docs to a staging directory ({dir}.new) 6 - - On success, atomically swap: old -> .old, new -> current, remove .old 5 + - Write new docs to a staging directory ([dir.new]) 6 + - On success, atomically swap: old -> [.old], new -> current, remove [.old] 7 7 - On failure, leave original docs intact 8 8 9 9 This is a standalone module that can be used for testing without ··· 89 89 90 90 (** Get paths for atomic swap operations. 91 91 Returns (staging_dir, final_dir, old_dir) where: 92 - - staging_dir: {version}.new - where new docs are written 93 - - final_dir: {version} - the live docs location 94 - - old_dir: {version}.old - backup during swap *) 92 + - staging_dir: [<version>.new] - where new docs are written 93 + - final_dir: [<version>] - the live docs location 94 + - old_dir: [<version>.old] - backup during swap *) 95 95 let get_swap_paths ~html_dir ~pkg ~version ~blessed ~universe = 96 96 let base_dir = 97 97 if blessed then
+5 -3
day10/lib/build_lock.ml
··· 4 4 providing reliable "in progress" detection without stale state. 5 5 6 6 Lock file naming: 7 - locks/build-{package}.{version}-{universe}.lock 8 - locks/doc-{package}.{version}-{universe}.lock 9 - locks/tool-{name}.lock or locks/tool-{name}-{ocaml_version}.lock 7 + {[ 8 + locks/build-<package>.<version>-<universe>.lock 9 + locks/doc-<package>.<version>-<universe>.lock 10 + locks/tool-<name>.lock or locks/tool-<name>-<ocaml_version>.lock 11 + ]} 10 12 11 13 This module provides query functions for the web UI. The actual lock 12 14 acquisition is done by Os.create_directory_exclusively in bin/os.ml. *)
+6 -4
day10/lib/run_log.ml
··· 1 1 (** Run logging for batch processing. 2 2 3 3 Manages timestamp-based run directories with structured logs: 4 - - runs/{id}/summary.json 5 - - runs/{id}/build/{package}.log 6 - - runs/{id}/docs/{package}.log 7 - - latest -> runs/{id} (symlink) 4 + {[ 5 + runs/<id>/summary.json 6 + runs/<id>/build/<package>.log 7 + runs/<id>/docs/<package>.log 8 + latest -> runs/<id> (symlink) 9 + ]} 8 10 *) 9 11 10 12 (** Run metadata *)
+8 -6
day10/web/data/layer_data.ml
··· 1 1 (** Read layer info for packages from day10's cache directory. 2 - Uses the packages/{pkg}/ directory structure with symlinks: 3 - - build-{hash} -> ../../build-{hash} (all builds) 4 - - doc-{hash} -> ../../doc-{hash} (all docs) 5 - - blessed-build -> ../../build-{hash} (canonical build if blessed) 6 - - blessed-docs -> ../../doc-{hash} (canonical docs if blessed) 7 - Falls back to scanning build-* directories if no symlinks exist. *) 2 + Uses the [packages/<pkg>/] directory structure with symlinks: 3 + {[ 4 + build-<hash> -> ../../build-<hash> (all builds) 5 + doc-<hash> -> ../../doc-<hash> (all docs) 6 + blessed-build -> ../../build-<hash> (canonical build if blessed) 7 + blessed-docs -> ../../doc-<hash> (canonical docs if blessed) 8 + ]} 9 + Falls back to scanning [build-*] directories if no symlinks exist. *) 8 10 9 11 type layer_info = { 10 12 package: string;
+1 -1
day10/web/views/live_log.ml
··· 23 23 else 24 24 None 25 25 26 - (** Try to find a completed layer log by looking in packages/{pkg_str}/ for layer symlinks *) 26 + (** Try to find a completed layer log by looking in [packages/<pkg_str>/] for layer symlinks *) 27 27 let find_completed_layer_log ~cache_dir ~platform ~stage ~pkg_str = 28 28 let pkg_dir = Filename.concat (Filename.concat (Filename.concat cache_dir platform) "packages") pkg_str in 29 29 if not (Sys.file_exists pkg_dir) then None
+1 -1
js_top_worker/lib/findlibish.ml
··· 128 128 (** Parse a findlib_index file (JSON or legacy text format) and return 129 129 the list of META file paths and universe paths. 130 130 131 - JSON format: {"meta_files": ["path/to/META", ...], "universes": ["universe1", ...]} 131 + JSON format: [{"meta_files": [...], "universes": [...]}] 132 132 133 133 meta_files: direct paths to META files 134 134 universes: paths to other universes (directories containing findlib_index) *)
+1 -1
odoc-scrollycode-extension/src/scrollycode_extension.ml
··· 5 5 - dark: Cinematic terminal aesthetic (JetBrains Mono + Outfit) 6 6 - notebook: Clean editorial aesthetic (Newsreader + DM Sans) 7 7 8 - Authoring format uses @scrolly.<theme> custom tags with an ordered 8 + Authoring format uses [@scrolly.<theme>] custom tags with an ordered 9 9 list inside, where each list item is a tutorial step containing 10 10 a bold title, prose paragraphs, and a code block. *) 11 11
+2 -2
odoc/sherlodoc/db/type_polarity.mli
··· 23 23 24 24 How is polarity computed exactly ? When you have [t -> u], the polarity of [t] 25 25 is inversed, and the polarity of [u] stays the same. A good example of this is 26 - the type of {!Stdlib.Out_channel.with_open_gen} : 26 + the type of [Out_channel.with_open_gen] : 27 27 28 28 {[ 29 29 val with_open_gen : open_flag list -> int -> string -> (t -> 'a) -> 'a ··· 32 32 Here the polarities are [-open_flag list], [-int], [-string], [+Out_channel.t], 33 33 [-'a] and [+'a]. The fact that we have [+Out_channel.t] might be puzzling at 34 34 first, because an [Out_channel.t] is not returned by the function, but 35 - {!Stdlib.Out_channel.with_open_gen} is indeed one of the possible ways to create 35 + [Out_channel.with_open_gen] is indeed one of the possible ways to create 36 36 an [Out_channel.t]. 37 37 38 38 There is however a complication. If the user queries for [int -> int -> string],