Skip to content

Commit df02987

Browse files
cscheidclaude
andcommitted
docs(plan): YAML-stack extraction handoff + single-workspace-repo decision (bd-egcyeym9)
Adds claude-notes/plans/2026-06-29-yaml-stack-extraction-handoff.md — a self-contained plan for an agent to extract quarto-yaml + quarto-yaml-validation into one repo (posit-dev/quarto-yaml, a Rust workspace with two crates), following the now-completed source-map/error-reporting precedents. Also records the decision (foundation plan §1, YAML design-doc banner) that the YAML stack ships as a single two-crate workspace repo — unlike the foundation crates' one-repo-per-crate rule — because the parser and validator are tightly coupled and both Quarto-dialect-specific. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent ea820fc commit df02987

3 files changed

Lines changed: 236 additions & 5 deletions

File tree

claude-notes/plans/2026-06-26-extract-error-reporting-foundation.md

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -371,11 +371,17 @@ YAML stack (`quarto-yaml` + `quarto-yaml-validation`) per the sibling plan.
371371

372372
## Decisions
373373

374-
1. **Repo granularity — DECIDED (2026-06-27): two repos**, one per crate. The
375-
crates split naturally (leaf source-location utility vs. diagnostics host) and
376-
already have independent dependent sets (14 vs. 9); co-locating buys only weak
377-
CI cohesion and needlessly couples their release cadences. (The YAML stack is a
378-
third, separate repo in step 2.)
374+
1. **Repo granularity — DECIDED (2026-06-27): two repos**, one per *foundation*
375+
crate. The foundation crates split naturally (leaf source-location utility vs.
376+
diagnostics host) and already have independent dependent sets (14 vs. 9);
377+
co-locating buys only weak CI cohesion and needlessly couples their release
378+
cadences. **The YAML stack is different — DECIDED (2026-06-29): one repo,
379+
`posit-dev/quarto-yaml`, a Rust *workspace* with two crates** (`quarto-yaml` +
380+
`quarto-yaml-validation`). They are tightly coupled (validation depends on the
381+
parser) and both Quarto-dialect-specific, so a shared workspace fits. Both still
382+
publish to crates.io independently (leaf-first: `quarto-yaml`, then
383+
`quarto-yaml-validation`). Execution handoff:
384+
`claude-notes/plans/2026-06-29-yaml-stack-extraction-handoff.md`.
379385

380386
2. **Crate names — DECIDED (2026-06-27): keep current names.** Both externalized
381387
crates stay `quarto-source-map` and `quarto-error-reporting` (and `quarto-yaml`

claude-notes/plans/2026-06-26-extract-quarto-yaml-validation-design.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,15 @@
1818
> Q4, and Q5 below describe `error-reporting-core`/façade/json-relocation, defer to
1919
> the foundation plan. Step ordering is also foundation-first (source-map →
2020
> error-reporting → *then* this YAML work).
21+
>
22+
> **➡️ For execution, use the handoff note
23+
> `claude-notes/plans/2026-06-29-yaml-stack-extraction-handoff.md`** — it is the
24+
> up-to-date, self-contained plan: the YAML stack ships as **one repo
25+
> `posit-dev/quarto-yaml`, a workspace with two crates** (decided 2026-06-29; the
26+
> foundation crates' "one repo per crate" rule does *not* apply here), and the
27+
> foundation extraction it depends on is **done** (source-map + error-reporting
28+
> published, PRs #348/#349/#350). This design doc remains the rationale for the
29+
> error-code discipline applied to YAML.
2130
2231
This document moves the extraction from *current-state architecture* to
2332
*chosen design*. It resolves the seven open questions the research doc left
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
# Handoff: extract the YAML stack (`quarto-yaml` + `quarto-yaml-validation`)
2+
3+
**Strand:** bd-egcyeym9 (final phase of the diagnostics/YAML extraction epic)
4+
**Date:** 2026-06-29
5+
**Audience:** an agent picking this up cold. You should not need to read the
6+
session transcript — everything needed is here or in the linked docs.
7+
8+
---
9+
10+
## 1. Goal & motivation
11+
12+
Extract `quarto-yaml` and `quarto-yaml-validation` out of the q2 monorepo into a
13+
**single new repository `posit-dev/quarto-yaml`, structured as a Rust workspace
14+
with two crates**, and publish both to crates.io independently. The motivating
15+
need: **invisible internal Posit consumers of `quarto-yaml-validation`** want a
16+
standalone crate; and the error codes must follow the cross-package discipline.
17+
18+
This is the **last phase** of the epic. The diagnostics foundation is already
19+
done and merged:
20+
- `quarto-source-map 0.1.0``posit-dev/quarto-source-map` (crates.io) — PR #348.
21+
- `quarto-error-reporting 0.1.0``posit-dev/quarto-error-reporting` (crates.io,
22+
catalog-agnostic, `json` feature-gated) — PRs #349 (carve-out) + #350 (cutover).
23+
24+
## 2. Preconditions (verify before starting)
25+
26+
- **PR #350 merged** (q2 consumes `quarto-error-reporting 0.1.0`). `git switch
27+
main && git pull`. Confirm `crates/quarto-error-reporting/` is **gone** and the
28+
workspace builds.
29+
- Both foundation crates are live on crates.io at `0.1.0`.
30+
- You have (or the user grants) the same setup used in Phases 1/3: GitHub `gh`
31+
auth with `posit-dev` org rights (SSH key SSO-authorized), and the **user**
32+
performs every `cargo publish` (crates.io credentials are theirs; publishing is
33+
irreversible).
34+
35+
## 3. Read these first (the mechanics are already proven)
36+
37+
- **`claude-notes/plans/2026-06-26-extract-error-reporting-foundation.md`** — the
38+
authoritative playbook. Phase 1 (source-map) is the leaf-extraction template you
39+
will mirror almost exactly; Phase 3 (error-reporting) is the second run with the
40+
gotchas. Read the **Risks** and the per-step completion notes.
41+
- **`claude-notes/designs/cross-package-error-codes.md`** — the error-code
42+
discipline. Drives the one real design task (§6 below).
43+
- **`claude-notes/plans/2026-06-26-extract-quarto-yaml-validation-design.md`**
44+
the original YAML design doc. Note its top banner: parts about
45+
`error-reporting-core`/façade are superseded; the YAML-specific substance
46+
(origin codes, delete `validate-yaml`, the discipline application) still stands.
47+
48+
## 4. Repo structure (DECIDED: one two-crate workspace)
49+
50+
```
51+
posit-dev/quarto-yaml/ (new repo)
52+
Cargo.toml # [workspace] members=["crates/*"]; [workspace.package]; [workspace.dependencies]
53+
crates/
54+
quarto-yaml/ # the parser leaf
55+
quarto-yaml-validation/ # schema validation; depends on quarto-yaml
56+
LICENSE README.md .gitignore .gitattributes .github/workflows/ci.yml
57+
```
58+
59+
- Use a **workspace** (unlike the single-crate foundation repos). Per-crate
60+
`Cargo.toml`s inherit `version`/`edition`/`license`/`repository` from
61+
`[workspace.package]` and pull shared deps from `[workspace.dependencies]`.
62+
- **`.gitattributes` with `* text=auto eol=lf` from commit 1** — Phase 3's Windows
63+
CI caught a CRLF bug in committed JSON-vs-generated comparisons; start with LF
64+
enforced so you never hit it. (`quarto-yaml-validation` ships
65+
`test-fixtures/` YAML/JSON — same exposure.)
66+
- **CI** (`.github/workflows/ci.yml`): mirror the foundation repos — matrix
67+
Linux/macOS/**Windows** on **stable** Rust, `fmt` + `clippy --all-targets -D
68+
warnings`, `cargo test`. (Both crates need no nightly.) Watch for **stable-clippy
69+
lints the q2 pinned-nightly tolerates** — Phase 3 hit `items_after_test_module`
70+
in `macros.rs`; fix in the new repo (q2 deletes its copy at cutover, so the
71+
standalone becomes the single source — no divergence).
72+
73+
## 5. Dependency & cutover facts (measured 2026-06-29 on main)
74+
75+
**`quarto-yaml`** (the leaf): deps `yaml-rust2`, `serde`, `thiserror`,
76+
`quarto-source-map`. Its **only** quarto dep is the published source-map →
77+
becomes `quarto-source-map = "0.1.0"`. Does **not** depend on
78+
`quarto-error-reporting`. In-tree q2 consumers: **pampa, quarto-config,
79+
quarto-core, quarto-lsp-core** (+ `validate-yaml`, being deleted).
80+
81+
**`quarto-yaml-validation`**: deps `anyhow`, `thiserror`, `serde`, `serde_json`,
82+
`yaml-rust2`, `regex`, `quarto-yaml`, `quarto-source-map`,
83+
`quarto-error-reporting`. The quarto deps become: `quarto-yaml` (intra-workspace),
84+
`quarto-source-map = "0.1.0"`, `quarto-error-reporting = "0.1.0"`. **Only in-tree
85+
consumer is `validate-yaml`** (the demo binary). **After deleting `validate-yaml`,
86+
`quarto-yaml-validation` has ZERO q2 consumers** — q2 does not depend on it at all.
87+
88+
**Consequence for the q2 cutover:** q2 **deletes** `crates/quarto-yaml-validation`
89+
AND `crates/validate-yaml`, keeps consuming `quarto-yaml` (now published), and
90+
gains **no** dependency on the published `quarto-yaml-validation`. The latter is
91+
published purely for the external Posit consumers.
92+
93+
### The WASM gotcha (read carefully — different from Phase 1)
94+
95+
`wasm-quarto-hub-client` is an *excluded standalone workspace*. It does **not**
96+
directly depend on `quarto-yaml`; it gets it transitively via `pampa`/`quarto-core`
97+
(path-included). Those crates use `quarto-yaml = { workspace = true }`, which
98+
resolves against **q2's** `[workspace.dependencies]` (workspace inheritance follows
99+
the crate's filesystem home — q2 root — even inside the WASM build). So:
100+
- Convert the **path**-dep consumers (`pampa`, `quarto-config`) to
101+
`{ workspace = true }`; the `{ workspace = true }` ones (`quarto-core`,
102+
`quarto-lsp-core`) stay.
103+
- Set `[workspace.dependencies.quarto-yaml]``version = "0.1.0"`.
104+
- The WASM crate likely needs **no direct `quarto-yaml` dep** (it had a direct
105+
`quarto-source-map = "0.1.0"` only because it uses source-map directly). **Verify
106+
with the full `cargo xtask verify`** — the WASM build is the proof; if it can't
107+
resolve `quarto-yaml`, add a direct `quarto-yaml = "0.1.0"` to the wasm crate (as
108+
was needed for source-map).
109+
110+
## 6. The one design task: error codes (needs a user decision)
111+
112+
`quarto-yaml-validation/src/error.rs` `ValidationErrorKind::error_code()` currently
113+
returns **Quarto presentation codes** `Q-1-10`, `Q-1-11`, … These do **not** belong
114+
in a standalone library (they are q2's namespace, per the discipline). It has
115+
**no** dependency on an installed catalog (no `get_docs_url`/`install_catalog`
116+
refs), so the change is localized to `error_code()` + the ~15 `error.rs` tests that
117+
assert `"Q-1-x"`.
118+
119+
Per `cross-package-error-codes.md`, change `error_code()` to **own, namespaced
120+
origin codes** — e.g. `yaml-schema/missing-required`, `yaml-schema/type-mismatch`,
121+
`yaml-schema/invalid-enum`, … (one per `ValidationErrorKind` variant). There is
122+
**no q2 remap** to build (q2 doesn't consume the crate); the external consumers get
123+
the origin codes and may remap to their own presentation codes.
124+
125+
> **⚠️ DECISION TO CONFIRM WITH THE USER before implementing.** Changing `Q-1-x`
126+
> `yaml-schema/*` is **breaking** for the invisible Posit consumers that currently
127+
> see `Q-1-x`. Options:
128+
> - **(A) Origin codes from `0.1.0`** — clean per the discipline; coordinate the
129+
> break with those consumers. *Recommended* (0.1.0 is a fresh public line; do it
130+
> right from the start).
131+
> - **(B) Ship `Q-1-x` as-is in `0.1.0`, defer origin codes to `0.2.0`**
132+
> non-breaking now, but ships Quarto codes in a "non-Quarto" crate (violates the
133+
> discipline) until later.
134+
>
135+
> Ask the user which, and (for A) capture the `Q-1-x``yaml-schema/*` mapping as
136+
> a frozen table in the commit message so the lineage is recoverable.
137+
138+
## 7. Execution checklist
139+
140+
### Phase A — `quarto-yaml` (the leaf; publish first)
141+
- [ ] Create `/Users/cscheid/repos/github/posit-dev/quarto-yaml/` as a **workspace**;
142+
copy `crates/quarto-yaml/``crates/quarto-yaml/`; add `LICENSE` (from q2
143+
root), `.gitignore` (`/target`), `.gitattributes` (`* text=auto eol=lf`),
144+
`README.md` (write fresh — the crate is a YAML parser with source tracking).
145+
- [ ] Workspace `Cargo.toml`: `[workspace] members=["crates/*"]`,
146+
`[workspace.package]` (version `0.1.0`, edition `2024`, license `MIT`,
147+
`repository = https://github.com/posit-dev/quarto-yaml`, authors), and
148+
`[workspace.dependencies]` with `quarto-source-map = "0.1.0"` + the shared
149+
crates.io deps (pin versions to match q2's `[workspace.dependencies]`:
150+
`yaml-rust2`, `serde`, `thiserror`, …). Drop `[lints] workspace = true` or
151+
add a `[workspace.lints]` block (q2 has none).
152+
- [ ] Build + `cargo test` + `cargo clippy --all-targets -- -D warnings` + `cargo
153+
fmt --check` + `cargo publish --dry-run -p quarto-yaml`. Fix any stable-clippy
154+
lints (see §4).
155+
- [ ] External-consumer smoke test (separate crate, path dep, parse a YAML string,
156+
assert source info) — proves the public API is usable standalone.
157+
- [ ] `gh repo create posit-dev/quarto-yaml --public --source=. --push`
158+
(public; mirror Phase 1/3). Confirm CI green on all 3 OSes.
159+
- [ ] **USER**: `cargo publish -p quarto-yaml` (from the new repo).
160+
161+
### Phase B — `quarto-yaml-validation` (second crate, same repo)
162+
- [ ] Copy `crates/quarto-yaml-validation/` (incl. `test-fixtures/`) into the
163+
workspace. Its `quarto-yaml` dep = `{ version = "0.1.0", path =
164+
"../quarto-yaml" }` (path for local dev, version so `cargo publish` resolves
165+
the now-published `quarto-yaml`); `quarto-source-map` / `quarto-error-reporting`
166+
= `"0.1.0"`.
167+
- [ ] **Apply the error-code change** from §6 (after the user's A/B decision) +
168+
update the `error.rs` tests.
169+
- [ ] If yaml-validation tests render diagnostics and asserted `Q-1-x` text, update
170+
them; with no catalog installed in the standalone repo, diagnostics render
171+
**code-only** (`EmptyCatalog`) — assert on the origin codes.
172+
- [ ] Build + test + clippy + `cargo publish --dry-run -p quarto-yaml-validation` +
173+
smoke test (validate a doc against a schema, assert a `yaml-schema/*` code).
174+
- [ ] CI green (3 OSes). **USER**: `cargo publish -p quarto-yaml-validation`
175+
(after `quarto-yaml` is live so the dep resolves).
176+
177+
### Phase C — q2 cutover (one PR, like #348/#350)
178+
- [ ] Branch `braid/bd-egcyeym9-yaml-cutover` off updated main.
179+
- [ ] `[workspace.dependencies.quarto-yaml]` `path``version = "0.1.0"`.
180+
- [ ] Convert `quarto-yaml` path-deps (`pampa`, `quarto-config`) →
181+
`{ workspace = true }`; leave the existing `{ workspace = true }` ones.
182+
- [ ] **Delete** `crates/quarto-yaml-validation/` and `crates/validate-yaml/` (and
183+
drop `[workspace.dependencies.quarto-yaml-validation]` from root `Cargo.toml`;
184+
check for any stray refs to `validate-yaml` in `xtask`/docs/CI).
185+
- [ ] Delete in-tree `crates/quarto-yaml/`.
186+
- [ ] `cargo build --workspace` → confirm Cargo.lock resolves `quarto-yaml 0.1.0`
187+
from the registry. `cargo nextest run --workspace`. **Full `cargo xtask
188+
verify`** (the WASM leg is the real gate — see §5 WASM gotcha; do NOT pipe it
189+
through `tail`, which masks the exit code — a Phase 1 lesson).
190+
- [ ] Update `CLAUDE.md`: move `quarto-yaml` into the "Externalized foundation
191+
crates" section; remove `quarto-yaml-validation` and `validate-yaml` from the
192+
binaries/crate lists; the `validate-yaml` line under **Binaries** must go.
193+
- [ ] Commit, push to `feature/…`, open PR against `main`, watch CI (5 checks),
194+
report. Merge is the user's call.
195+
196+
## 8. Proven gotchas (from Phases 1 & 3 — don't rediscover them)
197+
198+
- **CRLF on Windows**`.gitattributes` `* text=auto eol=lf` from the first commit.
199+
- **Stable clippy stricter than q2's nightly** → fix lints in the new repo
200+
(`items_after_test_module` etc.); the standalone repo becomes the single source.
201+
- **WASM workspace resolution** → §5; verify with full `cargo xtask verify`, never
202+
`--skip-hub-build`.
203+
- **`| tail` masks `cargo xtask verify`'s real exit code** → run it without a tail
204+
pipe (or check the file), or use `run_in_background`.
205+
- **crates.io / GitHub are user/identity-gated and irreversible** → you prep & dry-
206+
run; the user publishes and (optionally) `cargo owner --add github:posit-dev:<team>`.
207+
208+
## 9. Open items to raise with the user
209+
210+
1. **Error-code policy (§6 A vs B)** — the one blocking decision.
211+
2. Confirm the repo is `posit-dev/quarto-yaml` (workspace, two crates) — decided
212+
2026-06-29, but re-confirm before `gh repo create`.
213+
3. Reuse the Phase-1 visibility/ownership choices (public repo; personal crates.io
214+
account now, `cargo owner --add posit-dev` on a weekday) unless told otherwise.
215+
4. Whether to also relocate the deleted **`CONTRIBUTING-ERRORS.md`** intent / any
216+
q2-internal YAML docs (low priority).

0 commit comments

Comments
 (0)