Skip to content

Add a Loom <-> Galaxy-flavored-markdown adapter for Pages sync#144

Open
dannon wants to merge 12 commits into
galaxyproject:mainfrom
dannon:feat/galaxy-markdown-adapter
Open

Add a Loom <-> Galaxy-flavored-markdown adapter for Pages sync#144
dannon wants to merge 12 commits into
galaxyproject:mainfrom
dannon:feat/galaxy-markdown-adapter

Conversation

@dannon
Copy link
Copy Markdown
Member

@dannon dannon commented May 29, 2026

PR #114 wired up Loom <-> Galaxy Page sync (the loom-galaxy-page binding block,
the /sync command, and the push/pull/link tools) but deliberately left the page
content untransformed: push stripped the binding block and sent everything else
verbatim. So a synced page mirrored the notebook's raw markdown -- loom-invocation
blocks showed up as inert YAML fences instead of native Galaxy embeds, and pull had
no defined way to reconstruct them. This adds the content adapter #114 left as a
follow-up.

What it does

notebook.md stays canonical; the Galaxy page is a faithful, reconstructable projection.

Push -- each loom-invocation block becomes:

  • a hidden carrier holding the literal block (base64), and
  • when the invocation id validates on the connected server, a visible Galaxy
    invocation_outputs(invocation_id=...) directive (inside a galaxy fenced block),
    so the page renders a real invocation embed.

Narrative (prose, plan sections, anchors, checkboxes, tables) passes through untouched.

Pull / resume -- restores the original loom-invocation fences from the carriers
(every field intact, including notebook_anchor, so no network re-derive) and strips
the directives, since Loom owns the projection and regenerates it on each push.

Two things the live server decided for us

  • Directive ids are validated at store time. An undecodable invocation id 400s the
    entire page, so the directive is gated -- emitted only when the id validates,
    otherwise just the carrier goes out (graceful degrade, never a 400).
  • The carrier is a link-reference definition, not an HTML comment. HTML comments
    survive storage but Galaxy's renderer escapes them to visible text. A CommonMark
    reference definition ([loom-invocation:v1]: #loom "<base64>") renders to nothing and
    is pure markdown, so the page stays clean while the carrier round-trips byte-for-byte.

Verification

  • Fixture tests both directions: round-trip identity, multiple blocks, narrative that
    happens to mention carrier syntax, the validated-vs-degraded rich push, and the wired
    push path through pushNotebookToGalaxy.
  • Live against test.galaxyproject.org (Galaxy 26.1.rc1): the carrier round-trip (clean
    carrier-only page, byte-identical reconstruction) and the rendered output --
    invocation_outputs renders the outputs table and the carriers are invisible,
    confirmed on single- and multi-invocation pages.

Full test suite green; root and Orbit typechecks clean.

Notes for review

  • Two chore: normalize ... to 2-space commits are pure prettier/eslint reformatting of
    two files that predated the formatter -- split out so the behavioral diffs stay
    readable (the lint-staged hook reformats them on any touch anyway).
  • Loom-canonical, single-server for now: the validator checks ids against the connected
    server (not a block's own galaxy_server_url), and pull stays remote-wins (the lossless
    round-trip removes the silent-corruption risk). Symmetric reconcile, a Galaxy-canonical
    mode, and richer directive coverage (datasets, workflows) are deferred.

dannon added 12 commits May 29, 2026 12:10
adds loomToGalaxyMarkdownRich -- an async push variant that emits a visible
\`\`\`galaxy invocation_outputs directive next to each hidden carrier, but only
when the invocation id validates against the connected server. the validator is
injected (InvocationValidator interface) so tests stay pure/offline. graceful
degrade: invalid ids skip the directive and fall back to carrier-only, so a bad
id never 400s the whole page push. round-trip identity holds: pull strips the
three directive lines and restores the carrier exactly.
Switches both push content projections (create and update paths) from
loomToGalaxyMarkdown to loomToGalaxyMarkdownRich so synced Galaxy pages
get validated ```galaxy directive blocks instead of just the hidden
carrier comment. Updates the import accordingly -- the non-rich variant
is no longer needed here.
stripGalaxyDirectiveBlocks removed every ```galaxy fence on pull, so a directive
a human wrote (or a co-author added on the Galaxy side) round-tripped to empty --
silent data loss, and the exact inverse of the carrier-in-prose case we already
defend against.

Loom always emits its directive immediately above the invocation's carrier with
no blank line, so a ```galaxy block is ours iff a carrier line follows its close.
Strip runs before carrier decoding so that check stays exact. Anything else is
preserved verbatim. Added tests for a standalone authored fence surviving and for
an authored fence sitting next to a real Loom directive.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant