Skip to content
Open the menu

The specialism

The AI does not decide. I do.

A general model writes Commerce Cloud code that looks right and is wrong. The cartridge override path, the dw.* surface, ISML and the job framework are too specific for that.

So I set the constraints, the architecture and the direction. The AI executes inside them, at a speed no team matches by hand, and nothing reaches your repository unread.

The rule

Written by AI, reviewed twice

  1. 01 AI writes

    Inside written constraints

    Held to a 22 section SFCC standard and your own project rules, with the sandbox and the cartridge tree in context.

  2. 02 AI reviews

    One specialist per domain

    The diff is split by domain and each one goes to its own reviewer. Findings that cannot be reproduced in the file are dropped.

  3. 03 I review

    A certified human, last

    Nothing reaches your repository until a certified B2C Commerce developer has read the diff. That step is never delegated.

Accelerators

One specialist per domain, and the tools that come with them

Pick one and run it. Every step below is what the tool actually does, in the order it does it.

Agent

SFCC code reviewer

The diff is split by domain and each domain goes to its own reviewer, with its own context and its own written rules. Only the domains that actually changed are ever started.

Eleven domains exist. Five have changed files, so five run.

  • Client JavaScript Event binding, DOM assumptions, what breaks on a cached page.
  • ISML templates Escaping, remote includes, template scope, what leaks into the markup.
  • SCSS Selector scope, overrides that reach further than intended, dead rules.
  • Server JavaScript Controllers, models, helpers and hooks, and the limits of the Rhino runtime.
  • Forms and constraints A field constrained in one layer and left open in the four others.
  • Resources and translations Labels, properties files, and the key that ships missing in one locale.
  • Jobs Steps, chunking, transactions and the quotas that fail at volume, not in test.
  • Metadata and data Definitions, site preferences, and data the code expects but nobody delivers.
  • Scope and reuse Work landing in core that belongs to one brand, and the helper that already existed.
  • Tracking Data layer pushes, event names, and the analytics that quietly stops reporting.
  • Security and cache Caching, personalisation, and personal data reaching a page that gets cached.

How it runs, step by step

  1. Collect the scope

    • Diff the branch against its target, then sort every changed file into a domain.
    collect-scope.sh feature/CART-2184 develop

    git diff --stat develop...feature/CART-2184

    cartridges/app_brand/cartridge/controllers/Cart.js | 34 ++++--

    cartridges/app_brand/cartridge/models/cart.js | 12 ++-

    cartridges/app_brand/cartridge/templates/.../cart.isml | 21 +++-

    cartridges/app_brand/cartridge/forms/default/cart.xml | 8 +-

    metadata/site-preferences/preferences.xml | 6 ++-

    5 files changed, 63 insertions(+), 18 deletions(-)

    DOMAIN_SERVER_JS 2 files

    DOMAIN_ISML 1 file

    DOMAIN_FORMS 1 file

    DOMAIN_METADATA 1 file

    DOMAIN_CLIENT_JS empty, skipped

    DOMAIN_SCSS empty, skipped

    DOMAIN_PROPERTIES empty, skipped

    DOMAIN_JOBS empty, skipped

  2. Run the mechanical checks

    • Their results are handed to the reviewers as facts, never as findings.
    data-coverage.py · schema validation

    python3 scripts/data-coverage.py . develop feature/CART-2184

    preferences.xml valid against preferences.xsd

    cartCouponLimit referenced in Cart.js, absent from staging export

    forms/cart.xml valid against form.xsd

    3 facts collected, 0 findings. Facts go to the reviewers.

  3. Fan out, in parallel

    • One reviewer per domain that has files, all started in a single pass.
    5 agents started

    agentscopestate

    server-js2 filesrunning

    isml1 filerunning

    forms1 filerunning

    metadata1 filerunning

    security-cache3 filesrunning

    six other domains not started, and not billed

  4. Collect the findings

    • Every finding comes back in the same shape, whichever agent wrote it.
    isml · finding 1 of 2

    SEVERITY BLOCKER

    FILE templates/default/cart/cart.isml:48

    RULE Escape every value rendered into markup

    WHAT The coupon code is interpolated raw.

    IMPACT Stored cross site scripting: a coupon code is customer supplied.

    <span class="coupon">${pdict.couponCode}</span>

    <span class="coupon">${pdict.couponCode.toString().encodeHtml()}</span>

  5. Verify before reporting

    • A finding that cannot be reproduced in the file is dropped, not reported.
    verification pass

    BLOCKER cart.isml:48 reopened, confirmed, caused by this diff

    MAJOR Cart.js:112 reopened, confirmed, caused by this diff

    MAJOR cart.js:37 already on develop, not this diff, DROPPED

    3 claims checked, 1 dropped

  6. Hand it to the human

    • One consolidated report. Then I read the diff myself, and that is never delegated.
    report · feature/CART-2184

    severityfilerule

    BLOCKERcart.isml:48escape rendered values

    MAJORCart.js:112no query inside a loop

    MINORcart.xml:14constrain the field in all layers

    cartCouponLimit is read by the code and missing from the metadata export

    Ready for human review. 1 blocker to clear before merge.

Tool

Product graph importer

Name a product on Staging or Development and it pulls the whole family onto your sandbox: master, variants, categories, price books, inventory, custom attributes and images.

How it runs, step by step

  1. Point it at an environment

    • One interactive command. No export to request, no ticket to raise.
    npm run clone:product

    npm run clone:product

    ? Source environment staging

    ? Product ID 701643915283

    ? Include the family yes, master and all variants

    ? Target sandbox (dw.json)

    reading over the OCAPI Data API, read only

  2. Walk the family

    • Everything the product depends on, not just the product record.
    resolved graph

    M0421-MASTER master

    ├─ 701643915283 black / 38 variant

    ├─ 701643915290 black / 40 variant

    ├─ 701643915306 ecru / 38 variant

    └─ 701643915313 ecru / 40 variant

    categories 4 women > ready-to-wear > coats, +3

    price books 6 eur-list, eur-sale, gbp-list, +3

    inventory 3 eu-warehouse, uk-warehouse, store-pool

    custom attrs 18 fabricComposition, careInstructions, +16

    image groups 5 large, medium, small, swatch, hi-res

  3. Write the sandbox

    • A targeted import in MERGE mode. Nothing else on the instance is touched.
    sfcc-site-archive-import · MERGE

    sfcc-site-archive-import clone_M0421.zip --mode merge

    catalog.xml 5 products written

    pricebook.xml 6 price books written

    inventory.xml 3 lists written

    no initSandbox, no full import

    site preferences untouched, other products untouched

  4. Make it usable

    • A product that imports but cannot be added to a basket has not helped anybody.
    post import state

    checkvaluestate

    onlineall 5 recordsforced on

    searchableall 5 recordsforced on

    inventory999 per localeforced

    priceevery locale bookforced

    https://your-sandbox.../s/Brand/black-wool-coat.html ready

Agent

Log to fix loop

The assistant reads your logs live, groups them by signature and takes the heaviest first. You watch the same logs in SFCC Log Center, grouped, counted and searchable, with a file explorer over every WebDAV root.

How it runs, step by step

  1. Pull the logs

    • Read straight from the instance. No copy and paste, no screenshot of a stack trace.
    error-blade-20260920.log · 8,412 lines

    [2026-09-20 09:14:02.118 GMT] ERROR PipelineCallServlet|...|Cart-AddProduct

    TypeError: Cannot read property "value" from null

    at app_brand/cartridge/models/cart.js:112

    [2026-09-20 09:14:07.442 GMT] ERROR PipelineCallServlet|...|Cart-AddProduct

    TypeError: Cannot read property "value" from null

    [2026-09-20 09:14:11.903 GMT] ERROR SystemJobThread|...|ProductFeed

    QuotaExceededException: api.jsObjectArraySize

    ... 8,406 more lines

  2. Group by signature

    • Thousands of lines collapse into a handful of distinct faults, each one counted.
    grouped · last 24 h

    countsignaturefirst seen

    3,918cart.js:112 read "value" from null09:14

    412ProductFeed quota jsObjectArraySize02:00

    88Adyen service timeout after 10s11:37

    31ISML include missing decorator06:02

    8,412 lines, 4 distinct faults

  3. Trace the heaviest to the code

    • Stack to cartridge, cartridge to controller, controller to the call that throws.
    app_brand/cartridge/models/cart.js

    110 var shipment = basket.defaultShipment;

    111 var method = shipment.shippingMethod;

    112 this.shippingLabel = method.custom.label.value;

    113 this.items = getItems(basket);

    shippingMethod is null before a method is selected

    reached from Cart-AddProduct, the first request of a new basket

  4. Propose the fix

    • A patch, the reason it fires, and what else the change touches.
    patch · models/cart.js

    this.shippingLabel = method.custom.label.value;

    this.shippingLabel = method && method.custom.label

    ? method.custom.label.value

    : Resource.msg('cart.shipping.pending', 'cart', null);

    also read by cart.isml:22 and CheckoutServices.js:88, both safe

    needs the cart.shipping.pending key in every locale

  5. Next signature

    • The count drops, the list shortens, and the log gets quieter. Without a freeze.
    grouped · after the fix

    countsignaturestate

    0cart.js:112 read "value" from nullcleared

    412ProductFeed quota jsObjectArraySizenext

    88Adyen service timeout after 10squeued

    8,412 lines down to 531. One signature at a time.

Agent

Specification writer

A ticket becomes a functional recap, a technical design in your conventions, test cases somebody outside the team can follow, and the pull request description in your template.

How it runs, step by step

  1. Read the ticket, and the project

    • The request, the acceptance criteria, and the code that already does half of it.
    CART-2184

    TITLE Cap the number of coupons per basket

    ASK Business wants a maximum of 3 coupon codes per order.

    GIVEN A basket already holding 3 coupons

    THEN A fourth is refused with a message, basket unchanged

    found models/cart.js already exposes the coupon list

    found forms/cart.xml where field constraints belong

    convention limits are site preferences here, never hard coded

  2. Write the technical design

    • Files touched, data needed, and what it breaks if done the obvious wrong way.
    Docs/CART-2184-technical.md

    ## Data

    New site preference cartCouponLimit Integer, default 3

    ## Changes

    Cart.js read the preference, refuse above the cap

    cart.xml mirror the cap as a form constraint

    cart.properties cart.coupon.limit, all 6 locales

    ## Watch out

    The cap must hold server side. A form constraint alone is bypassable.

    Ship the preference with the release or production reads null and refuses all.

  3. Write the test cases

    • Step by step, aimed at somebody who does not know Commerce Cloud.
    Docs/test-cases/CART-2184.md

    stepactionexpected

    1Add any product to the basketBasket shows 1 item

    2Apply codes WELCOME, SPRING, VIPAll three accepted

    3Apply a fourth codeRefused, message shown

    4Reload the basketStill three codes, total unchanged

  4. Write the pull request

    • Your team template, not a generic one.
    pull request description

    STORY Cap the number of coupons per basket

    TICKET CART-2184

    PURPOSE Stop margin loss from stacked promotions.

    CHANGES Server side cap, form constraint, 6 locales.

    DEPLOY Import cartCouponLimit BEFORE activating the code version.

Skill

Project bootstrap

Detects your cartridges, asks the questions that matter, and writes the standards file an assistant will actually be held to. Plus the access map, and a gitignore that protects your credentials before the first commit.

How it runs, step by step

  1. Detect, then ask

    • The architecture is read from the repository. Only what cannot be read is asked.
    init-sfcc

    cartridges found app_brand, app_storefront_base, int_adyen, bm_tools

    architecture SFRA 6.3

    build Bitbucket Pipelines

    ? Brands 2 (BAL, BV)

    ? Locales 6 (en_GB, fr_FR, de_DE, it_IT, es_ES, ja_JP)

    ? Integrations Adyen, Avatax, Klarna

    ? Environments dev, staging, production

  2. Write the standard

    • Generated from a 22 section SFCC reference, then narrowed to this project.
    CLAUDE.md

    ## Cartridge path and override discipline

    Never edit app_storefront_base. Override into app_brand, keep the path.

    ## Forbidden

    A query inside a loop. Collect, then query once.

    Unescaped output in ISML. Every rendered value is encoded.

    A write outside a transaction.

    ## Field constraints

    Constrain in all five layers or in none. A form rule alone is bypassable.

  3. Protect the secrets

    • The access map is committed. The credentials are not, and cannot become so by accident.
    .gitignore · docs/access/SFCC-ACCESS.md

    dw.json

    .sfcc-access.local.json

    **/*.local.json

    committed which environment answers to which account

    never the account passwords themselves

    Every assistant session on this project is now held to the same written rules.

Connected

Wired into what your team already uses

In use today

Your SFCC instance

Logs, job runs, system object definitions, site preferences and script evaluation, read live from the instance over MCP. The assistant stops guessing at your data model and reads it.

Wired on request

Figma

Designs read from the file itself over Dev Mode, with spacing, tokens and component names intact. Integration starts from the real values instead of a screenshot and a ruler.

Wired on request

JIRA

Tickets, acceptance criteria and the discussion underneath them, in context. The assistant understands what the project is doing instead of inferring it from the diff.

Running cost

Faster is worth nothing if the bill goes up

Most of what assisted delivery costs is waste: whole files pushed into a model that needed four lines, agents started on work that does not concern them, the same project rules retyped every session.

I treat that as an engineering problem, because it is one.

No agent on an empty domain

A review only starts the specialists whose files actually changed. Eleven domains exist, four or five run.

One context per specialist

Each agent sees its own domain and nothing else, so none of them pays to read the other ten.

Bulk output never enters the window

Logs, build output and large files are filtered outside the model. Only the answer comes back.

Standards written once

Project rules live in a file the agent reads, not in a prompt somebody retypes every morning.

Training

AI on SFCC, taught to your team

Your developers keep the work. I make the tooling theirs.

Delivered in house, on your own codebase, not on a demo project.

Book a session
  • 01 Working an agent on a cartridge codebase
  • 02 Writing project standards an agent will follow
  • 03 Wiring an assistant to a sandbox over MCP
  • 04 Keeping the running cost down
  • 05 Review discipline, and what to never delegate

AI on SFCC

Ten years on Commerce Cloud, and a thousand hours with Claude

That pairing is the whole point. Platform expertise alone leaves the speed on the table, and AI expertise alone ships code that breaks on the second release.

What comes out of it: agents, skills and tools your developers use on the cartridges you already run.

What this is for

A developer joining your SFRA project finds the answer instead of asking for it. A review catches the override that would have broken in two releases. The specification exists before the ticket closes.

It runs on the project you have today. The storefront does not change, the architecture does not change, and nothing ships unread.

Ready to build your own Salesforce Commerce Cloud project?

let's work together!