# Dsc Scenario

> Build a multi-call repro plan against a Salesforce API reference published on developer.salesforce.com ("DSC"). Invoke when the user wants to reproduce a customer flow on an instance and needs to know which supporting API calls to make, in what order, with which scopes, and how IDs thread through the chain – examples: "repro a registered shopper adding a promo coupon and checking out", "what do I…

- **Type:** Skill
- **Install:** `agentstack add skill-j-256-claude-code-skills-dsc-scenario`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [j-256](https://agentstack.voostack.com/s/j-256)
- **Installs:** 0
- **Category:** [Web & Browser](https://agentstack.voostack.com/c/web-and-browser)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [j-256](https://github.com/j-256)
- **Source:** https://github.com/j-256/claude-code-skills/tree/main/skills/dsc-scenario

## Install

```sh
agentstack add skill-j-256-claude-code-skills-dsc-scenario
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# DSC Scenario Composer

Produce a plan of SCAPI / OCAPI calls – in order, with scope union, ID threading, and a runnable cURL block – to reach a target state. Every claim backed by a public `developer.salesforce.com` URL.

## When to use

The user is trying to reproduce a customer flow on an instance and needs to know:
- Which API calls must happen *before* the target operation can succeed.
- Which scopes the instance's SLAS/OAuth client must be configured with.
- Where each input to the target (basket IDs, customer IDs, line-item IDs) comes from.

Or the user pastes a cURL command and asks "what else do I need to call to make this work."

## Inputs

Ask for missing bits only when the skill can't proceed:

- **Target** – one of:
  - An operationId (`createOrder`, `shopper-baskets.addItemToBasket`).
  - A natural-language goal ("registered shopper adds a coupon and checks out"). You resolve this to an operationId by matching against `_index.json.title` + Summary prose across cached references; ask the user to confirm before proceeding.
  - A sample request (cURL, raw HTTP). Use `lib/parse-request.js` + `lib/resolve-slug.js` to map it to a slug.
- **Reference URL** – the developer.salesforce.com URL of the reference containing the target. Usually inferrable from the request path or operationId's reference prefix.

**Resolving an OCAPI target you can't name the reference for.** OCAPI reference slugs are not guessable (`ocapi-shop-orders`, `ocapi-data-code-versions` – there is no `ocapi-shop-api`; guessing 404s). When the user gives a `METHOD` + path ("OCAPI shop, `POST /orders`", "submit a basket via `POST /orders`") but not the reference slug, resolve it deterministically with `resolve-target.js` – pass the **area-landing URL** (`https://developer.salesforce.com/docs/commerce/b2c-commerce/references`) plus the method and path; it scans the landing and returns the exact `{reference, slug, referenceUrl}` to hand to `scenario.js`:

```bash
node ~/.claude/skills/dsc-scenario/scripts/resolve-target.js /.cache/dsc-scrape"
}
EOF
```

It returns `{area, candidates:[{reference, slug, referenceUrl, ...}]}`. Use `candidates[0]` (most-specific path wins). Empty `candidates` means no match – ask the user for the reference URL; do NOT guess a slug (see "Decline, don't fabricate" below). The area-landing URL itself comes from the OCAPI hint in the user's prompt via the alias map (`_shared/scrape/aliases.js` maps "OCAPI", "/dw/shop", "/dw/data", etc. to the b2c-commerce landing).

## Flow

1. **Resolve target** to `{reference, targetSlug}`. For natural-language goals, match titles + Summary prose and confirm with the user.
2. **Invoke `scenario.js`** – it scrapes/refreshes the cache for you (via the accessor) and runs the type-graph walk locally; you do not warm the cache or read its files first:

   ```bash
   node ~/.claude/skills/dsc-scenario/scripts/scenario.js /.cache/dsc-scrape"
   }
   EOF
   ```

   Omitting `graph` is the default: `scenario.js` runs `walkTypes` itself against the cache it just warmed. For an unusually large or cross-reference-heavy target where you want a sub-agent to do the walk instead, read `scripts/walk-via-agent.md`, pass that prompt to the `Agent` tool, and put its returned `{nodes, edges, externalInputs}` in the `graph` field – but that is the exception, not the routine path, and even then the sub-agent reads the cache `scenario.js` warmed, it does not scrape.

   **Producer choice point (two-pass).** If `scenario.js` returns a `bridgeCandidates` array, the target depends on a resource that has *more than one* operation producing it *from nothing*, and the skill needs you to pick which one belongs in the plan. The candidates are those from-nothing producers. Pick the single canonical **create** – the one that builds the resource fresh, not the ones that derive it from an existing instance (`mergeBasket`/`transferBasket` presuppose an existing basket; `createBasket` does not). Re-run `scenario.js` with `bridgeProducer: ""` added to the stdin JSON. The second call composes the full plan deterministically – you do not hand-write the producer step. Pick exactly one; do not list the alternatives in the plan.

   This fires in two structurally identical situations, distinguished only by where the producer lives – you handle both the same way (pick one, re-invoke with `bridgeProducer`):
   - **Cross-reference:** the target's *body* is a named resource produced by another reference (e.g. `createOrder` takes a prepared `Basket`, produced by `shopper-baskets-v2.createBasket`).
   - **In-reference:** the target needs an id (e.g. `addPaymentInstrumentToBasket` needs a `basketId`) that several ops in the target's *own* reference produce from nothing (`createBasket`, `transferBasket`, `mergeBasket`). Without a pick the plan would chain all of them as bogus mandatory prerequisites; the candidate list lets you keep only the canonical create.
4. **Layer business-logic ordering.** The structural plan from Step 2 may need reordering based on rules stated in the Summary or endpoint `description` prose. Apply constraints only when they're *quoted* from the docs; otherwise leave the structural order as-is and annotate as "no explicit ordering constraint found – structural only." Never invent constraints.
5. **Honor the submittability advisory (if present).** When `scenario.js` returns a `submittability` object, the produced resource (e.g. a `Basket`) must be *populated* beyond the structural FK-threading minimum for the target to accept it – curated runtime knowledge the spec does not state. Render it per "Submittability registry" below: a populated producer body + the curated business-rule framing. Absent that object, the structural plan is the whole story.
6. **Compose the output** per the template below. Cite only the URLs in `sources[]`; never cite local paths.

## Output composition

scenario.js emits `{plan, runnable, sources, staleness}`. Wrap it for the user like this (and if `staleness` is non-empty, prepend the stale-data warning from "Key invariants" above the `## Scenario:` line):

```
## Scenario: 

Target:     (.)
References involved: 
Combined scopes required:    (mandatory -- always emit this line with the scope names from plan.combinedScopes; never omit or summarize it away, even in a terse answer)

## Plan

1. **.** .
   - Method/path:  
   - Spec: 
   - Produces: 
   - Why: 

2. ... (one block per step.)

## Run it

## Sources
- 
- 
```

The auth step(s) appear at the top of the plan list (steps 1, optionally 1a/1b for the two-leg PKCE flow), driven by `plan.authBranch` and `plan.authFlow` from `composePlan`. When `authBranch === 'unknown'`, omit the auth-step block entirely; the plan starts with the target reference's first operation. The "References involved" line includes `auth` (Shopper Login / SLAS) when `authBranch === 'shopper-slas'`; AM auth steps cite the canonical `account.demandware.com/dwsso/oauth2/access_token` URL with a one-line note (see "Account Manager (AM) auth framing" below) -- never a `developer.salesforce.com` URL.

The `## Run it` block is mandatory. The runnable's URL prefix is emitted deterministically by `scenario.js` from each reference's `basePath` (SCAPI `/checkout//v/...`, OCAPI Shop `/s/${SITEID}/dw/shop/v/...`, OCAPI Data `/s/-/dw/data/v/...`). OCAPI calls also carry a `?client_id=${CLIENT_ID}` query param (the auth floor); `scenario.js` emits that too. You do not reconstruct any of it.

**PKCE in the runnable.** When the auth flow uses PKCE (any SLAS shopper flow, AM `'public-pkce'`), don't hand-write the `CODE_VERIFIER` / `CODE_CHALLENGE` lines. Run `node ~/.claude/skills/dsc-scenario/scripts/pkce-snippet.js` and paste its stdout into the bash block before the `/oauth2/authorize` (or `/oauth2/login`) call. Hand-rolled snippets drift toward the 32-byte / 43-char minimum-length form; the helper emits the 96-byte / 128-char form (still RFC 7636 compliant, more entropy) consistently.

**Extracting JSON response fields in the runnable -- use `jq -r`.** When a later call needs a field from an earlier call's JSON *response body* (`access_token` from `getAccessToken`, `basketId` from `createBasket`, `applicableShippingMethods[0].id` from `getShippingMethodsForShipment`), extract it with `jq -r` -- e.g. `ACCESS_TOKEN=$(echo "$TOKEN_RESPONSE" | jq -r .access_token)`. This is the idiom the deterministic renderer (`scripts/curl-block.js`) emits, so a hand-composed multi-reference runnable must match it. Do NOT hand-roll `node -e "...JSON.parse(d)..."` or `python3 -c "...json.load(...)..."` for this -- it's the right answer the renderer already produces, and a 200-character Node stdin-reader is not what a support engineer pastes into a terminal. (This is the inverse case of the redirect rule below: the authorization *code* comes from the 303 `Location` header and must NOT be parsed as JSON; genuine JSON response bodies like the token or basket *are* parsed, with `jq`.) Begin the runnable with a one-line preflight so a missing `jq` fails loud rather than silently mis-capturing -- `command -v jq >/dev/null || { echo "this script needs jq (brew install jq, or apt-get install jq)"; exit 1; }`. Note: jq ships with macOS only since Sequoia (15); Linux and older-macOS users may not have it, which is why the preflight earns its line. Do NOT use the display-context fallback `... || _jq() { cat; }` (as in `stepped-demo-script`): here the value is captured into a shell variable, and falling back to `cat` would stuff the entire JSON blob into `ACCESS_TOKEN`/`BASKET_ID` and break every downstream call.

**Capturing the authorization code in the runnable.** The SLAS authorization-code legs answer with a `303` whose `Location` header carries the code – `authorizeCustomer` ("The authorization code was successfully added to the `redirect_uri`") and `authenticateCustomer` (the code and `usid` are added to the location header and returned as query params). The guest leg (`authorizeCustomer` + `hint=guest` + public client) and the registered-B2C leg (`authenticateCustomer` + a Basic-auth `shopperUserID:shopperPassword` header) are both **non-interactive**: a single `curl` returns the 303, and the runnable must capture the code straight from the redirect. Either idiom is correct: `curl -sS -o /dev/null -w '%{redirect_url}'` then parse `code=` out of the captured URL, or `curl -sS -D -` (dump headers) then `grep -i '^location:'` and `sed` the `code=` value out. Do NOT emit a manual "open this URL in a browser and paste the code" step for these flows (and never a `read`/`read -rp` prompt mid-script); a human hand-off in the middle of the script breaks the paste-and-run contract exactly like a bare spec path does.

**The code is in the `Location` header, never a JSON response body.** Both legs are declared with `303` (redirect) responses only – there is no `200` with a JSON token-less body, and no `authorizationCode` field anywhere in either operation's response schema. Do NOT write `... | node -e "...JSON.parse(d).authorizationCode"` or `python3 -c "...json.load(...)['authorizationCode']"` against the authorize/login response – that field is fabricated; the spec returns the code via the redirect `Location`, so the runnable must read it from there. Parsing a JSON body off the authorize/login call is a spec-fidelity bug, not a style choice.

The lone genuine exception to the non-interactive rule is the **registered-federated** leg (`authorizeCustomer` + `hint=`): there the shopper authenticates at an external IDP (Okta, Auth0, …), so that one call is inherently interactive – keep the browser step and say why, rather than pretend the code can be scraped headlessly.

**`authenticateCustomer` (`POST /oauth2/login`) request contract.** The registered-B2C login leg is easy to get subtly wrong because the spec states its parameters in prose, not in a formal `parameters`/`requestBody` schema (the schema only lists `organizationId`, `Authorization`, `x-slas-client-auth`). Take the contract from the description verbatim:

- **`Authorization: Basic base64(:)`** – required header, the *shopper's* own username:password (NOT the client ID/secret; the client goes in the optional `x-slas-client-auth` header when strict client auth is enabled).
- **Required parameters: `code_challenge`, `channel_id`, `client_id`, `redirect_uri`.** Optional: `usid`.
- There is **no** `grant_type` on `/login` (that belongs on the `/token` exchange in `getAccessToken`), and no `response_type`, `channel_type`, `login_id`, `login_password`, or `locale` – those are fabrications. In particular `channel_id` is required and easy to drop; do not omit it, and do not invent `channel_type` in its place.

When you compose the registered-B2C runnable, the `/login` curl carries the shopper Basic-auth header plus exactly those four required params (and `usid` only if you have one to thread). Don't add a `grant_type` to it; don't substitute `login_id`/`login_password` form fields for the Basic header.

When a step's only evidence is `{kind: 'structural', ...}`, the "Why" line should read: " requires  in the request; this step's response provides it." When you add a business-logic constraint from prose, quote the relevant sentence and cite the Summary or endpoint URL inline.

**Cross-reference steps go in the same Plan list, not in a separate section.** If `externalInputs[]` includes `{reference: "auth", ...}`, the "References involved" line names `auth` (the SLAS reference's URL slug; the title on DSC is "Shopper Login (SLAS)") alongside the others, and the SLAS step (or steps – usually `authorizeCustomer` + `getAccessToken`) appears as numbered step(s) in the main Plan with their own `Spec:` URL. Don't write "external input – not part of either reference"; that prose is wrong (the input *is* part of a DSC reference). Auth steps in particular are mandatory expansions – see "Cross-reference walks" below. The "References involved" line and the Plan must include the auth steps even when the rest of the scenario is scoped to one reference.

## Submittability registry – populating the produced resource

The structural walk plans the FK-threading minimum: enough to make the *type graph* resolve. For some targets that minimum is *not enough for the target to accept the produced resource*. The canonical case: `createOrder`'s only structural input is `basketId`, so the walk emits a `createBasket` with an empty `{}` body – but `createOrder` rejects an unpopulated basket at submit (400). The set of fields the basket must carry to be *submittable* is in **neither** the machine-readable spec (`Basket.required` is `null`) **nor** the basket-prep prose (it states no hard required-set). It is curated runtime knowledge, encoded in `scripts/submittability.json` and folded in deterministically by `scenario.js` – the same category of encoded fact as the SLAS auth-routing table, **not** model fabrication.

When `scenario.js` returns a `submittability` object, it means the target's body type has a registry entry. The object carries `{typeName, note, submittableVia, needed, bodyContents:[{field, why}], provenance, confidence:"curated", producerSlug}`. `note` is a one-line plain-language summary of the rule (surface it verbatim if you want a quick framing sentence); `submittableVia` is `"producer-body"` when the minimum is populated in the producer's request body (today's only shape); `needed` is the list of *separate* ops required (empty for body-content entries like `Basket`). Render it like this:

- **Populate the producer step's body** (the `producerSlug` step – e.g. `createBasket`) with the `bodyContents` fields, showing a *realistic* example body (a line item, a shipping method + address, a billing address, a payment instrument), not the empty `{}` and not opaque `` placeholders. The runnable from `scenario.js` carries these fields and a banner; flesh the body out to something a support engineer can actually paste, keeping the field set to exactly w

…

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [j-256](https://github.com/j-256)
- **Source:** [j-256/claude-code-skills](https://github.com/j-256/claude-code-skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-j-256-claude-code-skills-dsc-scenario
- Seller: https://agentstack.voostack.com/s/j-256
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
