Skip to content

feat(snowflake): Milo substrate flavor on top of feedback-improvements#166

Open
vhargrave wants to merge 8 commits into
adobe:mainfrom
vhargrave:feat/snowflake-milo-substrate-v2
Open

feat(snowflake): Milo substrate flavor on top of feedback-improvements#166
vhargrave wants to merge 8 commits into
adobe:mainfrom
vhargrave:feat/snowflake-milo-substrate-v2

Conversation

@vhargrave

Copy link
Copy Markdown

What

Adds the Milo substrate flavor to snowflake, rebased onto this branch's feedback-improvements rework. Originally proposed against main as #162; this re-applies it on top of your reworked phases + installer so the two land together instead of conflicting.

On a Milo repo, snowflake must not replace head.html / scripts.js / styles.css — that rips out the Milo runtime that loads the live global-navigation + footer from page metadata. So the Milo flavor installs a minimal substrate: an overlay blocks/snowflake block (Milo loads it from codeRoot) and nothing else. Chrome stays owned by Milo; the bespoke body is drawn by the block from /templates/<name>.html.

Why it composes cleanly with your rework

  • Your rework is pure-EDS — zero Milo references anywhere. The Milo deltas are additive ## Milo flavor sections gated on .snowflake/config.json substrateFlavor, so they don't touch the EDS path.
  • The flavors are parallel mechanisms: your EDS overlay-engine.js (hooked into the boilerplate scripts.js) vs the Milo blocks/snowflake block. They consume the identical artifact contract (section[class] + [data-slot] markers, /templates/<name>.html), so the Milo block renders your reworked phase output 1:1 with no engine changes.

Changes

  • assets/substrate-milo/ — minimal Milo substrate (overlay block + ignore patches). Additive; nothing on this branch references it.
  • scripts/install-substrate.mjs--flavor=milo|eds / --milo / --eds, else auto-detect (milolibs in head.html, or setLibs() in scripts.js). EDS path unchanged; defaults to eds.
  • phases/{0-prereq,1-capture,3-generate,4-wire}.md## Milo flavor sections at the matching anchors. 3-generate's step refs (3.1/3.3/3.4/3.8) align with your numbering.
  • install-substrate.test.mjs — +3 tests (milo auto-detect, --flavor=eds override, bad-flavor rejection).

Tests

node --test scripts/install-substrate.test.mjs9/9 pass (your 6 + 3 new). Milo install verified to add blocks/snowflake and leave scripts.js byte-identical.

Context

Driving this from page-forge (a DA tool that runs snowflake to publish prototypes). It auto-detects Milo repos and deploys via this flavor — adopting your feedback-improvements gets the nicer 1:1 EDS blocks on the Milo path too. Happy to adjust naming/placement to fit how you'd prefer to fold it in.

…s rework

Re-applies the Milo flavor (originally PR adobe#162, off main) on top of David's
worktree-fix-snowflake-feedback-improvements branch. David's branch reworked
the same phase docs + installer for EDS; this re-homes the additive Milo
deltas into his versions. No semantic conflict — his rework is pure-EDS and
has zero Milo awareness; the artifact contract (section[class] + [data-slot]
markers, /templates/<name>.html) is identical, so the Milo overlay block
consumes his reworked phase output unchanged.

- assets/substrate-milo/: minimal Milo substrate (overlay block only; leaves
  Milo's head.html/scripts.js/styles.css untouched). Additive — David has none.
- scripts/install-substrate.mjs: --flavor / --milo / --eds + auto-detect
  (milolibs / setLibs). EDS path unchanged; defaults to eds.
- phases/{0-prereq,1-capture,3-generate,4-wire}.md: re-applied "Milo flavor"
  sections at the matching anchors (3-generate step refs 3.1/3.3/3.4/3.8 still
  align with David's numbering).
- install-substrate.test.mjs: +3 tests (milo auto-detect, --flavor=eds
  override, bad-flavor rejection). Full suite 9/9 green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@catalan-adobe

Copy link
Copy Markdown
Contributor

Re-opening

@catalan-adobe catalan-adobe reopened this Jun 1, 2026
@vhargrave vhargrave changed the base branch from worktree-fix-snowflake-feedback-improvements to main June 1, 2026 14:18

@catalan-adobe catalan-adobe left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review notes

1. VERSION mismatch (must-fix)

assets/substrate-milo/MANIFEST.json declares "substrateVersion": "1.0.0-milo.1" but assets/substrate-milo/VERSION contains 1.0.0-milo.2. These should agree — the installer reads VERSION to write substrateVersion into .snowflake/config.json, so the manifest's own field drifts from what gets deployed.

2. writeSlot duplication (suggestion)

The writeSlot logic in blocks/snowflake/snowflake.js is described as "ported verbatim from the EDS substrate's writeSlot" (assets/substrate/scripts/overlay-engine.js). If a bug is found in slot writing, both files need patching independently. Worth adding a cross-reference comment in each file pointing to the other, e.g.:

// Ported from assets/substrate/scripts/overlay-engine.js — keep in sync.

and the reverse in the EDS file. Alternatively, extracting to a shared module if the two runtimes can reference a common path — but a comment is the pragmatic minimum.


Everything else looks clean — the additive design, auto-detection, phase doc gating, test coverage, and [data-overlay] scoping are all solid. Nice work.

…ent + writeSlot sync comments

- MANIFEST.json substrateVersion 1.0.0-milo.1 → 1.0.0-milo.2 to match VERSION
  (the file the installer reads to write .snowflake/config.json substrateVersion)
- cross-reference 'keep in sync' comments on both writeSlot impls (Milo
  blocks/snowflake/snowflake.js ↔ EDS scripts/overlay-engine.js)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vhargrave

Copy link
Copy Markdown
Author

Thanks for the review @catalan-adobe — both addressed in 1e0475e.

1. VERSION mismatch (must-fix) — Bumped MANIFEST.json substrateVersion to 1.0.0-milo.2 to agree with VERSION. Took VERSION as the source of truth since the installer reads it to write substrateVersion into .snowflake/config.json — so the deployed value is now what the manifest declares.

2. writeSlot duplication (suggestion) — Went with the pragmatic cross-reference rather than extracting a shared module (the two runtimes don't share a load path — Milo loads the block from codeRoot, EDS hooks overlay-engine.js into the boilerplate scripts.js — so a common import path would mean a new shared asset + install wiring, more than this is worth right now). Added paired comments:

  • Milo blocks/snowflake/snowflake.js: // Ported from assets/substrate/scripts/overlay-engine.js — keep in sync.
  • EDS scripts/overlay-engine.js: // Mirrored in assets/substrate-milo/blocks/snowflake/snowflake.js — keep in sync.

If writeSlot keeps growing we can revisit extraction. Tests still 9/9 (node --test scripts/install-substrate.test.mjs).

… blocks)

Adds a Milo block-level path so a Milo repo can produce editable DA block
tables that render 1:1 with the live gnav/footer -- not just the page-level
overlay. Milo runs the standard decorateBlocks pipeline and loads any block
from the repo root with no allow-list (verified in milo/libs/utils/utils.js
loadBlock/getBlockData), so bespoke forge-* blocks decorate natively while
Milo keeps its runtime and renders chrome from page metadata.

- phases/3-generate.md: "Milo flavor deltas" under the Block-level path --
  no styles.css/head.html/scripts.js edits, no header/footer fragments,
  forge-<section> block names, rebuild decorators, self-contained per-block
  CSS, full-bleed wrapper overrides, chrome metadata block (no template key),
  per-section visual-diff 1:1 gate.
- phases/4-wire.md: Milo block-level wire branch (copy blocks/forge-*, no
  fragments/templates/styles).
- SKILL.md, 0-prereq.md, block-level-conversion.md: note Milo supports
  level=block and where the deltas live.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…he runtime (Milo block-level)

Converges snowflake's animation output onto the page-animator --pa-* contract so
generated forge-* block pages animate on load AND stay adjustable in the
page-animator panel/sidekick — one vocabulary for generate -> animate -> adjust.

- substrate-milo: vendor the scroll-animation runtime (blocks/animation/{js,css} +
  tools/page-animator/controls.js) verbatim from milo@page-animator-poc (3fc472a1f),
  with provenance headers. Milo auto-loads blocks/animation from repo root, so a
  sibling <div class="animation forge-<name>"> drives a CSS scroll animation
  (animation-timeline: view()) on the deployed page and stamps data-pa-* so the
  panel can read + adjust it. MANIFEST.replace + VERSION bumped to 1.0.0-milo.3;
  install-substrate.test asserts the runtime files land (scripts.js untouched).
- phases/3-generate.md: new step B.5b — emit animations as --pa-* `animation`
  sidecar blocks (not bundled JS). Policy default|preserve|off (default = tasteful
  staggered reveal; view() no-ops above-fold; cinematics stay bundled). 1:1 gate
  unaffected (reveals settle to the real end-state).
- knowledge/animation-sidecars.md: the --pa-*/range-*/timing-* vocabulary + defaults
  (mirrors controls.js), recipes, browser-floor note. Wired into the load list + SKILL.md.

Remove the vendored copy once the page-animator work lands in Milo main.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@catalan-adobe

Copy link
Copy Markdown
Contributor

@vhargrave, are you looking at extending the scope of this plugin in this PR? I see Milo mentions leaking more and more beyond the Milo substrate.

@catalan-adobe catalan-adobe self-requested a review June 2, 2026 12:04
…OR.md

Addresses review feedback that Milo-specific content was leaking beyond the
Milo substrate into the skill's shared core. The substrate FILES were already
isolated, but the Milo GENERATION GUIDANCE (chrome metadata, page-level and
block-level deltas, --pa-* animation sidecars, wiring) had spread across the
shared phase docs as conditional branches.

- New assets/substrate-milo/FLAVOR.md holds all Milo deltas verbatim, anchored
  #capture / #generate-page / #generate-block / #wire.
- Each phase (1-capture, 3-generate x2, 4-wire) now carries a single gated
  pointer to the matching FLAVOR.md section instead of inline Milo prose.
- Moved knowledge/animation-sidecars.md -> assets/substrate-milo/ (it documents
  the vendored runtime; belongs with it). Repointed referrers.
- SKILL.md, 0-prereq.md, block-level-conversion.md trimmed to pointers; fixed a
  stale "installs only blocks/snowflake" line (it also ships blocks/animation +
  tools/page-animator).

Net: shared core back to substrate-neutral (-333/+43 lines); no behavior change
(verbatim relocation behind the same "read FIRST if milo" gate the skill already
uses). FLAVOR.md/animation-sidecars.md are not in MANIFEST, so not deployed.
install-substrate tests 9/9.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ls, no inlined JS

The source page's animation code is stripped when sections become forge-* block
tables (the "snowflake nuke"). preserve mode now re-expresses that motion as
adjustable --pa-* sidecar DATA the deployed Milo runtime runs, instead of leaving
it as bundled JS (which earlier led agents to inline a Lenis shim into block JS):

- enter/reveal -> --pa-opacity-from + --pa-translate-y (entry range)
- scale/blur-in -> --pa-scale / --pa-blur
- parallax/translate-on-scroll -> approximate as a --pa-translate-y reveal
  (Ryan's panel is an entry-based reveal model, not continuous parallax;
  approximate-and-adjustable is the correct substitute)
- never inline Lenis/GSAP/scroll-listener JS into block code; smooth-scroll is
  free from Milo's foundation:c2 Lenis init
- no source motion -> fall back to default reveal; skip true pin/scrub timelines

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@vhargrave

Copy link
Copy Markdown
Author

@vhargrave, are you looking at extending the scope of this plugin in this PR? I see Milo mentions leaking more and more beyond the Milo substrate.

@catalan-adobe no, it's grew a little too far. I've tried reducing the reach as much as I can just now, but yeah let me know. Worst case we might need to have our own separate snowflake skill for our milo flavours.

@catalan-adobe

Copy link
Copy Markdown
Contributor

Current state seems to be still fine, I could successfully test it locally. So if you are done with current state, I will approve and you can merge it.

… elements

Full-width buttons/inputs/cards with their own padding overflow the
container without box-sizing (CTA bleeding past card edge). Milo has no
global box-sizing reset reaching scoped block CSS, so the block-level
generate guidance now mandates a per-block reset.
… never reinterpret as dynamic

Root cause of a 1:1 regression: block-level Generate dropped a visible
Stardust placeholder ("PLACEHOLDER · price / e.g. US$0/mo") because the
agent judged it a commerce-injected slot and rendered an em-dash. The
placeholder-preservation rule existed only for the overlay path (Phase 2
data-slot-skip), which has no meaning when decorate() rebuilds DOM from a
hand-designed content model. Add the rule to the block-level content-model
guidance (block-level-conversion.md + 3-generate.md B.5 + Milo FLAVOR.md
generate-block): a visible generator placeholder is static source content
to reproduce verbatim, not a dynamic slot to drop.
@vhargrave

Copy link
Copy Markdown
Author

Current state seems to be still fine, I could successfully test it locally. So if you are done with current state, I will approve and you can merge it.

yes please then!

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.

2 participants