diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index 2495bd3bb..d8851e48e 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -1,10 +1,13 @@ -{"id":"bd-068k","title":"Phase 0: quarto-publish crate scaffold + provider trait","description":"Phase 0 of bd-t3ny. Build the quarto-publish crate skeleton: types, PublishProvider trait (prepare/commit/verify), PublishHost, PublishRenderer, ProviderRegistry, NativeHost, CLI argument validation, and wire into crates/quarto/src/commands/publish.rs (replacing NotImplemented stub).\n\nProvider impls in this phase: gh-pages registered with prepare/commit unimplemented!() — tests exercise registry lookup and CLI dispatch only.\n\nPlan: claude-notes/plans/2026-05-03-publish-command-and-gh-pages.md (Phase 0 section)","status":"completed","priority":1,"issue_type":"task","created_at":"2026-05-03T14:37:32.946678Z","created_by":"cscheid","updated_at":"2026-05-03T14:57:58.079046Z","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-068k","depends_on_id":"bd-t3ny","type":"parent-child","created_at":"2026-05-03T14:37:32.946678Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} +{"id":"bd-068k","title":"Phase 0: quarto-publish crate scaffold + provider trait","description":"Phase 0 of bd-t3ny. Build the quarto-publish crate skeleton: types, PublishProvider trait (prepare/commit/verify), PublishHost, PublishRenderer, ProviderRegistry, NativeHost, CLI argument validation, and wire into crates/quarto/src/commands/publish.rs (replacing NotImplemented stub).\n\nProvider impls in this phase: gh-pages registered with prepare/commit unimplemented!() — tests exercise registry lookup and CLI dispatch only.\n\nPlan: claude-notes/plans/2026-05-03-publish-command-and-gh-pages.md (Phase 0 section)","status":"closed","priority":1,"issue_type":"task","created_at":"2026-05-03T14:37:32.946678Z","created_by":"cscheid","updated_at":"2026-05-03T14:57:58.079046Z","closed_at":"2026-05-03T14:57:58.079046Z","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-068k","depends_on_id":"bd-t3ny","type":"parent-child","created_at":"2026-05-03T14:37:32.946678Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} {"id":"bd-0a3b","title":"Cargo: upgrade rand v0.9.4 → v0.10.1","description":"Major upgrade surfaced by cargo-upgrade survey 2026-05-04. Current 0.9.4 is range-pinned in workspace; latest is 0.10.1. Type: pre-1.0 minor (semver-breaking). Review changelog and bump deliberately. See claude-notes/plans/2026-05-04-cargo-upgrade-survey.md and bd-hb8h.","status":"open","priority":3,"issue_type":"chore","created_at":"2026-05-04T18:15:54.920222Z","created_by":"cscheid","updated_at":"2026-05-04T19:06:15.700658Z","source_repo":".","compaction_level":0,"original_size":0,"labels":["cargo","deps"],"dependencies":[{"issue_id":"bd-0a3b","depends_on_id":"bd-hb8h","type":"discovered-from","created_at":"2026-05-04T18:16:05.024220Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} {"id":"bd-0fd0","title":"Lua filter injection slot between generate and render transforms","description":"Today, UserFiltersStage::pre() runs before AstTransformsStage and ::post() runs after. There is no Lua filter slot interleaved between generate-style transforms (NavbarGenerate, SidebarGenerate, ListingGenerate, etc.) and their render-side counterparts inside AstTransformsStage, so user filters cannot mutate resolved navigation/listings data before HTML emission.\n\nStrawman: split build_transform_pipeline (crates/quarto-core/src/pipeline.rs) into build_generate_transforms() and build_render_transforms() with a configurable user-filter bridge in between. The L3 listing transforms, navbar generate, sidebar generate, etc., become the first consumers.\n\nSource-code TODO markers should land at:\n- crates/quarto-core/src/pipeline.rs (NAVIGATION PHASE comment block)\n- crates/quarto-core/src/transforms/navbar_generate.rs (existing precedent with the same latent assumption)\n- crates/quarto-core/src/transforms/listing_generate.rs (added during L3)\n\nDiscovered while writing L3 (bd-ml8z) sub-plan; see D2/D13 in claude-notes/plans/2026-05-06-listings-L3-resolve-transform.md.","status":"open","priority":2,"issue_type":"task","created_at":"2026-05-06T18:26:18.595826Z","created_by":"cscheid","updated_at":"2026-05-06T18:26:18.595826Z","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-0fd0","depends_on_id":"bd-ml8z","type":"discovered-from","created_at":"2026-05-06T18:26:18.595826Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} +{"id":"bd-0gkh3","title":"Phase 6 — hub-mcp refresh-on-401 (RefreshManager)","description":"New module ts-packages/quarto-hub-mcp/src/auth/refresh-manager.ts wrapping CredentialStore + oauth4webapi refresh-token grant.\n\nBehaviour fixed by empirical verification 2026-05-19: Google does NOT rotate refresh tokens for Limited-Input-Devices clients, so the persistence rule is keep-prior-on-missing. Defensive test still covers the rotated-refresh-token case.\n\nConcurrent-callers coalesce via in-flight-promise mutex; invalid_grant from Google triggers CredentialStore.clear() + typed ReauthRequired naming authenticate_start.\n\nPlan §Phase 6: claude-notes/plans/2026-05-05-hub-mcp-device-flow-implementation.md","status":"open","priority":1,"issue_type":"task","created_at":"2026-05-20T14:27:14.252095Z","created_by":"shikokuchuo","updated_at":"2026-05-20T14:27:14.252095Z","source_repo":"kyoto","source_repo_path":"/Users/shikokuchuo/r/kyoto","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-0gkh3","depends_on_id":"bd-cmp48","type":"parent-child","created_at":"2026-05-20T14:27:14.252095Z","created_by":"shikokuchuo","metadata":"{}","thread_id":""}]} {"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","source_repo":".","compaction_level":0,"original_size":0,"labels":["tree-sitter","windows"],"dependencies":[{"issue_id":"bd-0gsj","depends_on_id":"bd-ntnx","type":"discovered-from","created_at":"2026-04-27T14:26:53.718601600Z","created_by":"cderv","metadata":"{}","thread_id":""}],"comments":[{"id":15,"issue_id":"bd-0gsj","author":"cderv","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.","created_at":"2026-04-28T15:37:36Z"},{"id":16,"issue_id":"bd-0gsj","author":"cderv","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.","created_at":"2026-04-28T16:05:51Z"},{"id":18,"issue_id":"bd-0gsj","author":"cderv","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.","created_at":"2026-04-29T16:25:17Z"}]} {"id":"bd-0jyl","title":"Source-info threading through listing markdown re-parse","description":"L3's ListingRenderTransform invokes pampa::readers::qmd::read on doctemplate output to obtain Pandoc blocks, then discards the fresh SourceContext. Diagnostics from that re-parse are collapsed into a single Q-12-10 host-page warning naming the listing id and first message — not threaded back to the host page's SourceContext.\n\nFor a proper design, a way is needed to merge the fresh SourceContext from the re-parse into the host page's SourceContext, *and* preserve the chain 'host YAML key → template substitution → markdown span' through to the diagnostic. Likely cuts across quarto-source-map, quarto-doctemplate, and the diagnostic-builder layer. Worth its own design session.\n\nDiscovered while writing L3 (bd-ml8z) sub-plan; see D3 in claude-notes/plans/2026-05-06-listings-L3-resolve-transform.md.","status":"open","priority":2,"issue_type":"task","created_at":"2026-05-06T18:26:24.690377Z","created_by":"cscheid","updated_at":"2026-05-06T18:26:24.690377Z","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-0jyl","depends_on_id":"bd-ml8z","type":"discovered-from","created_at":"2026-05-06T18:26:24.690377Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} {"id":"bd-0mji","title":"Phase D follow-up: SPA render-event hook + dep-graph filter regression tests","description":"Two related affordances needed once Phase D's dep-graph filter for re-render is implemented:\n\n1. **SPA render-event hook.** PreviewApp's render useEffect re-fires for every contentTick bump. Today this is invisible at the DOM when neither active-page content nor merged metadata change. A small test-only counter (window.__renderTicks or similar) would let Playwright assert 'this edit did/didn't trigger a re-render' for cases where the rendered output is identical.\n\n2. **Dep-graph filter regression tests.** Once Phase D filters re-renders against ProjectDependencyGraph, we need:\n - Active page re-renders when an edited sibling IS a dependency (positive case).\n - Active page does NOT re-render when an edited sibling is NOT a dependency (negative case — the savings).\n\nReframes the deferred Phase B.4 plan acceptance criterion 3 ('editing an unrelated sibling re-renders the active page only when there's a dep edge'). In Phase B the contract was relaxed to 'always re-renders' because no filter exists; Phase D restores the original strict contract.\n\nPlan: claude-notes/plans/2026-05-13-q2-preview-phase-b.md §B.4 (deferred criterion 3).","status":"closed","priority":3,"issue_type":"task","created_at":"2026-05-13T21:11:41.646912Z","created_by":"cscheid","updated_at":"2026-05-14T19:25:36.371752Z","closed_at":"2026-05-14T19:25:36.371611Z","close_reason":"Both items resolved: (1) window.__renderTicks render-event hook landed in D.3 (bd-kw93.9, commit 1ddcbeaf); (2) dep-graph filter regression tests landed in D.6 (bd-kw93.12, commit 7310d44b) — negative case in dep-graph-filter.spec.ts, positive case in the existing include-shortcode.spec.ts (Phase B.3).","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-0mji","depends_on_id":"bd-kw93","type":"parent-child","created_at":"2026-05-13T21:11:41.646912Z","created_by":"cscheid","metadata":"{}","thread_id":""},{"issue_id":"bd-0mji","depends_on_id":"bd-mrx1","type":"discovered-from","created_at":"2026-05-13T21:11:41.646912Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} +{"id":"bd-0pm7a","title":"fix(preview): iframe src must be relative to support subpath deployment","description":"Q2DebugIframe and Q2PreviewIframe hard-code src='/q2-debug.html' and src='/q2-preview.html' (root-absolute). Vite is configured with base: './' so the rest of the build uses relative URLs. Locally and in Playwright the app is served at the host root, so the two are indistinguishable; when deployed under a subpath the iframe src resolves to the wrong origin path and the iframe loads a 404 / SPA shell, leaving the preview blank. Fix: drop the leading slash in both files.","status":"closed","priority":1,"issue_type":"bug","created_at":"2026-05-21T11:56:32.004803Z","created_by":"shikokuchuo","updated_at":"2026-05-21T12:11:14.743822Z","closed_at":"2026-05-21T12:11:14.743764Z","close_reason":"Dropped the leading slash on both iframe srcs (commit 843cd7c3). Subpath deployment verified locally.","source_repo":"kyoto","source_repo_path":"/Users/shikokuchuo/r/kyoto","compaction_level":0,"original_size":0} {"id":"bd-0tr6","title":"Website projects epic: multi-page sites with sidebars, shared resources, and hub-client integration","description":"Design and implement multi-page website projects for Quarto 2. Covers: typed static-document snapshots (PageSnapshot/DocumentProfile working name), pipeline resumability, ProjectType trait, website sidebars, scoped/relocatable artifact stores (site_libs), cross-doc link rewriting, sitemap/favicon/site-url, incremental rebuilds, and hub-client project rendering.\n\nMVP scope explicitly excludes: search, listings/RSS, aliases, announcements, analytics, 404, reader-mode, repo-actions, breadcrumbs, book project type, quarto preview, freeze.\n\nPlan: claude-notes/plans/2026-04-23-website-project-epic.md\n\nDesign decisions from initial conversation:\n- Snapshot is a typed serializable value produced at a pipeline checkpoint after MetadataMergeStage; downstream code consumes Vec; user filters read but do not mutate.\n- Pipeline stages up to snapshot are resumable (cloneable) to avoid redundant execution across pass 1 (snapshot sweep) and pass 2 (per-file render).\n- ProjectType trait introduced now (website, default). Single-document renders go through DefaultProjectType.\n- Artifact stores scope-aware (Page vs Project) and relocatable; single-doc = project with implicit _quarto.yml.\n- Hub-client caches project nav state; renders active page with cached state. Design must leave room for Q2 'quarto preview' which will be a local hub-client instance.\n- Naming TBD in Phase 0 (DocumentProfile working name; user dislikes Page*/Metadata*).","status":"open","priority":1,"issue_type":"epic","created_at":"2026-04-23T18:42:28.206394Z","created_by":"cscheid","updated_at":"2026-04-23T18:42:28.206394Z","source_repo":".","compaction_level":0,"original_size":0} +{"id":"bd-0ufq5","title":"Phase 2 — Hub middleware: Bearer extraction + audience allowlist + CSRF/Origin gating","description":"Land Bearer-auth support in crates/quarto-hub alongside the existing cookie path. TDD: write the 30-test set in crates/quarto-hub/tests/auth_bearer.rs first, then implement.\n\nTouch points: auth.rs (audience config, OidcClaims migration, azp/iat checks), server.rs (cookie extraction, Authenticated extractor, CSRF + WS-Origin gating).\n\nDual-credential 400 rule and full OIDC §3.1.3.7 azp logic are required. Sibling bug bd-XXXX tracks the dual-credential CVE test specifically.\n\nPlan §Phase 2: claude-notes/plans/2026-05-05-hub-mcp-device-flow-implementation.md","status":"closed","priority":1,"issue_type":"task","created_at":"2026-05-20T14:26:56.463684Z","created_by":"shikokuchuo","updated_at":"2026-05-20T21:22:38.345334Z","closed_at":"2026-05-20T21:22:38.345295Z","close_reason":"Phase 2 landed. AuthConfig.additional_audiences plumbed, OidcClaims gained aud/azp/iat, build_auth_state_from_parts added, extract_credential + CredentialKind + dual-credential 400 in server.rs, Authenticated carries credential_kind, CSRF/WS-Origin gated, audit emission via tracing::event!. 33 new integration tests + 7 lib tests pass; cargo xtask verify --skip-hub-build clean. See plan.","source_repo":"kyoto","source_repo_path":"/Users/shikokuchuo/r/kyoto","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-0ufq5","depends_on_id":"bd-cmp48","type":"parent-child","created_at":"2026-05-20T14:26:56.463684Z","created_by":"shikokuchuo","metadata":"{}","thread_id":""}]} {"id":"bd-0wyo","title":"Server-precomputed other_metadata_html for default listing","description":"Q1's item-default.ejs.md iterates over fields *not* in the curated set ('title','image','image-alt','date','author','subtitle','description','reading-time','categories') and emits a
per such field. This is dynamic in EJS — there is no equivalent in doctemplate without server-side precomputation.\n\nL3 v1 ships the default listing with curated-fields-only output. This issue picks up the gap by adding an other_metadata_html string to the per-item TemplateValue::Map binding, computed server-side in the listing render transform. The built-in item-default.template gets one more interpolation site: $item.other-metadata-html$.\n\nSource-code TODO marker lands in crates/quarto-core/src/project/listing/templates/item-default.template at the otherFields gap location (added during L3).\n\nDiscovered while writing L3 (bd-ml8z) sub-plan; see D15 in claude-notes/plans/2026-05-06-listings-L3-resolve-transform.md.","status":"open","priority":3,"issue_type":"task","created_at":"2026-05-06T18:26:31.323613Z","created_by":"cscheid","updated_at":"2026-05-06T18:26:31.323613Z","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-0wyo","depends_on_id":"bd-ml8z","type":"discovered-from","created_at":"2026-05-06T18:26:31.323613Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} {"id":"bd-0xmt","title":"Phase A.0: Lift WASM JS bridge to @quarto/wasm-js-bridge workspace package","description":"Move hub-client/src/wasm-js-bridge/ to a new @quarto/wasm-js-bridge workspace package. Wire hub-client + q2-preview-spa + preview-renderer test configs to alias /src/wasm-js-bridge -> the package's src. Removes the cross-platform symlink/duplication risk for Phase A.3. See claude-notes/plans/2026-05-13-q2-preview-phase-a.md §A.0.","status":"closed","priority":1,"issue_type":"task","created_at":"2026-05-13T16:53:11.353454Z","created_by":"cscheid","updated_at":"2026-05-13T17:07:44.781502Z","closed_at":"2026-05-13T17:07:44.781355Z","close_reason":"Bridge files moved to @quarto/wasm-js-bridge workspace package; consumers alias /src/wasm-js-bridge. Verified end-to-end (sass compile through bridge proven by hub-client test:wasm; cargo xtask verify 11/11). Commits ea1bb889 + changelog 043f65e1.","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-0xmt","depends_on_id":"bd-kw93","type":"parent-child","created_at":"2026-05-13T16:53:11.353454Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} {"id":"bd-1066","title":"HTML comments lost during incremental writes","description":"HTML comments (<\\!-- ... -->) are dropped from the Pandoc AST during parsing. tree-sitter parses them as 'comment' nodes which become IntermediateUnknown and are silently removed. This causes comments to be lost when the incremental writer rewrites blocks containing comments. Block-level standalone comments survive as empty Para blocks (preserved via source spans on KeepBefore), but inline comments within paragraphs are completely gone.","status":"open","priority":1,"issue_type":"bug","created_at":"2026-02-09T15:35:05.022976Z","created_by":"cscheid","updated_at":"2026-02-09T15:35:05.022976Z","source_repo":".","compaction_level":0,"original_size":0} @@ -99,6 +102,7 @@ {"id":"bd-78ud","title":"Empty {stem}_files/ cleanup for pages with no Page-scoped artifacts","description":"Today render_document_to_file calls prepare_html_resources which always creates {stem}_files/, even when the page emits no Page-scoped artifacts (which is the common case for prose-only pages under a website project — all theme/extension CSS lives in site_libs/). Result: every rendered website has empty {stem}_files/ directories scattered around. Opportunity: defer creation until the first Page-scoped write, or skip creation entirely when no Page artifacts exist. Tracked from Phase 5 as open question 5; see claude-notes/plans/2026-04-24-websites-phase-5.md.","status":"closed","priority":3,"issue_type":"task","created_at":"2026-04-25T01:31:24.276058Z","created_by":"cscheid","updated_at":"2026-04-29T17:19:51.662612Z","closed_at":"2026-04-29T17:19:51.662200Z","close_reason":"Dropped eager dir_create from prepare_html_resources. {stem}_files/ is now created lazily by write_artifacts via dir_create(parent, true) on each artifact write — only when there's something to put inside. Prose-only website pages no longer leave empty per-page dirs in _site/. Single-doc renders still create the dir (theme CSS routes there for lib_dir == ''). New regression test website_render_omits_empty_stem_files_dirs in tests/artifact_scoping_pipeline.rs. End-to-end verified across 5 example projects: 0 empty dirs in any _site tree.","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-78ud","depends_on_id":"bd-u5pr","type":"discovered-from","created_at":"2026-04-25T01:31:24.276058Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} {"id":"bd-79xl","title":"Surface FormatIdentifier::Custom discriminant in TS engine protocol","description":"TsEngine::execute (Plan 1a Phase 1) asserts !matches!(fmt.identifier, FormatIdentifier::Custom(_)) because Custom(u32).as_str() returns the literal \"custom\" and drops the u32 discriminant (crates/quarto-core/src/format.rs:44-58). The assert means TS engine extensions cannot run against any custom format.\n\nResolution path:\n- Decide where the per-Custom(u32) format name lives. Either add a registry that maps the discriminant to a name string, or include the discriminant in the protocol via a new field (e.g., add an optional \"custom-name\" field to TsFormatIdentifier).\n- Plumb it through TsFormatIdentifier.base-format (or a sibling) so the harness can pass it to TS engines correctly.\n- Remove the assert in TsEngine::execute.\n\nLand before the first custom format ships to production. No urgency today (no in-tree custom formats); urgency rises when a downstream extension defines one.\n\nContext: discovered during Plan 1a editorial review. See claude-notes/plans/2026-04-16-plan1a-protocol-and-core.md (\"Custom-format gotcha\" in the format.identifier mapping section).","status":"open","priority":2,"issue_type":"task","created_at":"2026-05-06T19:06:56.710766Z","created_by":"gordon","updated_at":"2026-05-06T19:06:56.710766Z","source_repo":".","compaction_level":0,"original_size":0} {"id":"bd-7co9","title":"Clean up stale 'fork of tree-sitter-markdown' framing in tree-sitter-qmd","description":"tree-sitter-qmd is repo-native and has been developed independently for a long time, but several files still describe it as a fork of upstream tree-sitter-markdown. This misleads agents auditing for vendored dependencies (see claude-notes/research/vendored-dependencies-inventory.md entry H). Files to fix: crates/tree-sitter-qmd/README.md (line 3 fork claim); crates/tree-sitter-qmd/package.json (name/version/author/repository all upstream values); crates/tree-sitter-qmd/tree-sitter.json (metadata still upstream); crates/tree-sitter-qmd/README.tree-sitter-md.md (verbatim upstream README — delete or mark historical); the nested crates/tree-sitter-qmd/tree-sitter-markdown/ directory implies vendoring by layout (rename optional). tree-sitter-doctemplate was checked and is fine.","status":"closed","priority":3,"issue_type":"chore","created_at":"2026-05-04T21:24:05.479792Z","created_by":"cscheid","updated_at":"2026-05-04T21:39:51.600399Z","closed_at":"2026-05-04T21:39:51.600240Z","close_reason":"Cleaned up stale fork framing in tree-sitter-qmd: README.md rewritten, tree-sitter.json metadata replaced, README.tree-sitter-md.md and package.json/lock deleted. Committed on .worktrees/7co9-fork-framing-cleanup. Other multi-language scaffolding (pyproject.toml, setup.py, go.mod, Package.swift, binding.gyp, CMakeLists.txt, Makefile, bindings/{go,node,python,swift}/) left as a possible follow-up.","source_repo":".","compaction_level":0,"original_size":0,"labels":["deps","docs"],"dependencies":[{"issue_id":"bd-7co9","depends_on_id":"bd-xm7l","type":"discovered-from","created_at":"2026-05-04T21:24:05.479792Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} +{"id":"bd-7eohf","title":"Phase 4 — hub-mcp device-flow primitives (initiate + pollOnce)","description":"New module ts-packages/quarto-hub-mcp/src/auth/device-flow.ts on oauth4webapi + jose. Two primitives — initiateDeviceFlow, pollDeviceFlowOnce — not a single blocking flow.\n\nTDD: 14-test fixture-driven suite (msw or undici MockAgent — never live Google), then implement. Centralised redact() at every log site; process-level uncaughtException handlers scrub Google-token-shaped substrings.\n\nClient auth: oauth.ClientSecretPost(secret). client_id and client_secret sourced only from process.env (QUARTO_HUB_MCP_CLIENT_ID / _CLIENT_SECRET).\n\nPlan §Phase 4: claude-notes/plans/2026-05-05-hub-mcp-device-flow-implementation.md","status":"open","priority":1,"issue_type":"task","created_at":"2026-05-20T14:27:05.380683Z","created_by":"shikokuchuo","updated_at":"2026-05-20T14:27:05.380683Z","source_repo":"kyoto","source_repo_path":"/Users/shikokuchuo/r/kyoto","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-7eohf","depends_on_id":"bd-cmp48","type":"parent-child","created_at":"2026-05-20T14:27:05.380683Z","created_by":"shikokuchuo","metadata":"{}","thread_id":""}]} {"id":"bd-7giz","title":"Add 'cargo xtask setup' for fresh clone/worktree bootstrap","description":"Fresh worktrees (and fresh clones) currently fail 'cargo xtask verify' because the hub-client TypeScript build depends on node_modules/ that isn't created by verify. Discovered while bootstrapping the issue-152 worktree on 2026-05-03 — verify ran the WASM build successfully, then hub-client's tsc step failed with 'Cannot find module' for every npm dep until 'npm install' was run manually from the worktree root.\n\nProposal: introduce 'cargo xtask setup' as a discrete bootstrap verb that runs 'npm install' (and any future first-run prerequisites like rustup component add). Then either:\n (a) have 'verify' detect a missing node_modules/ and exit with 'run cargo xtask setup first', or\n (b) have 'verify' call into the same setup logic when it detects first-run state.\n\nOther large Rust+JS monorepos (Deno, Tauri) keep these separate; rust-analyzer hard-fails with a hint. Keeping 'verify' semantically about checking (not network installs) seems preferable.\n\nCross-reference: this snag should also be documented in the upcoming triage/diagnose skill (.claude/skills/triage.md, not yet created) so that running the skill on a fresh worktree picks up bootstrap as a prerequisite step. Until 'cargo xtask setup' exists, the skill should explicitly run 'npm install' before 'cargo xtask verify'.","status":"open","priority":2,"issue_type":"task","created_at":"2026-05-03T16:21:43.847529Z","created_by":"cscheid","updated_at":"2026-05-12T12:29:01.546361500Z","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-7giz","depends_on_id":"bd-f3pl","type":"related","created_at":"2026-05-03T16:27:22.098662Z","created_by":"cscheid","metadata":"{}","thread_id":""}],"comments":[{"id":19,"issue_id":"bd-7giz","author":"cderv","text":"cargo xtask dev-setup already exists — installs cargo-nextest and wasm-bindgen-cli (pinned, via cargo-binstall fallback to cargo install --locked). Consider extending dev-setup to also run npm install from repo root rather than introducing parallel 'setup' verb. Would unify Rust tool bootstrap + JS dep bootstrap behind one entry point. Skill .claude/skills/triage.md step 4 currently runs npm install manually — would switch to cargo xtask dev-setup once landed.","created_at":"2026-05-04T15:18:55Z"},{"id":23,"issue_id":"bd-7giz","author":"chris","text":"Cross-ref from bd-spsv work (2026-05-12). `cargo xtask create-worktree`\nnow prints a per-worktree setup checklist on stdout that lists\n`cargo xtask dev-setup` and `npm install` as separate steps, with\n`npm install` flagged as \"only if hub-client work is in scope\". When\nthis issue lands (dev-setup also runs `npm install`), the create-worktree\noutput in `crates/xtask/src/create_worktree.rs::print_summary` should be\nupdated to drop the explicit `npm install` line. The relevant block to\nedit is the \"# Per worktree\" group inside print_summary.","created_at":"2026-05-12T12:29:01Z"}]} {"id":"bd-7h6a","title":"Per-page favicon override (meta.favicon beats website.favicon)","description":"User flagged 2026-04-27 as expected-soon. A document with its own meta.favicon should override website.favicon. Phase 7 deferred this; needs a small priority lookup in WebsiteFaviconTransform that prefers ast.meta.favicon over ast.meta.website.favicon. Sub-plan reference: claude-notes/plans/2026-04-27-websites-phase-7.md §Non-goals + §Follow-up beads. Originating phase: bd-b9mz.","status":"open","priority":3,"issue_type":"feature","created_at":"2026-04-27T15:02:58.364038Z","created_by":"cscheid","updated_at":"2026-04-27T15:02:58.364038Z","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-7h6a","depends_on_id":"bd-0tr6","type":"parent-child","created_at":"2026-04-27T15:02:58.364038Z","created_by":"cscheid","metadata":"{}","thread_id":""},{"issue_id":"bd-7h6a","depends_on_id":"bd-b9mz","type":"discovered-from","created_at":"2026-04-27T15:02:58.364038Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} {"id":"bd-7l1u","title":"Q-2-35: Reject 4-space indented code blocks with a custom parse error (issue #184)","description":"Indented (4-space) code blocks are currently parsed as paragraphs whose leading whitespace is dropped, corrupting documents on a qmd → qmd round-trip (GH issue #184). Per project policy (https://github.com/quarto-dev/q2/issues/184#issuecomment-4426182995) Quarto Markdown does not support indented code blocks; the parser must reject them with a high-quality error message instead.\n\nApproach: mirror the Q-2-32 (TRIPLE_STAR) pattern — emit an external token from scanner.c that is declared in grammar.js but consumed by no rule body, then map the resulting (state, sym) pair through the Merr-style error table in crates/quarto-parse-errors/ to a Q-2-35 diagnostic.\n\nPlan: claude-notes/plans/2026-05-14-q-2-35-indented-code-block-error.md (TDD, six error-corpus cases including a negative one, full xtask verify, no writer changes).\nTriage: claude-notes/issue-reports/184/triage.md\nBranch: issue-184 (worktree at .worktrees/issue-184/)\nRepro: claude-notes/issue-reports/184/repro.qmd\n\nScope confirmed with @cscheid (2026-05-14):\n- Error fires on >= 4 leftover indentation (after container matchers consume their share), so well-indented list continuations are unaffected but extra-indented ones are flagged.\n- Error code Q-2-35.\n- Message suggests fenced code blocks (```) as the replacement.\n- Documentation note added to tree-sitter-markdown CONTRIBUTING.md mirroring the Q-2-32 precedent.","status":"closed","priority":1,"issue_type":"bug","created_at":"2026-05-14T16:08:34.776338Z","created_by":"cscheid","updated_at":"2026-05-14T17:02:40.507132Z","closed_at":"2026-05-14T17:02:40.506948Z","close_reason":"Implemented and verified end-to-end on branch issue-184 (commit e2d224f6). Reporter's repro now produces Q-2-35 diagnostic spanning the full offending line. Full cargo xtask verify (Rust + WASM + hub-client) passes; tree-sitter test 480/480; pampa test suite 3687/3687; xtask lint clean. Conservative detection rule (ATX_H1_MARKER || BLANK_LINE_START) avoids false positives in multi-line shortcodes and lazy paragraph continuations — see the plan doc for the rationale and the rejected alternatives.","source_repo":".","compaction_level":0,"original_size":0} @@ -155,12 +159,16 @@ {"id":"bd-c083","title":"Cargo: upgrade tree-sitter v0.25.10 → v0.26.8","description":"Major upgrade surfaced by cargo-upgrade survey 2026-05-04. Current 0.25.10 is range-pinned in workspace; latest is 0.26.8. Type: pre-1.0 minor (semver-breaking). Review changelog and bump deliberately. See claude-notes/plans/2026-05-04-cargo-upgrade-survey.md and bd-hb8h.","status":"closed","priority":3,"issue_type":"chore","created_at":"2026-05-04T18:15:55.321199Z","created_by":"cscheid","updated_at":"2026-05-04T20:30:44.759296Z","closed_at":"2026-05-04T20:30:44.759137Z","close_reason":"merged: 61b01cd4","source_repo":".","compaction_level":0,"original_size":0,"labels":["cargo","deps"],"dependencies":[{"issue_id":"bd-c083","depends_on_id":"bd-hb8h","type":"discovered-from","created_at":"2026-05-04T18:16:05.692502Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} {"id":"bd-c3jh","title":"Phase 9 follow-up: GC stale VFS artifacts at session end","description":"When the hub-client preview re-renders, the WASM Pass-2 produces new artifact paths (theme-css fingerprints change when content changes) but the *old* artifacts under /.quarto/project-artifacts/... linger in VFS storage. The new HTML never references them — so they don't poison the page — but they do leak.\n\nGC pass at session-end (or periodically): walk /.quarto/project-artifacts/, drop any entry whose path doesn't appear in a 'live' set (the union of artifact paths from the most-recent project render).\n\nPhase 9 plan §Risks: 'Add a follow-up to GC /.quarto/project-artifacts/... entries with no live references at session end. Not a Phase-9 blocker.'","status":"open","priority":4,"issue_type":"task","created_at":"2026-04-29T00:32:31.194561Z","created_by":"cscheid","updated_at":"2026-04-29T00:32:31.194561Z","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-c3jh","depends_on_id":"bd-0tr6","type":"parent-child","created_at":"2026-04-29T00:32:31.194561Z","created_by":"cscheid","metadata":"{}","thread_id":""},{"issue_id":"bd-c3jh","depends_on_id":"bd-ayj6","type":"discovered-from","created_at":"2026-04-29T00:32:31.194561Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} {"id":"bd-cfl67","title":"q2 render truncates source images referenced in qmd documents","description":"ResourceCollectorTransform stores image source paths as artifacts with empty content; write_artifacts then opens the source path for writing and truncates it to 0 bytes. Confirmed via 'q2 render docs/authoring/markdown/index.qmd' which truncates docs/authoring/markdown/elephant.png from 126124 bytes to 0. Plan doc: claude-notes/plans/2026-05-20-render-truncates-source-images.md","status":"open","priority":0,"issue_type":"bug","created_at":"2026-05-21T00:53:50.747194Z","created_by":"cscheid","updated_at":"2026-05-21T00:53:50.747194Z","source_repo":".","compaction_level":0,"original_size":0,"labels":["bug","data-loss"]} +{"id":"bd-cjxlb","title":"Keyring error messages must not leak credential blob","description":"Plan §Phase 5 calls this out as a specifically-flagged risk: when CredentialStore.write/read/clear surfaces a backend error, the error message must NOT contain the credential blob (or any token-shaped substring).\n\nTest name: keyring_error_does_not_leak_blob_in_message in ts-packages/quarto-hub-mcp/test/auth/credential-store.test.ts.\n\nImplementation: wrap keyring errors via redact(err.message) at every site that touches a blob.\n\nPlan §Phase 5: claude-notes/plans/2026-05-05-hub-mcp-device-flow-implementation.md","status":"open","priority":1,"issue_type":"bug","created_at":"2026-05-20T14:27:50.747041Z","created_by":"shikokuchuo","updated_at":"2026-05-20T14:27:50.747041Z","source_repo":"kyoto","source_repo_path":"/Users/shikokuchuo/r/kyoto","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-cjxlb","depends_on_id":"bd-sfr5l","type":"parent-child","created_at":"2026-05-20T14:27:50.747041Z","created_by":"shikokuchuo","metadata":"{}","thread_id":""}]} +{"id":"bd-cmp48","title":"hub-mcp Google device-flow auth (Design C')","description":"Implements Design C' (Google as device-flow AS) for hub-mcp ↔ quarto-hub authentication over WebSocket. Hub-mcp persists Google ID + refresh tokens locally (OS keyring); hub keeps existing JWKS validator with audience allowlist.\n\nPlan: claude-notes/plans/2026-05-05-hub-mcp-device-flow-implementation.md\n\nPer the plan's Beads section: one issue per phase (1-10) parent-child to this epic, plus separate bug-tagged issues for the dual-credential CVE test (Phase 2), keyring-leak test (Phase 5), and canonical-URL constant test (Phase 7).","status":"open","priority":1,"issue_type":"epic","created_at":"2026-05-20T14:26:43.587791Z","created_by":"shikokuchuo","updated_at":"2026-05-20T14:26:43.587791Z","source_repo":"kyoto","source_repo_path":"/Users/shikokuchuo/r/kyoto","compaction_level":0,"original_size":0} {"id":"bd-coffj","title":"q2 preview: Div with class='section' emits
, not
— breaks Quarto's :has(+ section) margin rules","description":"The native HTML writer (crates/pampa/src/writers/html.rs:1129-1142) emits
...
for Pandoc Div blocks whose class list contains 'section'; the React Div component in ts-packages/preview-renderer/src/q2-preview/blocks/Div.tsx always emits
. Consequence: Quarto theme CSS rules keyed on the
tag (e.g. 'main.content > p:has(+ section) { margin-bottom: 2rem }') don't apply to preview, causing visible spacing drift. Concrete repro: the 'This is an extremely basic website' paragraph in the fixture has 17px bottom margin in preview vs 34px in render. Fix: mirror the native writer — emit
when classes contains SECTION ('section').","status":"closed","priority":2,"issue_type":"bug","created_at":"2026-05-18T17:53:40.764259Z","created_by":"cscheid","updated_at":"2026-05-18T17:58:07.422417Z","closed_at":"2026-05-18T17:58:07.422276Z","close_reason":"Implementation complete: Pandoc Divs with class='section' now render as
in q2 preview, matching the native HTML writer. Visible result: 'This is an extremely basic website' paragraph margin-bottom now 34px (was 17px), matching q2 render exactly. Quarto theme's main.content > p:has(+ section) selector now matches in preview. 2 new SPA tests; 152/152 green; cargo xtask verify 12/12 green.","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-coffj","depends_on_id":"bd-kw93","type":"parent-child","created_at":"2026-05-18T17:53:40.764259Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} {"id":"bd-cpzp","title":"qmd writer: implicit-figure path drops trailing newline, collapses next block (issue #180)","description":"Triage doc: claude-notes/issue-reports/180/triage.md on branch issue-180.\n\nRoot cause: write_figure (crates/pampa/src/writers/qmd.rs:759) implicit-figure branch returns directly from write_image without emitting the trailing \\n that every block writer is expected to produce. When such a Figure is followed by another block (top-level or inside a Div), the inter-block separator collapses to a single \\n instead of a blank line, and the re-parser glues the two blocks into one Para. Affects every layout/subfigure div in the docs corpus.\n\nReports covered (both same root cause):\n- Bug A: top-level Figure followed by Para -> one Para after round-trip\n- Bug B: Div with multiple Figure children + caption -> one Para inside the Div after round-trip\n\nFix (TDD-first per crates/pampa/CLAUDE.md):\n1. Add failing roundtrip tests under tests/roundtrip_tests/qmd-json-qmd/\n - figure_implicit_then_para.qmd\n - layout_div_subfigures.qmd\n - figure_implicit_then_figure.qmd (extra coverage)\n2. Verify they fail.\n3. Fix write_figure: replace 'return write_image(...)' with 'write_image(...)?; writeln!(buf)?; Ok(())'.\n4. Verify tests pass.\n5. cargo nextest run --workspace to catch downstream snapshot updates.\n\nNot a duplicate of bd-emr4 — that is about non-implicit Figure shapes hitting the fallback fenced-div path. This bug is in the implicit-figure code path; different code, different fix.","status":"open","priority":2,"issue_type":"bug","created_at":"2026-05-12T18:39:12.847875Z","created_by":"cscheid","updated_at":"2026-05-12T18:39:15.853892Z","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-cpzp","depends_on_id":"bd-emr4","type":"related","created_at":"2026-05-12T18:39:15.853594Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} +{"id":"bd-cqkts","title":"Phase 7 — hub-mcp MCP-tool exposure (authenticate_start / authenticate_finish)","description":"New module ts-packages/quarto-hub-mcp/src/auth/auth-tools.ts. Exposes two tools: authenticate_start (returns verification_uri, canonical_url, user_code, expires_in_seconds) and authenticate_finish (one poll per call, returns pending/slow_down/success/typed-error).\n\nCanonical URL https://www.google.com/device is a hard-coded module constant — never copied from Google's response. Sibling bug bd-XXXX tracks the canonical-URL-constant test specifically.\n\nRFC 8628 §3.5 rate-limiting: nextPollAllowedAt cached state, bumped 5 s per slow_down. Short-circuit on already-authenticated and lastObservedAuthMode === 'no-auth'.\n\nRegistered before registerTools so read/write tools can name authenticate_start in their error text.\n\nPlan §Phase 7: claude-notes/plans/2026-05-05-hub-mcp-device-flow-implementation.md","status":"open","priority":1,"issue_type":"task","created_at":"2026-05-20T14:27:19.527539Z","created_by":"shikokuchuo","updated_at":"2026-05-20T14:27:19.527539Z","source_repo":"kyoto","source_repo_path":"/Users/shikokuchuo/r/kyoto","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-cqkts","depends_on_id":"bd-cmp48","type":"parent-child","created_at":"2026-05-20T14:27:19.527539Z","created_by":"shikokuchuo","metadata":"{}","thread_id":""}]} {"id":"bd-creo","title":"quarto render: fail strictly on Pass-1 failures (CI-friendly contract)","description":"Sibling of bd-rqba. Once Pass-1 failures are wired through as a dedicated pass1_failures field on the render summary surfaces (D1 in plan), give 'quarto render' a strict policy: any pass1_failures entry causes a non-zero exit. Remove the current string-matching of 'profile-pass skipped …' warning text in favor of the structured field.\n\nRationale: 'quarto render' is often used in headless CI; partial-progress leniency belongs to 'quarto preview' / hub-client, not render. The engine stays policy-free; consumers choose strict (render) vs lenient (preview).\n\nAlso: document the strict-vs-lenient contract in claude-notes/designs/document-profile-contract.md so future consumers (e.g., the planned hub-client-based 'quarto preview' binary) inherit it.\n\nPlan: claude-notes/plans/2026-05-01-hub-client-website-render-ux.md (Decision D1).","status":"open","priority":1,"issue_type":"task","created_at":"2026-05-01T14:16:43.115104Z","created_by":"cscheid","updated_at":"2026-05-01T14:16:43.115104Z","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-creo","depends_on_id":"bd-lk66","type":"parent-child","created_at":"2026-05-01T14:16:43.115104Z","created_by":"cscheid","metadata":"{}","thread_id":""},{"issue_id":"bd-creo","depends_on_id":"bd-rqba","type":"related","created_at":"2026-05-01T14:16:43.115104Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} +{"id":"bd-cxara","title":"Phase 9 — end-to-end verification","description":"CRITICAL per CLAUDE.md: tests passing alone is insufficient. Phase 9 runs the full verification matrix and records exact invocations + observed output in the plan's Verification log.\n\nCoverage: full cargo xtask verify; SPA cookie-path regression; clean-machine device-flow E2E (with per-platform credential-store inspection); no-auth-hub regression + explicit-authenticate short-circuit + observation-requirement check; allowlist parity for cookie 403 and bearer 403 (byte-identical detail); insecure-bearer dev-mode interaction (loopback + non-loopback with/without env flag); force-refresh; force-reauth; dual-credential CVE + WS-upgrade-with-no-Origin sanity check; audit-log spot-check; plaintext-leak grep.\n\nPlan §Phase 9: claude-notes/plans/2026-05-05-hub-mcp-device-flow-implementation.md","status":"open","priority":1,"issue_type":"task","created_at":"2026-05-20T14:27:31.218633Z","created_by":"shikokuchuo","updated_at":"2026-05-20T14:27:31.218633Z","source_repo":"kyoto","source_repo_path":"/Users/shikokuchuo/r/kyoto","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-cxara","depends_on_id":"bd-cmp48","type":"parent-child","created_at":"2026-05-20T14:27:31.218633Z","created_by":"shikokuchuo","metadata":"{}","thread_id":""}]} {"id":"bd-d8fo","title":"q2-preview chrome: replace HTML injection with React components","description":"Phase F (q2-preview website chrome) ships Strategy A: include the *-render transforms (navbar-render, sidebar-render, page-nav-render, footer-render, toc-render) in the q2-preview pipeline so meta.rendered.navigation.* is populated with Bootstrap HTML; the React PreviewDocument injects via dangerouslySetInnerHTML.\n\nThis is the pragmatic first cut — shippable in days, byte-identical to q2 render. The tradeoff is DOM stability inside the chrome: open dropdowns, sidebar collapse state, scroll position inside the sidebar, etc., are blown away every time the chrome re-renders. In practice the chrome only re-renders on _quarto.yml edits, page switches, or sidebar reorderings — uncommon during normal authoring — so the cost is bounded.\n\nThis follow-up tracks the proper Strategy B: replace the HTML-injection path with React components that render from the structured meta.navigation.* (the ConfigValue form populated by the *-generate transforms before *-render runs). Each component mirrors the Bootstrap HTML the corresponding render transform emits.\n\nWhat this unlocks:\n- Stateful chrome (open Bootstrap dropdowns, collapsed sidebar sections, scroll position inside the sidebar) survives chrome re-renders.\n- React owns the click handlers natively rather than going through iframePostProcessor's after-the-fact .qmd-link rewriting.\n- The drift risk between server HTML emission and React component output goes away (the React components ARE the renderer; q2-render's HTML stays the source of truth for non-preview pipelines).\n\nScope:\n- Mirror Navbar / Sidebar / PageNav / Footer / Toc / Breadcrumbs from the Bootstrap HTML the *-render transforms emit today.\n- TypeScript types matching the structured ConfigValue shape each *-generate transform writes.\n- Remove the corresponding entries from Q2_PREVIEW_TRANSFORM_EXCLUDED in pipeline.rs — those transforms become unused in the preview pipeline, so the *-render Rust modules either stay (for q2 render) or get factored to a shared HTML emitter consumed only by the q2 render pipeline.\n- Snapshot tests asserting the React components emit markup matching the *-render Rust HTML output.\n\nFiled during Phase F planning (2026-05-14). Parent edge to the F epic to be added once F itself is filed.","status":"open","priority":3,"issue_type":"feature","created_at":"2026-05-14T20:36:07.754117Z","created_by":"cscheid","updated_at":"2026-05-14T20:52:21.580312Z","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-d8fo","depends_on_id":"bd-kw93.15","type":"discovered-from","created_at":"2026-05-14T20:52:21.579976Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} {"id":"bd-d8go","title":"L9 follow-up: date_format doctemplate pipe","description":"Originally specified as part of L9's L4 enhancements; deferred at impl-start because L9's RFC 822 pubDate is computed server-side in feed/binding.rs (no template-level need). Adding the pipe later requires a tree-sitter grammar change in crates/tree-sitter-doctemplate/grammar/grammar.js (the pipe set is grammar-fixed; the existing date_format must be a custom rule like pipe_left/pipe_center/pipe_right because it takes an argument), plus a match arm in crates/quarto-doctemplate/src/pipes.rs. Useful for L8's existing custom-template authors who want to format dates in the host page.","status":"open","priority":3,"issue_type":"feature","created_at":"2026-05-08T17:33:55.509676Z","created_by":"cscheid","updated_at":"2026-05-08T17:33:55.509676Z","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-d8go","depends_on_id":"bd-o90m","type":"discovered-from","created_at":"2026-05-08T17:33:55.509676Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} -{"id":"bd-dhtw","title":"Phase 1: gh-pages provider end-to-end","description":"Phase 1 of bd-t3ny. Implement the gh-pages provider end-to-end on top of the Phase 0 scaffolding: common::git wrappers, common::github context discovery, _publish.yml reader, GhPagesProvider (prepare/commit/verify with .nojekyll poll), --dry-run cleanup with no residue, mkdocs-style summary, and an end-to-end test against a bare local remote (dry-run + real-run + json-run).\n\nPlan: claude-notes/plans/2026-05-03-publish-command-and-gh-pages.md (Phase 1 section)\n\nBlocked on Phase 0 (bd-068k).","status":"completed","priority":1,"issue_type":"task","created_at":"2026-05-03T14:37:39.274734Z","created_by":"cscheid","updated_at":"2026-05-03T15:18:38.648426Z","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-dhtw","depends_on_id":"bd-068k","type":"blocks","created_at":"2026-05-03T14:37:39.274734Z","created_by":"cscheid","metadata":"{}","thread_id":""},{"issue_id":"bd-dhtw","depends_on_id":"bd-t3ny","type":"parent-child","created_at":"2026-05-03T14:37:39.274734Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} +{"id":"bd-dhtw","title":"Phase 1: gh-pages provider end-to-end","description":"Phase 1 of bd-t3ny. Implement the gh-pages provider end-to-end on top of the Phase 0 scaffolding: common::git wrappers, common::github context discovery, _publish.yml reader, GhPagesProvider (prepare/commit/verify with .nojekyll poll), --dry-run cleanup with no residue, mkdocs-style summary, and an end-to-end test against a bare local remote (dry-run + real-run + json-run).\n\nPlan: claude-notes/plans/2026-05-03-publish-command-and-gh-pages.md (Phase 1 section)\n\nBlocked on Phase 0 (bd-068k).","status":"closed","priority":1,"issue_type":"task","created_at":"2026-05-03T14:37:39.274734Z","created_by":"cscheid","updated_at":"2026-05-03T15:18:38.648426Z","closed_at":"2026-05-03T15:18:38.648426Z","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-dhtw","depends_on_id":"bd-068k","type":"blocks","created_at":"2026-05-03T14:37:39.274734Z","created_by":"cscheid","metadata":"{}","thread_id":""},{"issue_id":"bd-dhtw","depends_on_id":"bd-t3ny","type":"parent-child","created_at":"2026-05-03T14:37:39.274734Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} {"id":"bd-djpt","title":"Ship Bootstrap Icons font + CSS with Quarto 2 HTML output","description":"Quarto 2 HTML renders Bootstrap-icon markup (``) for navbar tools and footer nav items, but the Bootstrap Icons package (font file + icon CSS that maps class names to glyphs) is not shipped, so the icons render as empty boxes. Quarto 1 handles this via a separate bootstrap-icons dependency.\n\nScope: vendor vs CDN decision (prefer vendor, to match SCSS strategy); where the font file lives in artifact storage; template link (``); interaction with the planned JS-deps mechanism (bd-ulgr).\n\nCompanion to bd-ulgr (Bootstrap JS). Both issues concern static Bootstrap-ecosystem resources that Q2 needs to ship to match Q1 UX; worth designing them together.\n\nSurfaced during bd-imiw visual testing — navbar/footer DOM is correct, icons are invisible.","status":"open","priority":2,"issue_type":"feature","created_at":"2026-04-18T20:00:13.337791Z","created_by":"cscheid","updated_at":"2026-04-18T20:00:13.337791Z","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-djpt","depends_on_id":"bd-imiw","type":"discovered-from","created_at":"2026-04-18T20:00:13.337791Z","created_by":"cscheid","metadata":"{}","thread_id":""},{"issue_id":"bd-djpt","depends_on_id":"bd-ulgr","type":"related","created_at":"2026-04-18T20:00:13.337791Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} {"id":"bd-dsco4","title":"Port _brand.yml integration to Q2 Typst format once available","description":"Q1 has src/resources/filters/quarto-post/typst-brand-yaml.lua. Q2's typst format is too immature for this today; revisit when typst-format work picks up.","status":"open","priority":4,"issue_type":"feature","created_at":"2026-05-21T02:31:31.019793Z","created_by":"cscheid","updated_at":"2026-05-21T02:31:31.019793Z","source_repo":".","compaction_level":0,"original_size":0} {"id":"bd-dy97y","title":"D7: Default HTML format ships Bootstrap-derived stylesheet","description":"## What\n\n`q2 render`'s default HTML format ships a minimal `tables_files/styles.css` (from `crates/quarto-core/resources/styles.css`). For tables to actually look styled even with `class=\"caption-top table\"` applied, the CSS bundle must include Bootstrap's `.table` and `.caption-top` rules.\n\nThe project already has Bootstrap SCSS sources at `resources/scss/bootstrap/`. This task wires the SCSS compilation/loading into the default HTML format so the produced HTML links a stylesheet containing those rules.\n\n## Scope\n\nJust enough to make a default render's tables look like Q1's tables. Custom themes, dark mode toggle, font scales etc. are out of scope.\n\n## Tests\n\nEnd-to-end test via `render_document_to_file` (per CLAUDE.md end-to-end requirement) that renders `tables.qmd` and asserts the produced HTML links a stylesheet whose contents contain `.table` selector + the expected rules (border, padding, font-weight on th).\n\nVisual re-verification in Chrome required before closing.\n\n## Plan\n\nclaude-notes/plans/2026-05-20-table-default-rendering-parity.md (D7 section). Phase 2 work — blocks on D1-D6 only for visual verification, not for code.","status":"closed","priority":2,"issue_type":"feature","created_at":"2026-05-20T20:55:25.109513Z","created_by":"cscheid","updated_at":"2026-05-20T22:51:48.063407Z","closed_at":"2026-05-20T22:51:48.063242Z","close_reason":"No code change needed. CompileThemeCssStage already ships the full Bootstrap 5.3.1 bundle as _files/styles.css for default renders. The original 'unstyled table' symptom was 100% markup-side — once D1/D2 added the caption-top/table class hooks and thead/th promotion, the existing CSS styled the table identically to Quarto 1. Visual verification 2026-05-20 confirms byte-identical computed styles to Q1.","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-dy97y","depends_on_id":"bd-hir7j","type":"parent-child","created_at":"2026-05-20T20:55:25.109513Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} @@ -173,6 +181,7 @@ {"id":"bd-elgxx","title":"D4/D5 react: preview Table.tsx emits header/odd/even row classes","description":"## What\n\nD4/D5 in pampa's HTML writer (closed as bd-12fpz, commit c02a405a) emit ``, ``, `` on rendered HTML. `q2 preview` does not benefit because it doesn't go through the pampa HTML writer — its iframe renders directly from AST JSON via the React Table component at `ts-packages/preview-renderer/src/q2-preview/blocks/Table.tsx`. That component currently emits bare ``.\n\n## Fix\n\nIn Table.tsx, extend `RowNode` to accept the row's class (\"header\" / \"odd\" / \"even\" / null). Caller (`Table`) computes it based on:\n- head rows: always \"header\"\n- body rows: alternating \"odd\" / \"even\" per TableBody, starting at \"odd\"\n- foot rows: null (no class)\n\nMirrors the pampa writer change (bd-12fpz) exactly.\n\n## Tests\n\nVitest test for Table.tsx that builds a minimal Table AST and asserts on the rendered DOM.\n\n## Plan\n\nclaude-notes/plans/2026-05-20-table-default-rendering-parity.md — discovered during preview e2e verification on the table-rendering-parity epic.","status":"closed","priority":3,"issue_type":"bug","created_at":"2026-05-20T22:38:08.670498Z","created_by":"cscheid","updated_at":"2026-05-20T22:42:26.405667Z","closed_at":"2026-05-20T22:42:26.405504Z","close_reason":"Implemented in this commit: row classes emitted in React Table component, mirroring pampa writer.","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-elgxx","depends_on_id":"bd-hir7j","type":"parent-child","created_at":"2026-05-20T22:38:08.670498Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} {"id":"bd-emr4","title":"qmd writer/reader: explicit Figure shapes don't round-trip (caption ≠ alt, multi-block content, etc.)","description":"Discovered while implementing bd-f5qd. The implicit-figure round-trip is fixed (write_figure now detects the implicit shape and emits bare image syntax). Figures with shapes other than the implicit one have no syntax in qmd that round-trips back to a Figure: the writer's fallback emits a fenced `::: {}` div with the caption as a sibling block, and the reader has no rule that turns that back into a Figure.\n\nConcrete minimal repro (caption text differs from image alt):\n\n $ cat > /tmp/explicit_fig.json <<'EOF'\n {\n \"pandoc-api-version\": [1, 23, 1],\n \"meta\": {},\n \"blocks\": [{\"t\":\"Figure\",\"c\":[\n [\"\",[],[]],\n [null,[{\"t\":\"Plain\",\"c\":[{\"t\":\"Str\",\"c\":\"A different caption.\"}]}]],\n [{\"t\":\"Plain\",\"c\":[{\"t\":\"Image\",\"c\":[[\"\",[\"lightbox\"],[]],[{\"t\":\"Str\",\"c\":\"Image alt\"}],[\"image.png\",\"\"]]}]}]\n ]}]\n }\n EOF\n $ cat /tmp/explicit_fig.json | cargo run --bin pampa -- -f json -t qmd\n ::: {}\n\n ![Image alt](image.png){.lightbox}\n\n A different caption.\n\n :::\n\n $ cat /tmp/explicit_fig.json | cargo run --bin pampa -- -f json -t qmd | cargo run --bin pampa -- -t native\n [ Div ( \"\" , [] , [] ) [\n Figure ( \"\" , [] , [] ) (Caption Nothing [Plain[Str \"Image\",Space,Str \"alt\"]]) [...],\n Para [Str \"A\",Space,Str \"different\",Space,Str \"caption.\"]\n ] ]\n\nThe original caption is gone (replaced by the implicit-figure rule firing on\nthe inner image-only paragraph, which copies the alt text into the caption).\nThe original caption text becomes a free-standing sibling Para. The outer\nDiv wraps the whole thing.\n\nOther shapes that hit the same fallback:\n- Figure with caption.short set\n- Figure with multiple content blocks\n- Figure with classes or kvs on the figure itself (not just the image)\n- Figure whose content isn't a single Plain[Image]\n\nFix needs reader-side support: design a qmd syntax for explicit figures and\nadd a reader rule that converts it back to a Figure block. Likely shape:\nfenced div with a recognizable class/id marker plus a convention for which\nblock is the caption (TS Quarto uses crossref-style `#fig-id` divs).\n\nFor now the writer's fallback path stays as-is; users get a structurally\nincorrect div until this is fixed.","status":"open","priority":2,"issue_type":"bug","created_at":"2026-05-01T00:49:16.022173Z","created_by":"cscheid","updated_at":"2026-05-01T00:49:48.575674Z","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-emr4","depends_on_id":"bd-f5qd","type":"discovered-from","created_at":"2026-05-01T00:49:16.022173Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} {"id":"bd-expy","title":"Pipe table parser commits to caption when ':::' follows a row (issue #206)","description":"Parse error when a fenced-div close `:::` directly follows a pipe table row without an intervening blank line. The first `:` of `:::` is shifted by the parser as the start of a `caption` rule (the caption first-token literal is `:`); once shifted, tree-sitter cannot back out, and the second `:` produces 'unexpected character or token here'.\n\nTriage doc: claude-notes/issue-reports/206/triage.md\nWorktree branch: issue-206\nGitHub issue: https://github.com/quarto-dev/q2/issues/206\nReporter: @rundel\nReal-world example: quarto-dev/quarto-web docs/websites/website-navigation.qmd L155-L159\n\nConfirmed not a fenced-div bug — bare pipe table + `:::` reproduces identically. The fenced div in the reporter's example is incidental.\n\nRecommended fix shape: introduce an external `_caption_start` scanner token that only matches a `:` not immediately followed by another `:` (and followed by inline whitespace). Re-key the `caption` rule on `_caption_start` instead of the literal `:`. Add tree-sitter corpus tests for `:::`-after-table, `: caption`-after-table, bare `:::` after table, and a pampa round-trip test for the repro fixture. Re-test on quarto-web after fix.\n\nOut of scope here, file separately if desired: pipe-table parser also silently absorbs trailing headings/paragraphs as one-cell rows when no blank line intervenes (see triage doc 'Adjacent finding').","status":"closed","priority":2,"issue_type":"bug","created_at":"2026-05-15T18:48:39.090203Z","created_by":"cscheid","updated_at":"2026-05-15T21:35:28.981270Z","closed_at":"2026-05-15T21:35:28.981068Z","close_reason":"Fixed by PR #208 (merged as f0a1fd53)","source_repo":".","compaction_level":0,"original_size":0} +{"id":"bd-f0h89","title":"Phase 3 — Audit logging (minimal v1)","description":"Inline tracing::event! on auth_ok / auth_fail carrying sub, credential_kind, action, outcome, plus detail on failure. Gate is Phase 2's three audit tests + redaction test.\n\nSchema-lock, dedicated audit.rs module, jti correlation, OTEL conventions, and the doc deferred to a follow-up plan.\n\nPlan §Phase 3: claude-notes/plans/2026-05-05-hub-mcp-device-flow-implementation.md","status":"open","priority":2,"issue_type":"task","created_at":"2026-05-20T14:27:00.051312Z","created_by":"shikokuchuo","updated_at":"2026-05-20T14:27:00.051312Z","source_repo":"kyoto","source_repo_path":"/Users/shikokuchuo/r/kyoto","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-f0h89","depends_on_id":"bd-cmp48","type":"parent-child","created_at":"2026-05-20T14:27:00.051312Z","created_by":"shikokuchuo","metadata":"{}","thread_id":""}]} {"id":"bd-f3jc","title":"[websites phase 0] Foundations: snapshot type, pipeline checkpoint, naming","description":"First phase of website epic. Plan: claude-notes/plans/2026-04-23-website-project-epic.md § Phase 0.\n\nDeliverables:\n- Final names for the static-document snapshot type (working name DocumentProfile), the ProjectType trait, and the checkpoint stage.\n- Typed snapshot struct, serde-serializable.\n- PipelineData checkpoint variant with Clone/serialization.\n- Round-trip serialization tests + a clone-and-resume test that produces byte-identical output to end-to-end.\n- Decide crate placement (quarto-core vs new quarto-project crate).\n- Decide checkpoint position: after merge, or after merge + pre-engine sugaring.\n- Documentation of the static contract: what is guaranteed, under what conditions.\n\nNo user-visible behavior change in this phase.","status":"closed","priority":1,"issue_type":"task","created_at":"2026-04-23T18:42:37.750604Z","created_by":"cscheid","updated_at":"2026-04-23T20:53:23.807873Z","closed_at":"2026-04-23T20:53:23.807149Z","close_reason":"DocumentProfile type + checkpoint stage + UnwrapProfile stage shipped. Resumability verified: clone-and-resume test produces byte-identical HTML to end-to-end; 3 real-fixture CLI renders byte-identical pre/post-change (MD5 match). 7654/7654 workspace tests + cargo xtask verify pass. Commit b8b72fc2 on feature/websites-phase-0. Contract doc at claude-notes/designs/document-profile-contract.md.","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-f3jc","depends_on_id":"bd-0tr6","type":"parent-child","created_at":"2026-04-23T18:42:37.750604Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} {"id":"bd-f3pl","title":"qmd writer drops table caption attributes (issue #152)","description":"GitHub: https://github.com/quarto-dev/q2/issues/152\nTriage: claude-notes/issue-reports/152/triage.md\nWorktree: .worktrees/issue-152 (branch issue-152, based on main @ 132c13c8)\n\nThe pipe-table branch of write_table (crates/pampa/src/writers/qmd.rs:1120-1239) ignores Table.attr entirely. A caption-attached attribute block like ': caption {tbl-colwidths=\"[30,70]\"}' is parsed correctly into Table.attr.2 (verified via 'cargo run --bin pampa --' on claude-notes/issue-reports/152/repro.qmd) but is dropped by 'pampa -t qmd', producing a lossy round-trip.\n\nThe list-table branch (write_list_table at lines 928-1118) already handles id/classes/keyvals correctly. The pipe-table branch needs analogous handling at the caption emission point (lines 1217-1235): after writing ': ', emit ' {}' via the existing write_attr helper (qmd.rs:396) when is_empty_attr(&table.attr) is false.\n\nOpen questions resolved during triage:\n 1. Empty-id auto-suppression NOT NEEDED. The reader (pipe_table.rs:148-200) only sets Table.attr.0 from explicit {#id} in the caption attr block; no implicit tbl-foo numbering exists.\n 2. Caption-suffix is the only valid placement for table attributes. Verified against pandoc 3.9.0.2: prefix form '{attrs}table: caption' is not parsed as a table by pandoc OR pampa (pampa raises Q-0-99 + Q-3-32). Suffix form ': caption {attrs}' produces byte-identical AST in both engines, including the mixed id+classes+keyvals shape.\n\nFix workflow per crates/pampa/CLAUDE.md:\n 1. Add failing fixtures under crates/pampa/tests/roundtrip_tests/qmd-json-qmd/ (table-caption-with-keyval.qmd, table-caption-with-id.qmd, table-caption-with-classes.qmd, table-caption-with-mixed-attrs.qmd).\n 2. Verify each fixture fails the round-trip equality check.\n 3. Implement: emit write_attr(&table.attr, buf, ctx) after the caption text (qmd.rs:1228-1234), guarded by !is_empty_attr(&table.attr). No id-suppression guard needed.\n 4. Verify new fixtures pass and existing table-caption.qmd snapshot is unchanged (its Table.attr is empty).\n 5. cargo xtask verify --skip-hub-build (Rust-only, no quarto-core/pandoc-types touched).\n\nExisting parser-side snapshot covering this fixture: crates/pampa/tests/snapshots/json/table-caption-attr.qmd. Read-side contract: docs/syntax/desugaring/table-captions.qmd.","status":"closed","priority":1,"issue_type":"bug","created_at":"2026-05-03T16:27:17.949705Z","created_by":"cscheid","updated_at":"2026-05-03T17:12:42.027337Z","closed_at":"2026-05-03T17:12:42.027211Z","close_reason":"Fixed in PR #154 / commit 688ac678. Pipe-table writer now appends Table.attr via write_attr() on the caption line; 4 round-trip fixtures cover id, classes, keyval, and mixed-attr shapes.","source_repo":".","compaction_level":0,"original_size":0} {"id":"bd-f5qd","title":"qmd writer: Figure node emits empty div wrapper and duplicates caption","description":"From issue #150 (item 2): The qmd writer for Figure nodes produces an empty `::: {}` div wrapper and emits the caption text after the image as a separate paragraph, duplicating it. Round-tripping qmd -> ast -> qmd -> ast produces different ASTs.\n\nRepro:\n printf '![Webpage](image.png){.lightbox}\\n' | cargo run --bin pampa -- -t qmd\n ::: {}\n\n ![Webpage](image.png){.lightbox}\n\n Webpage\n\n :::\n\nExpected: round-trip should be stable; for an Image with caption inside a Figure with no extra attrs, output should likely just be the bare image syntax (`![Webpage](image.png){.lightbox}`) since pandoc auto-wraps it in a Figure. Plan in claude-notes/plans/ to follow.","notes":"Plan: claude-notes/plans/2026-04-30-figure-qmd-roundtrip.md\nDiscussion needed before implementing — see plan's 'Proposed fix — discussion needed' section.","status":"closed","priority":1,"issue_type":"bug","created_at":"2026-05-01T00:01:55.558223Z","created_by":"cscheid","updated_at":"2026-05-01T00:51:41.517422Z","closed_at":"2026-05-01T00:51:41.517262Z","close_reason":"Fixed implicit-figure round-trip via match_implicit_figure_shape in write_figure. When Figure shape matches what the reader's implicit-figure rule produces (single Plain[Image] content, caption=alt, attr split between figure id and image classes/kvs), the writer now emits bare image syntax. Non-implicit Figure shapes continue to fall through to the existing fenced-div form which does not round-trip — tracked in bd-emr4 with concrete repro. See claude-notes/plans/2026-04-30-figure-qmd-roundtrip.md.","source_repo":".","compaction_level":0,"original_size":0} @@ -207,7 +216,6 @@ {"id":"bd-hp3tx","title":"Wire brand navbar logo / brand image into website navbar","description":"Brand data has logo (small/medium/large + images.*). Website navbar should be able to use brand.small or brand.images. as the navbar brand-image. Quarto 1 reference: external-sources/quarto-cli/src/project/types/website/website-navigation.ts (navbar.logo). Q2 has the data model in place (quarto-brand crate); just needs the navbar emission to consult it.","status":"open","priority":3,"issue_type":"feature","created_at":"2026-05-21T02:31:30.944971Z","created_by":"cscheid","updated_at":"2026-05-21T02:31:30.944971Z","source_repo":".","compaction_level":0,"original_size":0} {"id":"bd-ht0n","title":"[websites] Sidebar logo / subtitle / header / footer","description":"Render the sidebar's logo (with light/dark variants + logo-href + logo-alt), subtitle (parsed but not rendered in Phase 2), and the 'header:' / 'footer:' freeform content slots Q1 supports. All are parsed by Sidebar::from_config_value today but ignored in sidebar_to_html.","status":"open","priority":2,"issue_type":"feature","created_at":"2026-04-24T17:52:25.088205Z","created_by":"cscheid","updated_at":"2026-04-24T17:52:25.088205Z","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-ht0n","depends_on_id":"bd-9svl","type":"discovered-from","created_at":"2026-04-24T17:52:25.088205Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} {"id":"bd-hva0","title":"Local-vendor opt-in for MathJax/KaTeX (offline rendering)","description":"bd-w5ov shipped CDN-default for math (parity with Pandoc / Quarto 1). Users who need offline / air-gapped rendering must today set 'html-math-method.url:' pointed at a self-hosted mirror.\n\nMake this easier: ship a 'quarto install mathjax' (and 'quarto install katex') CLI helper that downloads the bundle into a project resources dir and writes the URL override into _quarto.yml. This is 'we're better than Q1' territory — Q1 offers no equivalent automation.\n\nOut of scope for this issue: vendoring bytes by default in the binary (rejected in bd-w5ov §4.5 because of binary-size cost and parity with Q1).\n\nAcceptance: 'quarto install mathjax' downloads, lays out under project resources, configures override; 'quarto render' uses the local URL; offline rendering works.","status":"open","priority":2,"issue_type":"feature","created_at":"2026-05-04T23:58:29.291389Z","created_by":"cscheid","updated_at":"2026-05-04T23:58:29.291389Z","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-hva0","depends_on_id":"bd-w5ov","type":"discovered-from","created_at":"2026-05-04T23:58:29.291389Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} -{"id":"bd-hytl","title":"Phase B.4: Acceptance bundle — _quarto.yml + posts/_metadata.yml propagation to preview","description":"Single Playwright spec pinning the two observable plan acceptance criteria for q2 preview (Phase B):\n\n1. Editing _quarto.yml (title:) re-renders the active page; new title visible in DOM within 5 s.\n2. Editing posts/_metadata.yml (subtitle:) re-renders pages under posts/; new subtitle visible in DOM within 5 s.\n\nFixture (single, dual-purpose): _quarto.yml + posts/_metadata.yml + posts/post1.qmd (no frontmatter, inherits both). Empirically verified both knobs flow through to the rendered title-block (2026-05-13 probe with target/debug/q2 render).\n\nReuses the multi-file fixture helper generalised in bd-pf63 (B.3).\n\nPlan acceptance criterion 3 ('unrelated sibling re-renders the active page') is deferred and tracked separately — its relaxed-contract form (any edit fires a re-render) is invisible at the DOM without SPA instrumentation. See discovered-from follow-up.\n\nPlan: claude-notes/plans/2026-05-13-q2-preview-phase-b.md §B.4.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-05-13T21:11:22.459798Z","created_by":"cscheid","updated_at":"2026-05-13T21:16:15.516971Z","closed_at":"2026-05-13T21:16:15.516850Z","close_reason":"Duplicate of bd-mrx1 — created by an accidental double-run during B.4 setup (jq parse error masked the first create's output). No work done against this ID; all B.4 work tracked under bd-mrx1.","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-hytl","depends_on_id":"bd-kw93","type":"parent-child","created_at":"2026-05-13T21:11:22.459798Z","created_by":"cscheid","metadata":"{}","thread_id":""},{"issue_id":"bd-hytl","depends_on_id":"bd-pf63","type":"discovered-from","created_at":"2026-05-13T21:11:22.459798Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} {"id":"bd-hzsi","title":"L10 — Q1 → Q2 listing template migration docs + LLM skill","description":"User-facing migration doc in docs/ covering EJS → doctemplate mapping (<%= … %> → $…$, control flow, helper-function → server-pre-rendered fields, item.extra). LLM skill in .claude/skills/ that suggests Q2 doctemplate equivalents from Q1 EJS templates. Worked examples for each built-in shape and a representative custom template. See claude-notes/plans/2026-05-05-listings-epic.md §L10.","status":"open","priority":2,"issue_type":"task","created_at":"2026-05-05T19:53:59.836077Z","created_by":"cscheid","updated_at":"2026-05-05T19:53:59.836077Z","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-hzsi","depends_on_id":"bd-61cd","type":"parent-child","created_at":"2026-05-05T19:53:59.836077Z","created_by":"cscheid","metadata":"{}","thread_id":""},{"issue_id":"bd-hzsi","depends_on_id":"bd-rqgx","type":"blocks","created_at":"2026-05-05T19:53:59.836077Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} {"id":"bd-i1df","title":"Kanban: Card detail view on title click","description":"Clicking a card title opens a detail modal showing all card information: title, type, status, created/deadline dates, priority, full body text. Plan: claude-notes/plans/2026-02-11-kanban-ui-enhancements.md","status":"closed","priority":2,"issue_type":"feature","created_at":"2026-02-11T18:32:37.650902Z","created_by":"cscheid","updated_at":"2026-02-11T18:36:06.274831Z","closed_at":"2026-02-11T18:36:06.274814Z","close_reason":"Implemented - card detail modal on title click","source_repo":".","compaction_level":0,"original_size":0} {"id":"bd-i4wv","title":"L9 follow-up: real version string in feed generator (replace quarto-2)","description":"L9 v1 emits quarto-2. Q1 emits quarto- from quartoConfig.version(). Replace with the actual Q2 version string when the version story stabilizes. Site: feed/binding.rs::FEED_GENERATOR.","status":"open","priority":4,"issue_type":"task","created_at":"2026-05-08T17:33:39.786912Z","created_by":"cscheid","updated_at":"2026-05-08T17:33:39.786912Z","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-i4wv","depends_on_id":"bd-o90m","type":"discovered-from","created_at":"2026-05-08T17:33:39.786912Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} @@ -274,12 +282,14 @@ {"id":"bd-ml8z","title":"L3 — ListingResolveTransform (Pass-2, built-ins via doctemplate)","description":"New Pass-2 transform inside AstTransformsStage. Reads host's listing: config + ProjectIndex; resolves contents: globs; filters/sorts; truncates by max-items. Builds per-item TemplateValue::Map with curated profile fields, server-pre-rendered helpers (image_html, metadata_attrs), and listing_item.extra. Renders via doctemplate against built-in default/grid/table templates embedded with MemoryResolver. Emits placeholders for L7 alongside L1 fallback content. See claude-notes/plans/2026-05-05-listings-epic.md §L3.","status":"closed","priority":1,"issue_type":"feature","created_at":"2026-05-05T19:53:22.859719Z","created_by":"cscheid","updated_at":"2026-05-07T13:35:17.550573Z","closed_at":"2026-05-07T13:35:17.550209Z","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-ml8z","depends_on_id":"bd-61cd","type":"parent-child","created_at":"2026-05-05T19:53:22.859719Z","created_by":"cscheid","metadata":"{}","thread_id":""},{"issue_id":"bd-ml8z","depends_on_id":"bd-b5jm","type":"blocks","created_at":"2026-05-05T19:53:22.859719Z","created_by":"cscheid","metadata":"{}","thread_id":""},{"issue_id":"bd-ml8z","depends_on_id":"bd-izqh","type":"blocks","created_at":"2026-05-05T19:53:22.859719Z","created_by":"cscheid","metadata":"{}","thread_id":""},{"issue_id":"bd-ml8z","depends_on_id":"bd-j60g","type":"blocks","created_at":"2026-05-05T19:53:22.859719Z","created_by":"cscheid","metadata":"{}","thread_id":""},{"issue_id":"bd-ml8z","depends_on_id":"bd-n8a4","type":"blocks","created_at":"2026-05-05T19:53:22.859719Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} {"id":"bd-mlj6","title":"Conditional render lists / _quarto-*.yml profiles","description":"Q1 supports profile-specific renders via _quarto-.yml (e.g. _quarto-prod.yml vs _quarto-dev.yml). Phase 1 of the website epic ignores profile files entirely — discovery excludes them because of the leading underscore, and config parsing only reads _quarto.yml. Follow-up: decide how profiles compose with the base config, add CLI flag for selecting profile, thread through ProjectContext::discover. See claude-notes/plans/2026-04-23-websites-phase-1.md §Decisions log.","status":"open","priority":3,"issue_type":"feature","created_at":"2026-04-24T01:05:24.479391Z","created_by":"cscheid","updated_at":"2026-04-24T01:05:24.479391Z","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-mlj6","depends_on_id":"bd-w5os","type":"discovered-from","created_at":"2026-04-24T01:05:24.479391Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} {"id":"bd-mot7","title":"qmd reader drops whitespace between code_span and html_element (issue #182)","description":"(see triage doc)","status":"closed","priority":1,"issue_type":"bug","created_at":"2026-05-12T13:42:59.836284Z","created_by":"cscheid","updated_at":"2026-05-12T13:43:26.216838Z","closed_at":"2026-05-12T13:43:26.216709Z","close_reason":"Duplicate, accidental second create. Keeping bd-nkx4.","source_repo":".","compaction_level":0,"original_size":0} +{"id":"bd-mqa4j","title":"Phase 8 — quarto-sync-client header pass-through + connection-manager integration","description":"Add auth?.getBearer option to client.connect(); new Node-only NodeWebSocketClientAdapter inside quarto-sync-client that constructs new WebSocket(url, [], { headers }). Browser path unchanged.\n\nConnection-manager try-then-fallback policy: read bundle, attempt WS with Bearer if present, on 401-with-creds forceRefresh+retry-once then ReauthRequired, on 401-without-creds AuthRequired.\n\nlastObservedAuthMode state machine ({no-auth, requires-auth, unknown}, process-local) drives Phase 7's short-circuit.\n\nInsecure-Bearer gate: refuse to send Bearer over plain HTTP/WS to non-loopback without QUARTO_HUB_MCP_ALLOW_INSECURE_AUTH=1; loud warning on every connect when set.\n\nFollow-up: upstream PR to thread headers through BrowserWebSocketClientAdapter — file separately.\n\nPlan §Phase 8: claude-notes/plans/2026-05-05-hub-mcp-device-flow-implementation.md","status":"open","priority":1,"issue_type":"task","created_at":"2026-05-20T14:27:25.451968Z","created_by":"shikokuchuo","updated_at":"2026-05-20T14:27:25.451968Z","source_repo":"kyoto","source_repo_path":"/Users/shikokuchuo/r/kyoto","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-mqa4j","depends_on_id":"bd-cmp48","type":"parent-child","created_at":"2026-05-20T14:27:25.451968Z","created_by":"shikokuchuo","metadata":"{}","thread_id":""}]} {"id":"bd-mre3","title":"[websites phase 2] Sidebar data model, generate, render, template","description":"Plan: claude-notes/plans/2026-04-23-website-project-epic.md § Phase 2.\n\nDeliverables:\n- Schema parsing for website.sidebar: Vec with id, title, contents, style, collapse-level.\n- Contents supports: string (path), {href, text, icon}, {section, contents}, {auto: ...}.\n- quarto-navigation data types: Sidebar, SidebarEntry, SidebarContents.\n- SidebarGenerateTransform: reads YAML config + ProjectIndex to resolve auto and expand entries.\n- SidebarRenderTransform: emits Bootstrap-5 HTML matching Q1 class names where possible.\n- Template slot: $rendered.navigation.sidebar$.\n- Sidebar-for-page selection logic.\n- Integration tests with manual, auto, and nested contents.\n\nBlocked by Phase 1 (needs ProjectIndex).","status":"closed","priority":1,"issue_type":"feature","created_at":"2026-04-23T18:42:49.915763Z","created_by":"cscheid","updated_at":"2026-04-29T00:31:30.148471Z","closed_at":"2026-04-29T00:31:30.148151Z","close_reason":"Phase 2 (sidebar) implemented — see git log Phase 2 sub-phase commits and claude-notes/plans/2026-04-24-websites-phase-2.md (closed as part of Phase 9 cleanup; this should have been closed earlier).","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-mre3","depends_on_id":"bd-0tr6","type":"parent-child","created_at":"2026-04-23T18:42:49.915763Z","created_by":"cscheid","metadata":"{}","thread_id":""},{"issue_id":"bd-mre3","depends_on_id":"bd-w5os","type":"blocks","created_at":"2026-04-23T18:43:42.284820Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} -{"id":"bd-mrx1","title":"Phase B.4: Acceptance bundle — _quarto.yml + posts/_metadata.yml propagation to preview","description":"Single Playwright spec pinning the two observable plan acceptance criteria for q2 preview (Phase B):\n\n1. Editing _quarto.yml (title:) re-renders the active page; new title visible in DOM within 5 s.\n2. Editing posts/_metadata.yml (subtitle:) re-renders pages under posts/; new subtitle visible in DOM within 5 s.\n\nFixture (single, dual-purpose): _quarto.yml + posts/_metadata.yml + posts/post1.qmd (no frontmatter, inherits both). Empirically verified both knobs flow through to the rendered title-block (2026-05-13 probe with target/debug/q2 render).\n\nReuses the multi-file fixture helper generalised in bd-pf63 (B.3).\n\nPlan acceptance criterion 3 ('unrelated sibling re-renders the active page') is deferred and tracked separately — its relaxed-contract form (any edit fires a re-render) is invisible at the DOM without SPA instrumentation. See discovered-from follow-up.\n\nPlan: claude-notes/plans/2026-05-13-q2-preview-phase-b.md §B.4.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-05-13T21:11:28.867999Z","created_by":"cscheid","updated_at":"2026-05-13T21:15:09.863020Z","closed_at":"2026-05-13T21:15:09.862892Z","close_reason":"Phase B.4 complete; zero production-code changes — new Playwright spec at q2-preview-spa/e2e/config-edits.spec.ts pins _quarto.yml + posts/_metadata.yml propagation. See commit df2f5f55 on beads/bd-mrx1-phase-b4-acceptance-bundle.","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-mrx1","depends_on_id":"bd-kw93","type":"parent-child","created_at":"2026-05-13T21:11:28.867999Z","created_by":"cscheid","metadata":"{}","thread_id":""},{"issue_id":"bd-mrx1","depends_on_id":"bd-pf63","type":"discovered-from","created_at":"2026-05-13T21:11:28.867999Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} +{"id":"bd-mrx1","title":"Phase B.4: Acceptance bundle — _quarto.yml + posts/_metadata.yml propagation to preview","description":"Single Playwright spec pinning the two observable plan acceptance criteria for q2 preview (Phase B):\n\n1. Editing _quarto.yml (title:) re-renders the active page; new title visible in DOM within 5 s.\n2. Editing posts/_metadata.yml (subtitle:) re-renders pages under posts/; new subtitle visible in DOM within 5 s.\n\nFixture (single, dual-purpose): _quarto.yml + posts/_metadata.yml + posts/post1.qmd (no frontmatter, inherits both). Empirically verified both knobs flow through to the rendered title-block (2026-05-13 probe with target/debug/q2 render).\n\nReuses the multi-file fixture helper generalised in bd-pf63 (B.3).\n\nPlan acceptance criterion 3 ('unrelated sibling re-renders the active page') is deferred and tracked separately — its relaxed-contract form (any edit fires a re-render) is invisible at the DOM without SPA instrumentation. See discovered-from follow-up.\n\nPlan: claude-notes/plans/2026-05-13-q2-preview-phase-b.md §B.4.","status":"closed","priority":2,"issue_type":"task","created_at":"2026-05-13T21:11:22.459798Z","created_by":"cscheid","updated_at":"2026-05-13T21:16:15.516971Z","closed_at":"2026-05-13T21:16:15.516850Z","close_reason":"Duplicate of bd-mrx1 — created by an accidental double-run during B.4 setup (jq parse error masked the first create's output). No work done against this ID; all B.4 work tracked under bd-mrx1.","source_repo":".","compaction_level":0,"original_size":0,"dependencies":[{"issue_id":"bd-mrx1","depends_on_id":"bd-kw93","type":"parent-child","created_at":"2026-05-13T21:11:22.459798Z","created_by":"cscheid","metadata":"{}","thread_id":""},{"issue_id":"bd-mrx1","depends_on_id":"bd-pf63","type":"discovered-from","created_at":"2026-05-13T21:11:22.459798Z","created_by":"cscheid","metadata":"{}","thread_id":""}]} {"id":"bd-msp0","title":"Hub-client preview resource resolution via service worker (epic)","description":"Long-term direction: replace the iframe post-processor's resource-rewriting passes (CSS data-URIs, image data-URIs, the disabled