Skip to content

Add themes/dynamic-bundles skill and workflow - #141

Draft
braypay wants to merge 2 commits into
Fluid-WeCommerce:mainfrom
braypay:skill/dynamic-bundles
Draft

Add themes/dynamic-bundles skill and workflow#141
braypay wants to merge 2 commits into
Fluid-WeCommerce:mainfrom
braypay:skill/dynamic-bundles

Conversation

@braypay

@braypay braypay commented Aug 6, 2026

Copy link
Copy Markdown

Adds a universal Dynamic Bundles skill and its execution workflow.

The skill takes a company's existing bundle implementation — or a source configurator page —
and recreates it as Fluid Dynamic Bundles, with one theme implementation that renders every
current and future bundle rather than a template per bundle.

Everything asserted below was confirmed live against Chipotle (company 980243433) on
2026-08-06: six variant→bundle conversions, four fixed catering kits built, one real
add-to-cart failure reproduced and root-caused. Where a claim is inference rather than a call I
made, it says so.

What's here

Path
themes/dynamic-bundles/SKILL.md The skill — 9 rules, architecture decision, price/type traps, write surfaces
themes/dynamic-bundles/playbooks/01-07 Discovery, translation, theme generation, routing, validation, greenfield, troubleshooting
themes/dynamic-bundles/playbooks/08-api-write-recipe.md The verified write recipe — every rule earned from a failed request or a wrong read-back
themes/dynamic-bundles/playbooks/09-pricing-patterns.md How to get upgrade pricing — the rebalanced-base pattern, the double-charge trap, negative-base guard
themes/dynamic-bundles/playbooks/10-ui-and-price-surfaces.md Card grouping, variant sub-pickers, anchor sizing, and the price-surface inventory
themes/dynamic-bundles/templates/ The bundle_builder section, its four-layer JS engine, token-only CSS, host template
themes/dynamic-bundles/schemas/ Bundle manifest schema
themes/dynamic-bundles/PLAN.md The research record. PART 7 records what the live run changed, including two PART 1 claims it disproved
workflows/dynamic-bundles.workflow.json 10-step engine, per-step acceptance criteria, bounded rework

The findings that shaped it

Classify the shape before writing anything. The single most expensive mistake available is
applying one pattern to products that merely look uniform. Chipotle had seven near-identical
"menu item" products that split three ways across three different option ids — four component
axes (→ bundles) and two flattened categories (→ collections). Converting a category into one
bundle destroys a browsable listing and advertises one wrong price. The test is are these
option values components of one sellable item, or separate sellable items?
, and it must be run
per product with the option id cited. Workflow step 2 fails if a run answers in aggregate.

Create is one call; convert is two. bundle: true persists on
POST create_bundle_product alongside groups — but on PATCH update_bundle_product it does
not: the groups are created while is_bundle stays false, bundle_config stays {}, and no
Bundle row syncs. A second groups-free flag call fixes it. Between the two calls an unrouted
product renders $0.00 with no picker, which is why routing happens per product rather than
batched at the end.

Three pricing behaviours that silently move the shelf price.

  • included groups add the sum of all their components' resolved prices to the floor.
  • Optional fixed_price (max_only) groups add nothing.
  • Optional dynamic_price groups add their cheapest item even at min_selections: nil — an
    optional paid add-on group moved a burrito's advertised floor from $9.35 to $12.30, so every
    collection tile misquoted a product buyable at $9.35.
  • Separately: fixed_price: "0.00" is ignored. A "free" group falls back to each
    component's own variant price; one $2.75 component pushed a bundle from $53–$64 to
    $55.75–$66.75.

Valid config is not evidence the storefront can sell it. A six-group bundle read back
perfectly from /api/v2025-06/bundles and still failed for every shopper:

POST /api/checkout/v2026-04/carts → 400
"product_bundle_group_id 1771 must reference a customizable group
 or an exclusive included group"

That is an included group being sent by the client. The cart attaches included components
itself — the successful response listed Cheese, Romaine and Tortillas under that group although
they were never in the request. Two shapes verified 200 with correct totals: a configurable
bundle sending customizable groups only ($53.00), and a pure fixed kit sending no
bundled_items at all ($52.00).

The corollary matters as much as the fix: an all-included product has nothing to select, so
any UI gating add-to-cart on "all required groups chosen" leaves every fixed kit permanently
unclickable — while the API side looks perfectly healthy. The workflow's cart-and-money step
therefore requires a successful cart call for one configurable bundle and one pure fixed
kit
; testing one shape is a fail.

Corrected while writing this up: an earlier draft of the skill claimed
price_range / bundle_price_range are computed with country_code: nil and read $0.00.
They aren't — across nine bundles they came back correct and country-resolved, and a wrong
one ($55.75–$66.75) is what exposed the pricing defect above. Only top-level
price / display_price are unusable. That distinction is now explicit, because an agent
following the old text would discard the best verification signal available.

Second conversion (2026-08-07/08) — what it changed

A second company was converted end to end (~13 live carts) and forced four corrections, two of
them to claims made in the first draft of this PR.

Upgrade pricing was the biggest gap. The skill documented fixed vs dynamic group pricing as
a data field but never said how to make a dearer choice cost more. Flipping choice groups to
dynamic_price alone double-charges — each item contributes its full variant price on top
of an anchor that already holds the headline ($11.19 would have charged $16.27). The working
pattern is a rebalanced base: anchor.fixed_price = headline − Σ(defaults), every choice
group dynamic, is_default inside config. Verified across carts at $11.19 default / $11.79
one tier up / $9.39 one down. Value bundles can compute a negative base, which is normal —
honour the source's declared default, and escalate if it is still negative rather than clamping.
New playbook 09-pricing-patterns.md.

price_range — I was wrong twice, in opposite directions. The first draft called a $0.00
range a platform read defect. The second conversion showed it is not: the field sums each item's
config.price and correctly reports zero when every item is zero. But my own correction then
over-claimed the other way — I reinstated it as "the primary write-verification signal" on the
strength of nine bundles that were, I now find, read from PATCH echoes. A fresh GET today
on Chipotle 89901, stored config byte-identical and updated_at unchanged since the write,
returns price_range: 0.0 where the echo said $52.00. The cart charges $52.00 correctly —
that part was verified with a real cart POST. So: the range is trustworthy for dynamic
groups, reads $0.00 for all-fixed bundles, and a PATCH echo is never evidence. That
last rule is now in SKILL.md §5a and enforced by the workflow's write-step QA.

Sequencing and membership. Groups built before their component products had full variant
sets pointed at the wrong variant, and one group had 43 items where the source offered 117 —
two rebuild passes. Variant completeness is now a blocking G0 preflight check. Separately,
group membership must be read from the source per bundle, never derived from the component
product's variant list: different bundles offer different subsets, and deriving silently adds
options the merchant does not sell.

Portability. playbooks/, templates/ and reference/ were unreachable (ENOENT) from
every workflow step sandbox, so the write contract had to be reverse-engineered from existing
bundles. The full contract table is now inlined in SKILL.md §5a, and §10 tells the reader to
expect this rather than stall. Related: the skill arrived in the target company but the
workflow did not — the two catalogs sync separately — so §6 now says the workflow may be
absent and how to recover.

Also folded in: one card per parent product with a variant sub-picker (a group holding three
variants of one product was rendering three identical cards); variant images rather than the
parent's; the anchor item sized from the same token as the option grid (it was rendering 36×36
beside 144px options); a price-surface inventory step, because the reference page had four
composed sections with only one wired to the engine and no price at all in the PDP header; and
country_code being required on cart creation.

Review round 1 — the runtime-handoff findings, addressed

The review was right that the workflow parsed but was not wired for a reliable end-to-end run.
Five findings, all now fixed in the workflow (rev 3) and reflected in SKILL.md §6. One
correction: the "all steps omit qa" finding described the 9-step rev 1 that was on the
branch — the rev 2 rewrite this update lands already carried an explicit qa block on every
step. The other four were live and are fixed with mechanisms the repo already documents.

1. Steps could not reach the shipped playbooks, templates or schema. Correct, and it had
already bitten us in the field — it is why the write contract is inlined in SKILL.md §5a. The
fix is the idiom streamlined-onboard-launch and speed-import already use: every step's prompt
now opens by calling run_skill("themes/dynamic-bundles"), which materializes the skill body and
its references into that step's chat. §5a stays as the safety net if the call fails, and now says
so rather than describing ENOENT as the expected state.

2. The approval gate approved a plan that did not exist yet. Correct — context.approved is
fixed when run_workflow is called, several steps before plan-translation produces anything.
It now means only this run may write. write-bundles calls human_in_the_loop with the
plan's real numbers — per bundle: headline, base arithmetic, before → intended-after span — and
gets approval for that before its first write. If human_in_the_loop is unavailable in the
build, that is explicitly not approval: the step writes nothing and says why. Separately, the
"no-op but dependents still run" hole is closed by finding 5's mechanism: steps 5–9 carry
runIf: {flag: "approved"}, so an unapproved run condition-skips the whole mutating tail. Step
10 is deliberately left ungated, so such a run still completes and reports what it would have
written.

3. The workflow was not pinned to the active theme. Correct and the most dangerous of the
five, since the failure is silent and lands in the wrong company's theme. preflight now
reconciles context.theme_id against GET /api/application_themes/active and stops the run
if it is missing or disagrees. All four theme-targeted steps additionally verify the checkout's
own theme id against preflight's before touching a file, and each has an acceptance criterion
that fails an unverified assumption. SKILL.md §6 now documents theme_id as required launcher
context.

On fallbackToManager: the review says Mist deliberately keeps theme work in a real theme
project and gives no manager fallback, but README.md documents it as degrading "to the manager
project instead of failing when none exists." Those disagree. I have kept the flag, because it is
the shape all 13 theme-targeted steps in this repo use and the theme-identity guard now makes the
wrong-project case loud either way — but if the README is stale, it should be fixed, since it
is what the next author will build against.

4. QA gates defaulted to continuing. Applies to rev 1 (0 of 9 steps had a qa block); rev 2
gave all 10 an explicit one. Acting on the underlying point, the hard gate now covers all three
steps where continuing on unverified work does damage rather than leaving a mess: preflight
(the blocking variant-completeness check), plan-translation (the writes execute this plan), and
write-bundles. Worth flagging for maintainers: README.md notes older desktop builds silently
downgrade qa.onFail: "stop" to "continue", which is exactly why the real protection here is
human_in_the_loop rather than the gate alone.

5. source-analysis was not actually condition-skipped. Correct — a STEP_OUTPUT: skipped
line is prose, not a skip. It now carries runIf: {flag: "source_url"}, gating directly on the
key itself since a non-empty string is truthy, so no deriveContext entry is needed. The prompt
keeps its self-skip and the acceptance criterion now passes the no-source path explicitly,
because README.md warns that older builds drop unknown keys and run the step anyway.

Notes for review

  • Writes go through Surface A only. /api/v2025-06/bundles is read-cross-check: it skips
    validation and writes bundles.settings without mirroring to products.bundle_config, so
    exclusivity written there is honoured by the portal and silently ignored by the cart.

  • Everything before write-bundles is read-only. That step refuses to write unless the caller
    passed context.approved: true; the human confirmation belongs to the calling skill. It is also
    the workflow's only onFail: "stop" gate — deliberately unlike the launch workflows, which never
    stop a run. Building a theme on top of bad bundle records is not worth continuing through.

  • playbooks/ rather than references/. SKILL.md and the playbooks cross-reference each
    other by that path in ~20 places; the manifest's references array points at the real paths,
    which is what validate_catalog.py checks. Happy to rename if you'd prefer strict consistency
    with the other skills.

  • CI validates this workflow only as JSON. validate_catalog.py checks new workflow entries
    for slug/path/updated_at, that the file exists, and that it parses with no duplicate keys. The
    deep step-shape assertions are hardcoded to streamlined-onboard-launch.workflow.json, so
    nothing machine-checks these ten steps. Rather than match one file by eye, I surveyed the field
    values every workflow in this repo actually uses and matched them: target is
    {"type": "manager"} or {"type": "kind", "kind": "theme", "fallbackToManager": true} — the
    only two shapes in the repo, and the four theme steps use the latter. qa.strictness and
    qa.onFail are likewise drawn from the values already in use. An earlier draft of this workflow
    used target: {"type": "theme"}, which appears nowhere in the repo; that would have passed CI
    and misrouted at runtime, and it is fixed here.

  • This skill uses /api/company/v1/products/... deliberately, and CI does not flag it.
    validate_shared_skill_contracts() bans that fragment, but only inside its hardcoded
    SHARED_CONTRACT_FILES list, which this skill is not in. The ban is right for the onboarding and
    clone skills — they should use /api/v202604/company/products. It cannot apply here:
    create_bundle_product / update_bundle_product exist only on the v1 surface, and it is the
    only surface that writes products.bundle_config, where the cart reads exclusivity. Please do
    not add this skill to SHARED_CONTRACT_FILES — it would fail immediately, and "fixing" the
    endpoint would point the skill at something that does not exist.

Universal Dynamic Bundles skill: classify a company's bundle shapes, write
records through Surface A, generate one reusable theme implementation, and
prove the money path through the checkout API.

Verified live against Chipotle (company 980243433) on 2026-08-06: six
variant->bundle conversions, four fixed catering kits, one real add-to-cart
failure reproduced and root-caused.

The three theme-targeted workflow steps use the repo's kind-target shape
({"type":"kind","kind":"theme","fallbackToManager":true}) rather than
{"type":"theme"}, which appears nowhere in the catalog.

@ShadReyes ShadReyes left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The catalog additions are structurally valid, and the workflow parses against the current Mist WorkflowDefinitionSchema, but this is not wired for a reliable end-to-end Mist run yet.

  1. Workflow steps cannot access the shipped playbooks, schema, or templates. The manifest declares these only as references/assets of themes/dynamic-bundles, while every workflow step is an isolated inline-prompt step. Mist materializes assets only when run_skill executes, into that chat's active project. Workflow step chats receive their inline prompt, caller context, and dependency outputs—not the initiating skill's materialized assets. As a result, the theme step instructions to copy templates/... point at files that do not exist in the theme worker sandbox; the same applies to workflow references to playbooks/... and schemas/....

  2. The advertised human approval gate cannot happen at the stated point. context.approved is fixed when run_workflow starts. The plan is produced inside the background workflow and the write step dispatches automatically afterward, so approved: true approves before the plan exists. If approval is absent, the write step reports a no-op but its dependents still run. This needs either a planning/apply split, a launcher that produces and presents the plan before starting the mutating workflow, or an actual pause/resume mechanism.

  3. The workflow is not pinned to the active theme. The preflight discovers the active theme only as step output. Mist resolves a theme step from initial context.theme_id; without it, it selects the first local theme checkout or scaffolds one. Therefore these steps can edit an unrelated theme. Also, fallbackToManager: true does not provide a manager fallback for theme targets—Mist deliberately keeps theme work in a real theme project.

  4. Safety gates default to continuing after failed QA. All steps omit qa, so Mist defaults to enabled: true, strictness: "standard", and onFail: "continue". A failed preflight, write verification, or cart/money verification becomes needs-review, satisfies dependencies, and allows later mutation steps to continue. Hard gates should explicitly use qa.onFail: "stop".

  5. The optional source-analysis step is not conditionally skipped. Printing a STEP_OUTPUT: ... skipped line does not mark a Mist workflow step as condition-skipped; only runIf does. Without a source URL, the step still runs and its unconditional acceptance criteria are likely to fail, leaving normal no-source runs as completed-with-issues. Pass a boolean source flag in launcher context and gate this step with runIf.

The manifest paths are safe, the reference corpus is within the Mist inline budget, repository validation passes, and the exact workflow JSON loads successfully. The blockers are specifically the runtime handoff, asset/reference materialization, approval lifecycle, and target-selection semantics above.

…ution

Skill: adds playbooks 09-pricing-patterns and 10-ui-and-price-surfaces from a
second live company conversion, and rewrites SKILL.md §6 as a launcher contract
(theme_id, approved, source_url) rather than a step list.

Workflow rev 2: 10 steps, explicit qa on every step, variant-completeness
preflight, rebalanced-base pricing arithmetic with a base >= 0 assertion, a
price-surface inventory step, fresh-GET verification, and a negative-case cart
test. Restores plan-translation's dependency on study-company, and drops
target: {"type":"theme"} -- a shape the documented schema does not define, which
would have passed CI and misrouted at runtime.

Workflow rev 3 addresses the review's runtime-handoff findings:

- Every step opens with run_skill("themes/dynamic-bundles"). A step chat does
  not inherit the launching skill's materialized files, so playbooks/,
  templates/ and schemas/ were dead references. SKILL.md §5a remains the
  fallback and now says so.
- write-bundles gates on human_in_the_loop with the plan's real numbers.
  context.approved is fixed before run_workflow starts and cannot be consent to
  a plan that does not exist yet; it now only means "this run may write".
- Steps 5-9 carry runIf {flag: approved}, so an unapproved run condition-skips
  the mutating tail instead of no-opping through it while dependents continue.
  Step 10 stays ungated so such a run still reports.
- preflight reconciles context.theme_id against the active theme and stops on a
  mismatch; all four theme steps verify the checkout's own theme id before
  writing. Mist resolves theme steps from initial context, not step output.
- source-analysis carries runIf {flag: source_url}; a STEP_OUTPUT line is prose,
  not a skip. Its acceptance passes the no-source path explicitly, since older
  builds drop unknown keys and run the step.
- qa.onFail: stop now also covers preflight and plan-translation.

Also fixes apply-manifest.py writing the manifest with ensure_ascii=False,
which re-encoded every existing em-dash and buried the new entries under ~30
lines of unrelated diff.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@braypay
braypay requested a review from ShadReyes August 8, 2026 01:11
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