@@ -181,8 +181,10 @@ the **WASM cutover** — on the easiest crate, before the harder error-reporting
181181 +serde), added ` README.md ` + ` .gitignore ` . Builds on ** stable** rustc 1.95
182182 (no nightly needed).
183183- [x] ** 1b.** Local validation green: ` cargo build ` , ` cargo test ` (104 unit + 4
184- doctests pass), ` cargo publish --dry-run ` clean. * (Gap: no GitHub Actions CI
185- workflow committed to the new repo yet — tests were run locally. Add one.)*
184+ doctests pass), ` cargo publish --dry-run ` clean. ** CI workflow added**
185+ (` .github/workflows/ci.yml ` , commit ` ee3780d ` ): stable Rust, test matrix
186+ Linux/macOS/** Windows** + a fmt/clippy(` -D warnings ` ) job. First run green on
187+ all 4 jobs — Windows build confirmed (first time this crate has built there).
186188- [x] ** 1c.** Published ` quarto-source-map 0.1.0 ` to crates.io (Carlos's personal
187189 account; ` cargo owner --add github:posit-dev:<team> ` deferred — weekend).
188190- [x] ** 1d.** q2 cutover (branch ` braid/bd-egcyeym9-source-map-extraction ` ):
@@ -214,33 +216,106 @@ engineering and is valuable even if the repo move slipped. Does **not** depend o
214216Phase 1. There is ** no façade** — ` quarto-error-reporting ` keeps its name and its
215217public surface (minus the moved catalog * data* ).
216218
217- - [ ] ** 2a (test first).** Pin the behaviour the carve-out must preserve, against
218- the * current* code:
219- - ` get_docs_url("Q-0-1") ` returns the quarto.org URL (with the q2 catalog
220- active);
221- - a new test asserting that with ** no** catalog installed, ` get_docs_url `
222- returns ` None ` (passes only after the registry exists — write it now,
223- expect red).
224- - [ ] ** 2b.** Introduce ` CatalogProvider ` + the ` OnceLock ` registry + delegating
225- free-functions in ` quarto-error-reporting ` ; keep ` ErrorCodeInfo ` here.
226- Repoint ` diagnostic.rs:290 ` at ` catalog() ` . Green.
227- - [ ] ** 2c.** Create ` quarto-error-catalog ` crate: move ` error_catalog.json ` + the
228- ` ERROR_CATALOG ` static + the ` QuartoCatalog ` provider + ` install() ` there.
229- Move the catalog-data tests with it. Wire ` install() ` into q2 binary/WASM/test
230- entry points.
231- - [ ] ** 2d.** Put ` json.rs ` behind a default-off ` json ` feature in
232- ` quarto-error-reporting ` ; have q2's dependents that use it enable
233- ` features = ["json"] ` . Confirm the 9 dependents + the 2 ` quarto-core ` catalog
234- callers + all ` json ` /` coalesce ` consumers compile ** unchanged** (only feature
235- flags and the ` ERROR_CATALOG ` import path may move).
236- - [ ] ** 2e.** Audit manifests: ` schemars ` becomes ` json ` -feature-only; drop
237- ` once_cell ` if the registry's ` OnceLock ` makes it unused; ` url ` stays.
238- - [ ] ** 2f.** ` cargo xtask verify ` green (touches ` quarto-core ` → hub-client/WASM;
239- do NOT ` --skip-hub-build ` ).
219+ - [x] ** 2a (test first).** Behaviour pinned: ` empty_catalog_returns_none `
220+ (direct, global-free) + ` installed_catalog_is_used_by_lookups ` (the single
221+ global-mutating test) in ` quarto-error-reporting ` ; the positive
222+ ` get_docs_url("Q-0-1") → quarto.org ` case relocated to ` quarto-error-catalog `
223+ integration tests (` install_makes_get_docs_url_resolve ` ,
224+ ` diagnostic_docs_url_resolves_after_install ` ).
225+ - [x] ** 2b.** ` quarto-error-reporting ` now catalog-agnostic: ` CatalogProvider `
226+ trait + ` EmptyCatalog ` + ` OnceLock ` registry + ` install_catalog ` in
227+ ` catalog.rs ` ; ` get_docs_url ` /` get_error_info ` /` get_subsystem ` keep their
228+ signatures but delegate to the installed provider (the ` &'static ` lifetime
229+ survives via ` catalog(): &'static dyn CatalogProvider ` ). ` ERROR_CATALOG `
230+ static + ` include_str! ` removed; ` lib.rs ` re-exports updated. ` diagnostic.rs `
231+ ` docs_url() ` unchanged (still calls the local delegating fn); its positive
232+ doctest/test relaxed/relocated.
233+ - [x] ** 2c.** New ` quarto-error-catalog ` crate: ` error_catalog.json ` (git-moved) +
234+ ` ERROR_CATALOG ` (Lazy) + ` QuartoCatalog: CatalogProvider ` + ` install() ` ; the
235+ example moved here; the 10 data-presence tests ported (direct map access).
236+ ` install() ` wired into the ` q2 ` binary ` main ` . ** WASM deliberately does
237+ NOT install** (see the 2f note): the WASM bridge never surfaces docs URLs
238+ (` JsonDiagnostic ` has no ` docs_url ` field), and installing would
239+ ` include_str! ` the 46 KB catalog into the bundle, pushing the WASM past
240+ hub-client's 35 MiB PWA precache limit. A legitimate "embedder installs
241+ nothing → EmptyCatalog" choice. The 2 ` quarto-core ` data-presence ` #[test] ` s
242+ now query
243+ ` quarto_error_catalog::ERROR_CATALOG ` directly (dev-dep added). ** Audit
244+ script + ~ 25 path references updated** to ` crates/quarto-error-catalog/… ` ;
245+ ` scripts/audit-error-codes.py ` passes (exit 0). Full workspace nextest:
246+ ** 10240 passed** .
247+ - [x] ** 2d.** ` json.rs ` now behind a ** default-off ` json ` feature** (` lib.rs `
248+ ` #[cfg(feature = "json")] ` on the module + re-export; ` tests/schema_drift.rs `
249+ gated with ` #![cfg(feature = "json")] ` ). Only ** 4** crates use the wire
250+ symbols (` quarto ` , ` quarto-core ` , ` quarto-preview ` , ` wasm-quarto-hub-client ` );
251+ each now declares ` features = ["json"] ` . ` to_json ` (uses ` serde_json::json! ` ,
252+ not the module) and ` coalesce.rs ` stay unconditional. Verified by `cargo
253+ tree` : ` schemars` **absent** by default, present with ` --features json`.
254+ - [x] ** 2e.** ` schemars ` made ` optional = true ` + `[ features] json =
255+ [ "dep: schemars "] ` . ` once_cell` **dropped** (registry uses ` std::sync::OnceLock`;
256+ confirmed unused via ` cargo tree ` ). ` url ` stays. Two clippy fixes in the new
257+ code (` map().unwrap_or ` → ` match ` ; needless doctest ` fn main ` ).
258+ - [x] ** 2f.** ` cargo xtask verify ` ** GREEN — all 14 steps** (incl. WASM build +
259+ hub-client tests). Two failures found + fixed en route: (1) two clippy lints
260+ in the new code (Step 1); (2) the WASM build (Step 7) broke hub-client's vite
261+ PWA step — wiring ` install() ` into the WASM bootstrap ` include_str! ` 'd the
262+ 46 KB catalog and forced it past the 35 MiB precache limit (` vite.config.ts `
263+ ` maximumFileSizeToCacheInBytes ` ). Fixed * soundly* (not by raising the limit):
264+ removed the WASM ` install() ` + ` quarto-error-catalog ` dep, since the WASM
265+ surfaces no docs URLs — pure dead weight there. WASM now 36,684,365 B
266+ (15.8 KB ** under** the limit). Workspace nextest ** 10240 passed** .
267+
268+ ** Phase 2 is COMPLETE.** ` quarto-error-reporting ` is now catalog-agnostic (a
269+ ` CatalogProvider ` seam + ` EmptyCatalog ` default, no embedded ` Q-* ` data); the q2
270+ policy lives in the new ` quarto-error-catalog ` ; ` json ` is a default-off feature.
271+ The crate is ready to carve out (Phase 3) once the Phase-1-style repo/publish
272+ machinery is pointed at it. Uncommitted on ` braid/bd-egcyeym9-error-reporting-split ` .
240273
241274> At the end of Phase 2, q2 still builds ` quarto-error-reporting ` from a path dep;
242275> it is now catalog-agnostic and cleanly carve-able.
243276
277+ ### Phase 2 — implementation notes (investigation 2026-06-27)
278+
279+ ** Blast-radius finding (much smaller than feared).** A full workspace audit shows
280+ the catalog is ** completely decoupled from the production render path** :
281+ - ` DiagnosticMessage ` does * not* consult the catalog to build its title/message
282+ (verified: no ` get_error_info ` /` message_template ` use in ` builder.rs ` /` diagnostic.rs ` ).
283+ - ` to_text ` (ariadne) and the JSON wire shape ** never call ` docs_url() ` ** ; ` json.rs `
284+ has no ` docs_url ` field. ** Zero ` .snap ` files contain a ` quarto.org/docs/errors `
285+ URL.** So the carve-out cannot change any rendered output or snapshot.
286+ - ` docs_url() ` has ** zero** consumers anywhere in the workspace. ` ErrorCodeInfo ` has
287+ no external users.
288+ - The * only* catalog uses outside ` quarto-error-reporting ` : two ` quarto-core `
289+ ` #[test] ` s (` project_resources.rs:1447 ` , ` theme_diagnostic.rs:271 ` ) asserting
290+ their codes are registered, plus the ` examples/with_error_code.rs ` example.
291+
292+ ** Consequence:** ` install() ` is ** behaviour-neutral today** (nothing reads the
293+ catalog in production); it matters only for the data-verification tests and for
294+ future features that surface docs URLs. This removes the test-fragility risk that
295+ would otherwise come from an uninitialised global.
296+
297+ ** Finalised design.**
298+ - * ` quarto-error-reporting ` (catalog-agnostic):* keep ` ErrorCodeInfo ` ; add
299+ ` CatalogProvider ` trait + ` OnceLock<Box<dyn CatalogProvider>> ` registry +
300+ ` install_catalog() ` + ` EmptyCatalog ` default. ` get_docs_url ` /` get_error_info ` /
301+ ` get_subsystem ` keep their ** exact signatures** (the ` &'static ` lifetime survives
302+ because ` catalog() ` returns ` &'static dyn CatalogProvider ` ) but delegate to the
303+ installed provider. ` diagnostic.rs:290 ` is unchanged (still calls the local
304+ ` get_docs_url ` ). Remove ` error_catalog.json ` + the ` ERROR_CATALOG ` static + the
305+ data tests + the example. Drop the ` ERROR_CATALOG ` re-export from ` lib.rs ` .
306+ - * ` quarto-error-catalog ` (new q2 crate):* ` error_catalog.json ` + the loader +
307+ ` QuartoCatalog: CatalogProvider ` + ` install() ` . Houses the moved data-presence
308+ tests, the moved doctests (e.g. ` get_subsystem("Q-0-1") == Some("internal") ` ),
309+ and the moved example. Deps: ` quarto-error-reporting ` , ` serde ` , ` serde_json ` ,
310+ ` once_cell ` .
311+ - * Test seams:* test providers/` EmptyCatalog ` are exercised ** directly** (no
312+ global) wherever possible; exactly one test asserts the global-empty default and
313+ one asserts global-install delegation — safe under nextest's process-per-test
314+ (and per-process doctests). The two ` quarto-core ` tests gain a
315+ ` quarto-error-catalog ` dev-dependency and call ` quarto_error_catalog::install() ` .
316+ - * Binaries/WASM:* wire ` quarto_error_catalog::install() ` into the ` q2 ` binary
317+ ` main ` and the WASM bootstrap (future-proofing; behaviour-neutral now).
318+
244319## Phase 3 — Extract ` quarto-error-reporting ` into a * separate* repo and cut q2 over
245320
246321Requires Phase 1 (source-map published) ** and** Phase 2 (carve-out done).
0 commit comments