Shortcode extensions: Q1 compatibility port (bd-540a976a) - #450
Merged
Conversation
…ontract corpus (bd-540a976a)
Phase 1 core fix: extension shortcode scripts are now eagerly loaded on the
first dispatch that reaches the Lua stage, instead of looked up on demand by
extension id == shortcode name. Handler names come from Lua table keys and are
decoupled from the extension id (extension quarto-tiers contributes shortcode
tier), so the by-name model failed for most real-world extensions. Load order
fixes precedence per plan D4: document shortcodes: scripts < extensions in
discovery order (built-ins first, more-local last); Rust built-ins always win.
Per-script load failures now warn with the extension id and script path named
as the cause instead of hijacking the triggering shortcode's result.
Phase 2 early fix: extract_shortcode_paths dropped document-level shortcodes:
entries stored as ConfigValueKind::PandocInlines (the metadata-as-str failure
class); now uses as_plain_text().
New Phase 0 compatibility corpus (smoke-all/extensions/contract-*): six
passing fixtures covering table-return + global-fn registration, dash-named
shortcodes, block/inline context, kwargs missing-key semantics, return-value
coercions, brace escaping, and document-level shortcodes: with
quarto.shortcode.error_output. contract-escape-comment is parked via
tests.run.skip pending a decision on Q1's Hugo-style {{</* */>}} escape form.
Verified end-to-end: connect-docs {{< tier Enhanced >}} now renders
badge-enhanced spans with zero unknown-shortcode warnings.
Plan: claude-notes/plans/2026-07-31-shortcode-extensions-port.md
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…args, stringify scalars (bd-540a976a)
Four Q1-parity fixes in the Lua shortcode calling convention, each with a
previously-failing contract fixture or regression test:
- raw_args now carries keyword-argument values after positionals (names
stripped), matching TS Quarto; Q1's video.lua relies on raw_args[1] as the
src fallback. Exact source interleaving of positional-after-keyword args is
not recoverable from the q2 AST and is documented as an approximation.
- The handler meta parameter is now the full metadata tree as nested Lua
tables with native scalar types, plus a dotted-path __index fallback on the
top level. Supports Q1's both documented access styles: meta['a.b.c'] and
meta.a.b.c, including 1-based array segments (meta['authors.2']). Replaces
the flat top-level stringified pairs, which dropped nested and compound
values entirely. pampa's ShortcodeArgs.metadata is now a ConfigValue.
- Nested shortcode arguments ({{< outer {{< inner >}} >}}) resolve bottom-up
before the outer handler runs and arrive as stringified plain args (TS
Quarto semantics). They were previously silently dropped. Keyword-value
nested shortcodes are handled the same way.
- pandoc.utils.stringify now converts booleans/integers/numbers to their
string value (pandoc-documented behavior). This also fixes the regression
the meta change would have caused in video.lua's auto-stretch gate, which
stringifies meta['auto-stretch'].
Contract fixtures contract-args-kwargs, contract-meta-dotted (extended with
chaining/boolean/array probes), contract-nested-arg now pass; full workspace
suite green (10,810 tests).
Plan: claude-notes/plans/2026-07-31-shortcode-extensions-port.md
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…fo (bd-540a976a) Closes the Phase 1 loud-failure work: - _extension.yml no longer hard-requires title/author (Extension.title/author are now Option<String>, read via as_plain_text). Only contributes remains structurally required. Real Q1 extensions with minimal manifests now load (bd-8b0af414). New smoke fixture q1-compat-minimal-manifest verifies the end-to-end path. - New Q-16 extension subsystem in the error catalog: Q-16-1 Extension Not Loaded, Q-16-2 Shortcode Script Load Failure, Q-16-3 Unknown Shortcode, Q-16-4 Shortcode Handler Shadowed (info). - discover_extensions now returns (extensions, diagnostics); manifest read/parse failures surface as Q-16-1 warnings naming the manifest file and cause, seeded into StageContext diagnostics — no longer a tracing log line followed by a misattributed unknown-shortcode at the use site (bd-nzdm1wry). Verified end-to-end: broken manifest renders with 'Warning [Q-16-1]: Extension not loaded' + Q-16-3 at the invocation. - Backfilled .with_code on shortcode resolution diagnostics (Q-16-2 script load failures, Q-16-3 unknown shortcode / handler not found). - D4 shadowing diagnostic: the Lua engine records cross-script handler-name collisions (same-script reload is not a collision); the resolve transform drains them as Q-16-4 info diagnostics. Info level deliberately does not trip noErrorsOrWarnings or --strict — overriding a built-in is a supported pattern (lipsum-override fixture unchanged). Full workspace suite green (10,813 tests). Plan: claude-notes/plans/2026-07-31-shortcode-extensions-port.md Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…cess::exit (bd-540a976a)
Completes Phase 2 of the shortcode-extensions port:
- Script-dir-aware require (register_scoped_require): resolves module names
against the script-dir stack top-down — sibling-relative from the
requiring module, then extension-root-relative (Q1's
require('modules/brand/brand') convention). Sandboxed module env, cached
by resolved path (nil result caches as true, like Lua), nested requires
supported, falls back to the native stdlib require when present (WASM has
none). All file access via SystemRuntime so native and WASM share the
code path. New contract-require fixture + pampa unit test.
- quarto.shortcode.error_output now accepts a table as message_or_args
(values concatenated with spaces), matching Q1's contract; Q1's own
fixture calls error_output('shorty', args, 'inline'). The error_args
case is now ported into contract-doc-shortcodes.
- shortcode_to_span no longer kills the process on nested shortcodes in
keyword args: they encode Q1-style (param span with data-key, no
data-value, nested shortcode span as content); the impossible
KeyValue-in-kwargs case degrades visibly instead of exiting.
Full workspace suite green (10,817 tests).
Plan: claude-notes/plans/2026-07-31-shortcode-extensions-port.md
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Phase 4 of the shortcode-extensions port — the three easy-tier built-ins Q1 has and Q2 lacked: - var (Rust VarShortcodeHandler): reads _variables.yml from the project root (project-scoped, Q1 parity), parsed with InterpretationContext:: DocumentMetadata so markdown values render as markdown (documented Q1 behavior). Dotted-path lookup via get_nested. Unknown variable → Q-16-5 warning + visible ?var marker. StageContext loads the file once per document and threads it through build_transform_pipeline; an existing but unparseable _variables.yml warns naming the file. - env (Rust EnvShortcodeHandler): positional name + optional fallback arg (Q1 1.5 #8316). Unset without fallback → Q-16-5 warning (stricter than silently emitting nothing; on wasm32 env reads always fail, so env is uniformly unset there). - pagebreak: new embedded built-in Lua extension (resources/extensions/quarto/pagebreak/), a near-verbatim port of Q1's handlePagebreak: per-format raw payloads (html/epub/latex/openxml/odt/ context/typst), pptx empty, form-feed fallback. - New Q-16-5 'Shortcode Value Not Found' catalog entry; also applied to the meta handler's unknown-key warning. Fixtures: contract-var (simple/nested/markdown-link values), contract-var- unknown (printsMessage WARN + ?var marker), contract-env (fallback), contract-pagebreak; Rust unit tests for env (set/fallback/unset) and var (nested lookup/unknown). Full workspace suite green (10,819). Plan: claude-notes/plans/2026-07-31-shortcode-extensions-port.md Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ps (bd-540a976a)
Phase 5 of the shortcode-extensions port:
- New shortcode-passthrough metadata key: a list of shortcode names to pass
through verbatim (renders the literal {{< ... >}} text, no warning). This
replaces Q1's implicit pass-anything-unknown-through (a Hugo-interop
behavior) with an explicit declaration; anything not declared keeps the
source-mapped Q-16-3 warning. Composes with q2 render --strict for
unknown-shortcode-as-error. Fixture: contract-passthrough.
- HTML writer no longer silently drops a surviving Inline::Shortcode — it
renders a visible <span class="quarto-unresolved-shortcode">?name</span>
marker (a surviving node is a resolution leak; silent disappearance made
it undiagnosable). Unit test added.
Note: catalog codes Q-3-30/Q-3-42 remain unreferenced — writers have no
diagnostics channel today; the visible marker covers discoverability. Left
as-is rather than gold-plating a writer diagnostics channel.
Full workspace suite green (10,821).
Plan: claude-notes/plans/2026-07-31-shortcode-extensions-port.md
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n (bd-u145dg3y) A shortcode used mid-sentence whose handler returns block-level output (e.g. video's raw HTML block) used to vanish into an empty flatten with no trace. Flattening that loses ALL output now emits a source-mapped Q-16-6 warning naming the shortcode, with the fix hint (own paragraph, blank lines around it). Partial flattens stay silent — that's Q1's documented blocks_to_inlines behavior. Also adds claude-notes/instructions/q1-feature-porting.md — the process guide for future Q1→Q2 feature ports, distilled from this port (three-way study, two divergence levers, corpus-first TDD, diagnostics idiom, repo gotchas), plus plan checklist updates for phases 4/5. Full workspace green; cargo xtask verify (all 14 steps incl. WASM + hub-client) passed; cargo xtask lint clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Per review: how Q2 does Hugo interop is not yet designed, so the
{{</* */>}} escape form (and the related paired-shortcode question) moves
to its own question strand rather than complicating this port. Parked
fixture's skip reason now points at bd-kmo1pzc2.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
✅ Snyk checks have passed. No issues have been found so far.
💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse. |
…16-1..6) Both sides appended entries at the file tail; no key or subsystem collisions. Resolved per snapshot-of-truth policy for generated-ish files: took main's version and re-added the Q-16 extension-subsystem entries. Workspace suite green post-merge (10,853 tests). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes the gap between Quarto 1's shortcode-extension support and Q2, per the plan at
claude-notes/plans/2026-07-31-shortcode-extensions-port.md(braid epic bd-540a976a).Headline fix
Extension shortcode scripts are now eagerly activated: previously, on-demand loading was keyed by extension id == shortcode name, so any real-world extension whose shortcode names differ from its directory name (e.g.
quarto-tierscontributingtier,fontawesomecontributingfa) silently failed with "Unknown shortcode". Verified end-to-end against the Connect docs project.What's in here (one commit per theme)
shortcodes:key fix — load all discovered extensions' scripts on first Lua dispatch; document-levelshortcodes:entries stored asPandocInlinesno longer dropped (as_plain_text).raw_argsincludes keyword values;metais the full metadata tree (dottedmeta["a.b.c"]and chainedmeta.a.b.c, native scalars, 1-based array segments); nested shortcode args resolve bottom-up;pandoc.utils.stringifyhandles booleans/numbers (pandoc parity)._extension.ymlno longer hard-requirestitle/author(bd-8b0af414); newQ-16error subsystem (Q-16-1…6); manifest/script failures surface at discovery/load with the file named as cause instead of a misattributed unknown-shortcode at the use site (bd-nzdm1wry); handler shadowing reported as info-level Q-16-4.require(sibling + extension-root-relative, cached,SystemRuntime-backed so WASM shares the path);quarto.shortcode.error_outputaccepts table args (Q1 contract);shortcode_to_span'sprocess::exit(1)replaced with Q1-style recursive span encoding.var(project-scoped_variables.yml, markdown values render),env(fallback arg),pagebreak(embedded Lua extension, near-verbatim Q1 port).shortcode-passthrough: [names]metadata key replaces Q1's silent pass-anything-through (composes withq2 render --strict); the HTML writer renders a visible?namemarker instead of silently dropping unresolved shortcodes; Q-16-6 warns when block-level shortcode output is dropped in inline position (bd-u145dg3y).Tests
14 new smoke fixtures under
crates/quarto/tests/smoke-all/extensions/(contract-*,q1-compat-minimal-manifest,block-shortcode-inline-warns) — a compatibility corpus derived from Q1's documented contract and Q1's own test fixtures — plus pampa/quarto-core unit tests. One fixture is parked viatests.run.skip(Hugo{{</* */>}}comment-escape, deferred to bd-kmo1pzc2).Workspace suite green (10,821 tests);
cargo xtask verify(incl. WASM + hub-client legs) andcargo xtask lintpass locally.Also adds
claude-notes/instructions/q1-feature-porting.md, the process guide for future Q1→Q2 ports distilled from this work.🤖 Generated with Claude Code