diff --git a/.braid/snapshot.jsonl b/.braid/snapshot.jsonl
index 0924d56f9f..6370dffbb2 100644
--- a/.braid/snapshot.jsonl
+++ b/.braid/snapshot.jsonl
@@ -17,7 +17,7 @@
{"id":"bd-0gsj","title":"Fix CRLF handling in tree-sitter-qmd grammar.js for pipe tables","description":"Tree-sitter grammar at crates/tree-sitter-qmd/tree-sitter-markdown/grammar.js parses pipe tables incorrectly when input uses CRLF line endings. With LF input, the pipe table closes at the blank line and following content becomes a separate block. With CRLF input, the table absorbs the following paragraph as a malformed body row. Bug propagates through pampa end-to-end: rendered Pandoc AST is structurally wrong for any Windows user with core.autocrlf=true (default) authoring a pipe table followed by a paragraph. Affects all output formats (HTML, PDF, Word) since Pandoc AST is upstream of every writer. Surfaced 2026-04-24 during PR 109 Windows verification as 5 failing pipe-table corpus tests; investigation in bd-ntnx classified it as a real grammar bug, not a corpus formatting issue. Full reproducer and observed parse trees in the design field. Cross-reference q2-windows-verify-tests memory note for related Windows CRLF failures.","design":"Reproducer. Two layers, both diagnostic. Layer 1 tests grammar.js directly. Layer 2 tests the full pampa pipeline that real users hit. Fixtures live in the tree-sitter-markdown directory so tree-sitter parse picks up the configured grammar without an init-config step.\n\nLayer 1 (grammar.js):\n\nprintf \"before\\r\\n\\r\\n| a | b |\\r\\n|---|---|\\r\\n| 1 | 2 |\\r\\n\\r\\nafter\\r\\n\" > crates/tree-sitter-qmd/tree-sitter-markdown/test-crlf.qmd\nprintf \"before\\n\\n| a | b |\\n|---|---|\\n| 1 | 2 |\\n\\nafter\\n\" > crates/tree-sitter-qmd/tree-sitter-markdown/test-lf.qmd\ncd crates/tree-sitter-qmd/tree-sitter-markdown\ntree-sitter parse test-crlf.qmd > test-crlf.parse\ntree-sitter parse test-lf.qmd > test-lf.parse\ndiff test-crlf.parse test-lf.parse\n\nLayer 2 (pampa end-to-end):\n\ncargo run --quiet -p pampa --bin pampa -- crates/tree-sitter-qmd/tree-sitter-markdown/test-crlf.qmd > pampa-crlf.out\ncargo run --quiet -p pampa --bin pampa -- crates/tree-sitter-qmd/tree-sitter-markdown/test-lf.qmd > pampa-lf.out\ndiff pampa-crlf.out pampa-lf.out\n\nObserved parse trees from Layer 1 on this branch (fix/windows-tree-sitter-crlf at 80f07171). Both trees agree on the prefix, so the divergence is the trailing block:\n\nLF (correct, table closes at the blank line):\n pipe_table [2, 0] - [5, 0]\n pipe_table_header [2, 0] - [2, 9] ...\n pipe_table_delimiter_row [3, 0] - [3, 9] ...\n pipe_table_row [4, 0] - [4, 9] ...\n pandoc_paragraph [6, 0] - [7, 0]\n pandoc_str [6, 0] - [6, 5]\n\nCRLF (buggy, table absorbs following paragraph):\n pipe_table [2, 0] - [7, 0]\n pipe_table_header [2, 0] - [2, 9] ...\n pipe_table_delimiter_row [3, 0] - [3, 9] ...\n pipe_table_row [4, 0] - [4, 9] ...\n pipe_table_row [6, 0] - [6, 5] <- SPURIOUS, should be a separate paragraph\n pipe_table_cell [6, 0] - [6, 5]\n pandoc_str [6, 0] - [6, 5]\n\nObserved Pandoc AST from Layer 2 (pampa default -t native), confirming the bug propagates past pampa input handling:\n\nLF: [Para [Str \"before\"], Table ... [body row [1,2]], Para [Str \"after\"]]\nCRLF: [Para [Str \"before\"], Table ... [body rows [1,2] AND [Plain \"after\"]]] <- no trailing Para\n\nConfirms pampa does NOT normalize line endings before feeding the grammar. End-user impact: any qmd document with CRLF endings, a pipe table, and following content produces structurally wrong output in every writer that consumes Pandoc AST.\n\nOpen design questions for the fix.\n\n1. Locate the bug in grammar.js. Likely candidates: newline character classes that match only \\n, or the external scanner (scanner.cc) handling of block boundary detection. Read both grammar.js and any external scanner before patching. Tree-sitter-markdown forks vary on this — confirm what our fork does versus what works.\n\n2. Upstream vs local fix. This grammar is forked from MDeiml/tree-sitter-markdown (or whichever upstream we track — confirm in tree-sitter.json or README). If upstream already fixes this, cherry-pick. If not, patch locally and consider an upstream PR.\n\n3. Test-corpus line-ending policy after the fix. Two viable strategies:\n a. Keep test/corpus/*.txt files materialized as CRLF on Windows checkout and rely on the fixed grammar to parse them identically. Tests then exercise CRLF on Windows and LF on Linux, which is useful coverage but creates a small risk that future grammar changes regress CRLF handling silently when only Linux CI runs.\n b. Pin test/corpus/*.txt to LF via .gitattributes (eol=lf for that subdirectory only) and add a separate Windows-only regression test that re-creates the fixtures with CRLF and asserts the parse trees match. More explicit, less coverage drift.\n\n The choice depends on whether we expect frequent grammar changes and whether ubuntu-only CI is sustainable. Recommend deciding before landing the grammar fix.\n\n4. Audit other CRLF patterns. The 5 failing pipe-table tests are the surface; there may be other constructs whose grammar paths assume \\n only. Grep grammar.js (and scanner.cc if present) for \\n usages and audit each one.\n\nReference. Investigation history and original framing in bd-ntnx (closed). Memory note q2-windows-verify-tests catalogues related Windows CRLF failures.","acceptance_criteria":"tree-sitter parse on matched CRLF and LF qmd inputs produces identical S-expression trees for pipe-table fixtures (Layer 1 reproducer diff is empty). pampa default native output is identical for the same matched inputs (Layer 2 reproducer diff is empty). All 5 originally-failing pipe-table tests under cd crates/tree-sitter-qmd/tree-sitter-markdown && tree-sitter test pass on Windows: Example 201 (GFM), Pipe table can precede content (two variants), Pipe table can follow content, Pipe table with blank-line caption. cargo xtask verify step 4 (tree-sitter test) passes on Windows. cargo nextest run --workspace and cargo xtask verify continue to pass on Linux CI (no regression). Decision recorded in commit message or PR description for upstream-vs-local fix and for test-corpus line-ending policy. Audit findings recorded for any other CRLF patterns discovered in grammar.js or scanner.cc. q2-windows-verify-tests memory note updated with PR link and resolution.","status":"closed","priority":1,"issue_type":"bug","created_at":"2026-04-27T14:26:53.718601600Z","created_by":"cderv","updated_at":"2026-05-04T13:45:00.482193500Z","closed_at":"2026-05-04T13:45:00.481818100Z","close_reason":"PR #139 merged 2026-04-30: fixed CRLF pipe-table line ending in scanner.c (typo fix at line 2259), added Rust regression test and xtask CRLF parity check","external_ref":"https://github.com/quarto-dev/q2/issues/138","labels":["tree-sitter","windows"],"dependencies":{"bd-ntnx:discovered-from":{"depends_on_id":"bd-ntnx","type":"discovered-from","created_at":"2026-04-27T14:26:53.718601600Z","created_by":"cderv"}},"comments":{"c-2vcbuybu":{"id":"c-2vcbuybu","author":"cderv","created_at":"2026-04-28T16:05:51Z","text":"Tomorrow's verification (next session pickup): (1) check PR #139 CI status — gh pr checks 139 --repo quarto-dev/q2; (2) if green, merge; (3) on merge, close bd-0gsj with PR link as the close reason; (4) flush and commit accumulated .beads/issues.jsonl from main repo (this session created bd-vxl8 and bd-picv, plus comments on bd-238o, bd-3pe8, bd-0gsj); (5) update q2-windows-verify-tests memory note status to fully resolved."},"c-swxf3f4l":{"id":"c-swxf3f4l","author":"cderv","created_at":"2026-04-29T16:25:17Z","text":"CRLF parity check landed in commit 8f61f587 on branch fix/windows-tree-sitter-crlf (PR #139). cargo xtask verify step 4 now re-runs tree-sitter test against a CRLF-converted corpus copy; 451/451 pass, regression-tested by reverting scanner.c (446/451, build fails). Awaiting PR merge to close."},"c-xjxgh7ir":{"id":"c-xjxgh7ir","author":"cderv","created_at":"2026-04-28T15:37:36Z","text":"PR #139 opened (https://github.com/quarto-dev/q2/pull/139). Layer 1 + Layer 2 reproducer diffs empty post-fix. tree-sitter test on Windows: 451/451 (was 446/451). Decision: local fix, not upstream — typo introduced on this fork in 79489c4e. Decision: no .gitattributes change for test corpus — fixed grammar parses LF and CRLF identically, so existing corpus tests both modes (LF on Linux, CRLF on Windows checkout). Audit: line 2259 was the only outlier; all other newline checks in scanner.c use paired '\\n' || '\\r'. Added Rust regression test pipe_table_crlf_matches_lf for cross-platform coverage."}}}
{"id":"bd-0gwekaem","title":"nested_cell_mask doesn't cover inline expressions, so a displayed `{r} x` still executes","description":"The nested-cell mask (claude-notes/plans/2026-09-02-nested-cell-masking.md, \"Make q2 self-documenting again: don't execute a cell that is being displayed\") suppresses execution of an executable *fence opener* inside a markdown-displaying CodeBlock. It does not cover *inline* expressions, so an inline brace expression inside a display block still runs.\n\nWHY IT DIDN'T APPLY BEFORE\nThe mask's Scope section lists what is deliberately out (RawBlock, doubled-brace, {=html} openers, cells nested in cells). Inline expressions are not on that list because they were not reachable: q2 never evaluated the `{r} expr` spelling at all. bd-inline-r-brace-spelling-not-evaluated-lk9s3iwe made that spelling live, which is what opens this hole. The fix for that strand is correct and matches Quarto 1; this is the follow-on the mask needs to stay whole.\n\nMEASURED, q2 (with the inline fix) vs Quarto 1 99.9.9. Fixture: knitr document, `v <- \\\"EVALUATED\\\"`, then each form. EVALUATED means the escape failed.\n\n form q2 Q1\n plain prose executes executes (the feature)\n `` `{r} v` `` extra backticks executes executes\n `{{r}} v` doubled brace shows {{r}} v shows {r} v\n inside a ````markdown block executes executes\n inside a ````r block executes executes\n\nTwo consequences:\n1. The escape q2 documents for cells -- wrap it in a longer fence -- does not work for inline expressions, so guides/authoring/computations.qmd has to document a different rule for the inline case than for the cell case.\n2. The only working escape is the doubled brace, and q2 displays it verbatim ({{r}}) where Q1 collapses it to {r}. So a q2 author currently has no way to show a reader the text `{r} x`. Note q2's literal rendering is defensible on its own terms -- collapsing doubled braces is what Q-2-50 deliberately dropped so Jinja/Mustache templates render correctly -- which is why the fence-wrapper route, not brace-collapsing, is the right fix.\n\nSHAPE OF THE FIX\nSmall and contained. `mask_code_block_text` (crates/quarto-core/src/engine/nested_cell_mask.rs) is the single rewrite site; add an inline pattern alongside MASK_OPENER_RE that rewrites an inline `{lang}` marker inside an in-scope CodeBlock the same way the opener is rewritten. `unmask` needs no change: UNMASK_OPENER_RE is deliberately not line-anchored (H4, for blockquote prefixes), so it already restores a mid-line occurrence.\n\nMind the plan's invariants: byte-exact restore is load-bearing for reconcile (spec section 'Reconcile -- why byte-exactness is load-bearing'), and the test-seam spec is frozen, so this wants its own tier rather than being bolted onto an existing one.\n\nAlso worth deciding: whether the empty-classes / `markdown`-only display-class predicate is right for inline too, or whether the inline case should be widened (an author writing ````r to show inline syntax gets nothing today -- the E row above).\n\nDocs: guides/authoring/computations.qmd's 'Writing about an inline expression' section documents today's behaviour and should be simplified to match the cell rule once this lands.","status":"open","priority":2,"issue_type":"bug","created_at":"2026-09-04T16:33:12.125548Z","created_by":"Gordon Woodhull","updated_at":"2026-09-04T18:12:17.724568Z","labels":["engine"],"dependencies":{"bd-inline-r-brace-spelling-not-evaluated-lk9s3iwe:discovered-from":{"depends_on_id":"bd-inline-r-brace-spelling-not-evaluated-lk9s3iwe","type":"discovered-from","created_at":"2026-09-04T16:33:12.125548Z","created_by":"Gordon Woodhull"}},"comments":{"c-5lgjuro9":{"id":"c-5lgjuro9","author":"Gordon Woodhull","created_at":"2026-09-04T18:12:17.724568Z","text":"PR #657 — https://github.com/quarto-dev/q2/pull/657 (remote branch bugfix/bd-0gwekaem-nested-cell-mask-inline-expressions, one commit 8e13ecf8b). Pushed after a full cargo xtask verify --skip-rust-tests --skip-hub-tests (WASM/hub-client leg included, all 14 steps green); workspace nextest 13692 passed / 199 skipped and workspace clippy -D warnings were already clean on the same commit."},"c-ndusewmr":{"id":"c-ndusewmr","author":"Gordon Woodhull","created_at":"2026-09-04T16:40:36.539600Z","text":"Dispatched to agent inline-executable-wrapped on workspace-2 (branch braid/bd-0gwekaem-nested-cell-mask-inline-expressions).\n\nConfirmed reproducible on main today with the classic spelling -- an inline `r v` inside a ````markdown display block renders EVALUATED on q2 0.29.0. The brace-spelling half stays latent until bd-inline-r-brace-spelling-not-evaluated-lk9s3iwe merges; the agent was told to cover both spellings and to branch from main rather than from that unmerged branch, which is pending a history squash."},"c-wvs2dbvu":{"id":"c-wvs2dbvu","author":"Gordon Woodhull","created_at":"2026-09-04T17:32:06.840651Z","text":"Fixed on braid/bd-0gwekaem-nested-cell-mask-inline-expressions @ 8e13ecf8b (off main @ 914f20697). Not pushed.\n\nMASKED SPELLING. One rewrite for both inline spellings: the marker is inserted directly after the opening backtick, so ````r v```` becomes ````q2-nested-executable r v```` and ````{r} v```` becomes ````q2-nested-executable {r} v````. unmask deletes the same fixed string. This departs from the strand's suggestion that UNMASK_OPENER_RE could restore the brace form for free: it can, but the classic spelling has no brace to work with and would need its own rule regardless, and two masked spellings buy nothing over one. Insert-only/delete-only also gives the strongest byte-exactness argument available, which the plan says is load-bearing for reconcile.\n\nMASK_INLINE_RE = (^|[^`])`((?:\\{[A-Za-z0-9_]+\\}|r)[ \\t#][^`]+)`\n\n- Brace form: any [A-Za-z0-9_]+, engine-agnostic, matching MASK_OPENER_RE's language class and Q1's per-language executeInlineCodeHandler. Doubled braces excluded by the same first-char rule as openers.\n- Classic form: literal r only. It is knitr's own rmarkdown syntax and is hardcoded to r in all_patterns$md$inline.code; generalizing it to any word would rewrite every ordinary code span in displayed prose.\n- Separator [ \\t#] is the union of knitr's [ #] and q2's [ \\t] -- the mask has to be a superset of every scanner that could claim the expression.\n- The (^|[^`]) guard is the bd-knitr-inline-r-eats-fence-2ofk91x1 shape: without it a nested fence's third backtick anchors a match. It excludes only a backtick, not a backslash -- knitr does not exclude one either, and the YAML-fallback case that motivates INLINE_R_PATTERN's backslash guard is text this mask never sees. Accepted limitation, shared with INLINE_R_PATTERN and Q1: two expressions with no character between them leave the second unmasked.\n\nDISPLAY-CLASS PREDICATE: left narrow (empty or [\"markdown\"]), and filed as bd-tiidc899. Three reasons. It is not inline-specific -- the same predicate gates both rewrites, so widening changes the cell escape too, which 2026-09-02-nested-cell-masking.md scoped out explicitly. Widening for inline only would give the incoherent rule '````r protects your inline expressions but not your nested fences'. And T18 is frozen and revert-bound to the narrow predicate. Narrow is also parity: the strand's own matrix has Q1 executing in a ````r block too.\n\nTESTS: new tier, own hunk ids, T1-T24 untouched. H14 mask rewrites an inline marker, H15 unmask restores it, H16 the guard. T25/T26 render-tier (real knitr); T27 x9 round-trip byte-exactness (classic, brace, tab, hash, two-on-a-line, bare fence, blockquoted, multiline body, opener+inline together); T28 a live cell's backtick-quoted R name untouched; T29 the guard; T30 engine-agnostic brace + doubled brace out of scope; T31 binds to the production scanner -- resolve_inline_r_expressions leaves the masked text alone while the control unmasked text is rewritten, which is the inline counterpart of T14 and starts discriminating the brace half automatically when the {r} alternation lands.\n\nVACUITY FOUND AND FIXED: the first draft of T25 used INLINE-RAN as its runtime-only marker and passed with H14 reverted. q2's classic spelling escapes markdown specials, so the executed value reaches the HTML as INLINE\\-RAN and the contains() check was blind to it. Marker changed to INLINERAN; both render rows confirmed RED under a reverted H14 before the fix was restored. A runtime-only marker used with an inline expression must survive .QuartoInlineRender's escaping unchanged -- recorded in the test file.\n\nVERIFICATION: workspace 13692 passed / 199 skipped, +16 over the 13676 baseline, no removals. cargo clippy --workspace --all-targets -D warnings clean. End-to-end with a freshly built q2 on the strand's repro: the ````markdown row now renders 'Inline: `r v`' where main renders 'Inline: EVALUATED'; the ````r row still renders EVALUATED (bd-tiidc899).\n\nDOCS: no change here. The 'Writing about an inline expression' section the strand names does not exist on main -- it is added by bd-inline-r-brace-spelling-not-evaluated-lk9s3iwe's second commit. Simplifying it belongs with that merge; docs/guides/authoring/computations.qmd on main only documents the cell rule, which stays accurate."}}}
{"id":"bd-0h8crv6u","title":"Duplicate-seq self-heal for file documents (backoff + circuit breaker)","description":"Generalize the index-doc duplicate-seq self-heal (bd-6f21d4c6, ts-packages/quarto-sync-client/src/client.ts installDuplicateSeqRecovery) to file documents: a legacy stable-actor client colliding on a FILE doc currently wedges that file's sync permanently for the receiving browser (only remedy: manual IndexedDB wipe). Add per-document recovery with backoff/attempt cap and the state.unavailableFiles retry poll as circuit breaker. Plan: claude-notes/plans/2026-10-22-file-doc-duplicate-seq-recovery.md","status":"closed","priority":1,"issue_type":"feature","created_at":"2026-10-22T10:23:21.422509Z","created_by":"shikokuchuo","updated_at":"2026-10-22T12:40:50.024324Z","closed_at":"2026-10-22T12:40:50.024324Z","close_reason":"Implemented: recoverFileDocument with per-document backoff (3 attempts / 30s cooldown / 300s quiet window) + circuit breaker into the unavailable-files retry poll; routing generalized by documentId in installDuplicateSeqRecovery; change-handler tracking keyed by doc id; recoveryBackoff injectable via ConnectOptions. Tests: new full-stack-file-collision.test.ts (red-then-green gated) + file-collision-recovery.test.ts (backoff, circuit breaker, index + untracked-doc regressions). 191 sync-client + 462 hub-client tests green; build:all green. Plan: claude-notes/plans/2026-10-22-file-doc-duplicate-seq-recovery.md","dependencies":{"bd-6f21d4c6:related":{"depends_on_id":"bd-6f21d4c6","type":"related","created_at":"2026-10-22T10:23:21.422509Z","created_by":"shikokuchuo"}}}
-{"id":"bd-0i6fii0k","title":"product-doc-theme (posit-docs) on q2: exploratory assessment + fix plan","description":"Exercise posit-dev/product-doc-theme (the posit-docs custom project type) on a q2 website, compare against Quarto 1 (prima facie correct), and record what works, what breaks, and what fundamentally cannot work as-is. Covers q2 add (not implemented), contributes.project path handling, theme/include rebasing, env shortcodes, and the example site's markdown. Deliverable: claude-notes plan with assessment + fix plan split between q2 and product-doc-theme.","status":"in_progress","priority":1,"issue_type":"task","created_at":"2026-10-21T19:39:12.691821Z","created_by":"Carlos Scheidegger","updated_at":"2026-10-26T16:27:40.583704Z","labels":["extensions"],"dependencies":{"bd-ad7i1pc6:related":{"depends_on_id":"bd-ad7i1pc6","type":"related","created_at":"2026-10-21T19:39:12.691821Z","created_by":"Carlos Scheidegger"}},"comments":{"c-38tbbbtl":{"id":"c-38tbbbtl","author":"Claude Fable 5.1","created_at":"2026-10-22T15:05:23.080050Z","text":"Filed per-finding strands (2026-10-22): F1 bd-74ktvzv7 (logo doubling, P1 bug), F3 bd-t3n1nghb (link-external-*), F4 bd-rfz4e3bw (q2 add), F6 bd-7u2xu4gp ({mermaid} fence warning), F7 bd-hfghrqvh (table caption classes), F8 bd-3z5bubrf (quarto-required), F9 bd-f5l0n6oa (Q-2-9 in config, P4), F10 bd-2pmjmjmj (diagram fig-align). F5 goes to the extension (raw {=html} block). F2 experiment: replacing the footer
tags with markdown images ({.light-content width=\"65px\\}) works on q2 — all four footer images are copied to _site/_extensions/posit-docs/assets/images/ and load (0 broken footer images in headless Chromium; the only broken image left is the F1 logo), and Q-2-9 drops from 24 to 2 (the in website.title). On Quarto 1 the left item (text+href) renders the markdown and relocates the files to site_libs/quarto-contrib/; but the right item (a markdown link wrapping two images with {#open_preferences_center title=...} attributes and no href) is emitted verbatim, unrendered, so the cookie-preferences link needs a different Q1-compatible spelling or stays raw HTML."},"c-et30yzmn":{"id":"c-et30yzmn","author":"Carlos Scheidegger","created_at":"2026-10-26T16:27:40.583704Z","text":"Status refresh 2026-10-26: rebased branch onto main @ 4d966ea05. Re-ran the example on nightly 0.36.0-nightly.20261024: F1/F4/F7/F10 verified fixed; F3 merged today (#906), not in the nightly yet. Filed F2(2) as bd-5r97wehd (raw
in footer not copied, no Q-5-6). Closed bd-t3n1nghb, bd-hfghrqvh, bd-2pmjmjmj (PRs merged). Example still 5/6 pages (F5 grid.qmd, extension-side). Plan updated with a Status 2026-10-26 section."},"c-oper168q":{"id":"c-oper168q","author":"Carlos Scheidegger","created_at":"2026-10-21T19:44:22.496560Z","text":"Assessment + plan written: claude-notes/plans/2026-10-21-product-doc-theme-q2.md (commit 9f3c57e44 on braid/bd-0i6fii0k-product-doc-theme-q2). Key findings: custom project types already work (bd-ad7i1pc6) — type: posit-docs resolves, themes/includes/env shortcodes all land; 5/6 example pages render. Gaps: F1 navbar logo src doubled (resolve_metadata_path re-resolves the rebased extension path against _extension.yml's dir; P1 q2 bug), F2 /_extensions/ asset URLs in footer raw
not copied/relocated (Q1 relocates to site_libs/quarto-contrib/quarto-project), F3 link-external-icon/newwindow unimplemented, F4 q2 add is a stub, F5 inline