@enricai/barnacle 1.12.63 → 1.12.65

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4493,7 +4493,15 @@ function replaceGuardedAgainstExistingPlaceholders(text, pattern, bindingByValue
4493
4493
  * first, and the literal is gone by the time the KV pass runs — masking
4494
4494
  * payload precedence on every call after the one that scraped it.
4495
4495
  */
4496
- function interpolateStateValues(template, priorSteps, targetCapture, payloadAccessorByValue = new Map(), isJsonBody = false, producerBoundaryBindings = new Map(), stepIndex = -1, topLevelPayloadKvValues = new Set()) {
4496
+ function interpolateStateValues(template, priorSteps, targetCapture, payloadAccessorByValue = new Map(), isJsonBody = false, producerBoundaryBindings = new Map(), stepIndex = -1, topLevelPayloadKvValues = new Set(),
4497
+ // A fold target's own join-field value (see `joinFieldValuesByStep` in
4498
+ // `emitMultiStepExecuteHttp`) must stay a literal here — binding it to a
4499
+ // prior step's produced state var (e.g. `${productId}`, always the FIRST
4500
+ // primary item's value) freezes every per-item drill call onto that one
4501
+ // item; the fold loop's own later `parameterize` pass is what rebinds the
4502
+ // literal to `${item.<field>}` per iteration, but only if the literal
4503
+ // text survives this pass untouched.
4504
+ excludedJoinFieldValues = new Set()) {
4497
4505
  const stateBindings = deriveStateVarByValue(priorSteps, targetCapture);
4498
4506
  const bindingByValue = new Map();
4499
4507
  for (const [value, accessor] of payloadAccessorByValue) {
@@ -4508,6 +4516,8 @@ function interpolateStateValues(template, priorSteps, targetCapture, payloadAcce
4508
4516
  continue;
4509
4517
  if (topLevelPayloadKvValues.has(value) && !isProducerStep)
4510
4518
  continue;
4519
+ if (excludedJoinFieldValues.has(value) && !isProducerStep)
4520
+ continue;
4511
4521
  bindingByValue.set(value, `\${${binding.varName}}`);
4512
4522
  sourceNameByValue.set(value, binding.sourceName);
4513
4523
  if (binding.restricted)
@@ -5525,7 +5535,7 @@ pascalName = null) {
5525
5535
  // length/chain-eligibility scoping doesn't happen to catch the coincidence.
5526
5536
  const url = (0, capture_filters_1.isZeroVarianceRepeatCapture)(cap, actions.map((a) => a.capture))
5527
5537
  ? cap.url
5528
- : interpolateStateValues(cap.url, prior, cap, payloadAccessorByValue, false, producerBoundaryBindings, i);
5538
+ : interpolateStateValues(cap.url, prior, cap, payloadAccessorByValue, false, producerBoundaryBindings, i, new Set(), joinFieldValuesByStep.get(i) ?? new Set());
5529
5539
  // Form-schema substitution runs first on the raw recon body so its
5530
5540
  // field-id-anchored matches see the original JSON. State-threading and
5531
5541
  // payload key-value passes then run on top. Option-id substitution runs
@@ -5595,7 +5605,7 @@ pascalName = null) {
5595
5605
  ? applyUrlParamPayloadSubstitutions(rawBodyWithProducerBoundary, parsedBody, urlParamBindings)
5596
5606
  : rawBodyWithProducerBoundary;
5597
5607
  const bodyAfterStateAndKv = rawBodyWithUrlParams
5598
- ? applyPayloadKeyValueSubstitutions(interpolateStateValues(rawBodyWithUrlParams, prior, cap, payloadAccessorByValue, true, producerBoundaryBindings, i, topLevelPayloadKvValues), inputBody, additionalBodies, outDiscoveredAdditionalBodyKeys, allResponseBodies)
5608
+ ? applyPayloadKeyValueSubstitutions(interpolateStateValues(rawBodyWithUrlParams, prior, cap, payloadAccessorByValue, true, producerBoundaryBindings, i, topLevelPayloadKvValues, joinFieldValuesByStep.get(i) ?? new Set()), inputBody, additionalBodies, outDiscoveredAdditionalBodyKeys, allResponseBodies)
5599
5609
  : "";
5600
5610
  // Mechanism A — generic (plain-JSON, wire-key-anchored) dropdown label→code
5601
5611
  // rewrite. Runs AFTER interpolateStateValues + the payload-KV pass, not
@@ -6067,11 +6077,27 @@ pascalName = null) {
6067
6077
  itemVarRefPattern.test(paramBody)) {
6068
6078
  referencesItemVar = true;
6069
6079
  }
6070
- chainLines.push(` const ${chainStep.varName} = (await httpClient(\`${paramUrl}\`, {`, ` method: ${JSON.stringify(chainRendered.method)},`);
6080
+ // Only bind this hop's response to a local when something actually
6081
+ // reads it back: the chain terminal (consumed by
6082
+ // emitFoldMatchAndMergeLines via `terminalStep.varName` below) or a
6083
+ // non-header `produces` entry some later step's request threads
6084
+ // (header threading goes through httpClient's own `bind` store, not
6085
+ // a local — see the header branch below). A hop chained purely to
6086
+ // unlock a later request (e.g. it sets server-side state via a
6087
+ // header token) has no reader for its own response and must stay
6088
+ // unbound, or the emitted loop declares a variable nothing
6089
+ // references (Biome noUnusedVariables).
6090
+ const isChainTerminal = chainIndex === target.chainTerminalIndex;
6091
+ const hasReferencedProduce = chainStep.produces.some((p) => p.kind !== "header" && referencedNames.has(p.name) && !chainDeclared.has(p.name));
6092
+ const bindsChainResponse = isChainTerminal || hasReferencedProduce;
6093
+ chainLines.push(bindsChainResponse
6094
+ ? ` const ${chainStep.varName} = (await httpClient(\`${paramUrl}\`, {`
6095
+ : ` await httpClient(\`${paramUrl}\`, {`, ` method: ${JSON.stringify(chainRendered.method)},`);
6071
6096
  const joined = [paramHeaders, paramBody].filter((s) => s !== "").join(" ");
6072
6097
  if (joined !== "")
6073
6098
  chainLines.push(` ${joined}`);
6074
- chainLines.push(` schema: ${chainRendered.schemaExpr},`, ` })) as Record<string, unknown>;`);
6099
+ chainLines.push(` schema: ${chainRendered.schemaExpr},`);
6100
+ chainLines.push(bindsChainResponse ? ` })) as Record<string, unknown>;` : ` });`);
6075
6101
  for (const p of chainStep.produces) {
6076
6102
  if (p.kind === "header") {
6077
6103
  chainUsesHeaderThreading = true;
@@ -8174,7 +8200,26 @@ function compileFoldReturnResultsMatcher(spec) {
8174
8200
  function buildFoldPlanFromSpec(actions, spec,
8175
8201
  // See detectDrillDownFoldPlan's identical parameter — constrains this
8176
8202
  // function's own primaryStepIndex loop to the same anchor identity.
8177
- primaryIdentityAnchor = null) {
8203
+ primaryIdentityAnchor = null,
8204
+ // When given, restricts the loop to this ONE primaryStepIndex instead of
8205
+ // scanning every occurrence and keeping only the freshest. Used by
8206
+ // mergeSpecPlanOntoSamePrimary to resolve a declared joinFields override
8207
+ // independently for EACH re-issued/paginated primary occurrence, since the
8208
+ // freshest-wins scan below only ever resolves the LAST occurrence.
8209
+ exactPrimaryStepIndex = null,
8210
+ // When given, restricts the matching-drill scan below to candidates whose
8211
+ // OWN endpoint (not a later chain hop) matches this key. A single primary
8212
+ // occurrence can structurally resolve MULTIPLE independent per-item
8213
+ // targets — one per distinct drill endpoint (e.g. a genuinely per-item
8214
+ // drill loop where each item's own request lands on the SAME endpoint
8215
+ // pattern but a distinct path/endpoint identity) — and this function's own
8216
+ // freshest-first scan below `break`s after the FIRST endpoint it resolves,
8217
+ // so an unrestricted call can only ever return an override for ONE of
8218
+ // them. Used by mergeSpecPlanOntoSamePrimary to resolve the declared
8219
+ // joinFields override independently for EACH structural target, keyed by
8220
+ // its own drill endpoint, instead of only the one this function's
8221
+ // freshest-first scan happens to land on first.
8222
+ restrictToDrillEndpointKey = null) {
8178
8223
  const primaryArrayPath = spec.resultsPath.split(".");
8179
8224
  const matchesFoldReturnEndpoint = compileFoldReturnEndpointMatcher(spec);
8180
8225
  // Built once and shared across every primaryStepIndex — see
@@ -8185,6 +8230,9 @@ primaryIdentityAnchor = null) {
8185
8230
  const requestValueIndex = buildRequestValueIndex(actions);
8186
8231
  let freshestPlan = null;
8187
8232
  for (let primaryStepIndex = 0; primaryStepIndex < actions.length; primaryStepIndex++) {
8233
+ if (exactPrimaryStepIndex !== null && primaryStepIndex !== exactPrimaryStepIndex) {
8234
+ continue;
8235
+ }
8188
8236
  if (primaryIdentityAnchor !== null &&
8189
8237
  operationGroupKey(actions[primaryStepIndex].capture) !== primaryIdentityAnchor) {
8190
8238
  continue;
@@ -8213,7 +8261,9 @@ primaryIdentityAnchor = null) {
8213
8261
  // response-only fallback below exists to resolve.
8214
8262
  const matchingDrillStepIndices = [];
8215
8263
  for (let drillStepIndex = primaryStepIndex + 1; drillStepIndex < actions.length; drillStepIndex++) {
8216
- if (matchesFoldReturnEndpoint(actions[drillStepIndex].capture)) {
8264
+ if (matchesFoldReturnEndpoint(actions[drillStepIndex].capture) &&
8265
+ (restrictToDrillEndpointKey === null ||
8266
+ endpointKey(actions[drillStepIndex].capture.url) === restrictToDrillEndpointKey)) {
8217
8267
  matchingDrillStepIndices.push(drillStepIndex);
8218
8268
  }
8219
8269
  }
@@ -8347,6 +8397,26 @@ function isDescendantArrayPath(candidate, base) {
8347
8397
  * spec's own drill indices look independent, so the heuristic's wrong-level
8348
8398
  * guess never survives alongside the corrected one.
8349
8399
  */
8400
+ /**
8401
+ * The stable identity of the logical drill-down a {@link FoldTarget}
8402
+ * resolves onto: the endpoint of its chain's TERMINAL call (the response
8403
+ * actually folded — see {@link FoldTarget.chainTerminalIndex}) paired with
8404
+ * the specific array read off that response (see
8405
+ * {@link FoldTarget.chainArrayPath}). Neither piece alone is stable enough to
8406
+ * compare two independently-resolved targets by: `drillStepIndex` (the
8407
+ * chain's ENTRY point) can legitimately differ between the structural
8408
+ * heuristic's own request-threading resolution and a spec's
8409
+ * response-only/upstream-hop resolution even when both terminate at the same
8410
+ * call (see {@link buildFoldPlanFromSpec}'s `entryIndex` docstring); and the
8411
+ * terminal endpoint alone can collide when one response happens to carry TWO
8412
+ * unrelated per-item arrays for two entirely different primaries (e.g. a
8413
+ * combined status response naming both `entries` and `contracts`). Pairing
8414
+ * the terminal's endpoint with the specific array it reads disambiguates
8415
+ * both cases at once.
8416
+ */
8417
+ function foldTargetDrillIdentity(actions, target) {
8418
+ return `${endpointKey(actions[target.chainTerminalIndex].capture.url)}\u0000${target.chainArrayPath.join(".")}`;
8419
+ }
8350
8420
  function mergeSpecPlanOntoSamePrimary(structuralPlans, actions, foldReturnSpec,
8351
8421
  // See detectDrillDownFoldPlan's identical parameter. Threaded into this
8352
8422
  // function's own buildFoldPlanFromSpec call so a spec plan resolved here
@@ -8355,9 +8425,55 @@ function mergeSpecPlanOntoSamePrimary(structuralPlans, actions, foldReturnSpec,
8355
8425
  primaryIdentityAnchor = null) {
8356
8426
  if (foldReturnSpec === null)
8357
8427
  return [...structuralPlans];
8428
+ // Override joinFields on EVERY structural primary occurrence the spec can
8429
+ // independently resolve at its OWN primaryStepIndex — not just whichever
8430
+ // single occurrence the freshest-wins `specPlan` resolution below happens
8431
+ // to land on. A re-issued/paginated primary (same endpoint, multiple
8432
+ // captures) produces one independent structural plan per occurrence; each
8433
+ // must get the declared override, not just the freshest one.
8434
+ const structuralPlansWithPerOccurrenceOverrides = structuralPlans.map((plan) => {
8435
+ // Resolved once per structural TARGET (keyed on that target's own drill
8436
+ // endpoint), not once for the whole primary occurrence: buildFoldPlanFromSpec's
8437
+ // freshest-first scan `break`s after the first drill endpoint it resolves, so a
8438
+ // single unrestricted call can only ever produce an override for ONE of a
8439
+ // primary's structural targets even when it has several (e.g. a genuinely
8440
+ // per-item drill loop resolving one independent target per item, each at its
8441
+ // own endpoint). See buildFoldPlanFromSpec's restrictToDrillEndpointKey docstring.
8442
+ const mergedTargets = plan.targets.map((target) => {
8443
+ const ownSpecPlan = buildFoldPlanFromSpec(actions, foldReturnSpec, primaryIdentityAnchor, plan.primaryStepIndex, endpointKey(actions[target.drillStepIndex].capture.url));
8444
+ if (ownSpecPlan === null ||
8445
+ JSON.stringify(ownSpecPlan.primaryArrayPath) !== JSON.stringify(plan.primaryArrayPath)) {
8446
+ return target;
8447
+ }
8448
+ // ownSpecPlan was resolved with restrictToDrillEndpointKey pinned to
8449
+ // this target's OWN drill endpoint, so its single target (see
8450
+ // buildFoldPlanFromSpec's `targets: [{...}]` literal — it never
8451
+ // returns more than one) IS the spec's resolution of this restricted
8452
+ // endpoint by construction. Matching it back to `target` via
8453
+ // foldTargetDrillIdentity is not just redundant but actively wrong: a
8454
+ // multi-hop drill chain can make the spec's forward walk
8455
+ // (buildFoldPlanFromSpec's computeFoldChain resolution) land on a
8456
+ // different chainTerminalIndex/chainArrayPath than the structural
8457
+ // heuristic did for the exact same restricted endpoint, so the
8458
+ // identity lookup misses and the declared joinFields override is
8459
+ // silently dropped in favor of the heuristic's structural guess.
8460
+ const specTarget = ownSpecPlan.targets[0];
8461
+ return specTarget === undefined ? target : { ...target, joinFields: specTarget.joinFields };
8462
+ });
8463
+ // A spec-declared target the heuristic missed entirely (an independent
8464
+ // drill-down at a NEW endpoint) is still found via the original
8465
+ // unrestricted resolution, which is free to land on any endpoint.
8466
+ const ownSpecPlan = buildFoldPlanFromSpec(actions, foldReturnSpec, primaryIdentityAnchor, plan.primaryStepIndex);
8467
+ const existingDrillIdentities = new Set(plan.targets.map((target) => foldTargetDrillIdentity(actions, target)));
8468
+ const newTargets = ownSpecPlan !== null &&
8469
+ JSON.stringify(ownSpecPlan.primaryArrayPath) === JSON.stringify(plan.primaryArrayPath)
8470
+ ? ownSpecPlan.targets.filter((target) => !existingDrillIdentities.has(foldTargetDrillIdentity(actions, target)))
8471
+ : [];
8472
+ return { ...plan, targets: [...mergedTargets, ...newTargets] };
8473
+ });
8358
8474
  const specPlan = buildFoldPlanFromSpec(actions, foldReturnSpec, primaryIdentityAnchor);
8359
8475
  if (specPlan === null)
8360
- return [...structuralPlans];
8476
+ return structuralPlansWithPerOccurrenceOverrides;
8361
8477
  const specPrimaryEndpointKey = endpointKey(actions[specPlan.primaryStepIndex].capture.url);
8362
8478
  // Deliberately keyed on the RAW primaryStepIndex (not endpoint identity):
8363
8479
  // this branch trusts the structural target's own chain/drillArrayPath and
@@ -8367,46 +8483,73 @@ primaryIdentityAnchor = null) {
8367
8483
  // different index) may have matched a different item entirely, so it must
8368
8484
  // fall through to the wholesale-replace (sharedDrillPlan) or union
8369
8485
  // (consumedPrimarySteps) handling below instead of merging by identity.
8370
- const samePrimaryPlan = structuralPlans.find((plan) => plan.primaryStepIndex === specPlan.primaryStepIndex &&
8486
+ const samePrimaryPlan = structuralPlansWithPerOccurrenceOverrides.find((plan) => plan.primaryStepIndex === specPlan.primaryStepIndex &&
8371
8487
  JSON.stringify(plan.primaryArrayPath) === JSON.stringify(specPlan.primaryArrayPath));
8372
8488
  if (samePrimaryPlan !== undefined) {
8373
- // A drillStepIndex the structural heuristic ALSO resolved keeps its
8374
- // structurally-resolved chain/drillArrayPath (already proven to reach
8375
- // real per-item data), but its `joinFields` is overridden to the spec's
8376
- // declared value — the heuristic's own joinFields only prove a field
8377
- // threads INTO the drill's REQUEST, which says nothing about whether
8378
- // that same field can be found on the drill's RESPONSE to match it back
8379
- // onto a primary item (see emitFoldMatchAndMergeLines). A flow author
8380
- // declaring `joinFields` on a foldReturn is asserting exactly that: this
8381
- // is the field their drill-down's RESPONSE actually carries.
8382
- const specTargetsByDrillStepIndex = new Map(specPlan.targets.map((target) => [target.drillStepIndex, target]));
8383
- return structuralPlans.map((plan) => {
8489
+ // A structural target serving the SAME drill-down the spec declares
8490
+ // keeps its structurally-resolved chain/drillArrayPath (already proven
8491
+ // to reach real per-item data), but its `joinFields` is overridden to
8492
+ // the spec's declared value — the heuristic's own joinFields only prove
8493
+ // a field threads INTO the drill's REQUEST, which says nothing about
8494
+ // whether that same field can be found on the drill's RESPONSE to match
8495
+ // it back onto a primary item (see emitFoldMatchAndMergeLines). A flow
8496
+ // author declaring `joinFields` on a foldReturn is asserting exactly
8497
+ // that: this is the field their drill-down's RESPONSE actually carries.
8498
+ // Matched by the drill's ENDPOINT IDENTITY (the same identity
8499
+ // scanPrimaryCandidateGroups' own `alreadyTargetedSameEndpoint` check
8500
+ // uses), not raw drillStepIndex equality: the spec's own entryIndex
8501
+ // resolution (resolveSpecMatchedPrimaryItemIndexAlongChain) and the
8502
+ // heuristic's own request-threading scan can each land on a different,
8503
+ // but equally valid, occurrence of the SAME re-issued/paginated drill
8504
+ // endpoint — raw-index equality only happened to override when both
8505
+ // resolutions picked the identical occurrence, silently leaving every
8506
+ // other occurrence's joinFields un-overridden. Keyed on each target's
8507
+ // {@link foldTargetDrillIdentity} — the terminal endpoint paired with the
8508
+ // specific array it reads — rather than `drillStepIndex`: a spec's
8509
+ // declared join value often resolves only through an upstream entry hop
8510
+ // (e.g. a header-threaded token) that isn't the endpointPattern-matched
8511
+ // call itself, so its `drillStepIndex` (== that entry hop) can
8512
+ // legitimately differ from the structural heuristic's own
8513
+ // `drillStepIndex` (its own, possibly different, threading entry point)
8514
+ // even when both chains terminate at the exact same logical drill-down
8515
+ // call and array.
8516
+ const specTargetsByDrillIdentity = new Map(specPlan.targets.map((target) => [foldTargetDrillIdentity(actions, target), target]));
8517
+ return structuralPlansWithPerOccurrenceOverrides.map((plan) => {
8384
8518
  if (plan !== samePrimaryPlan)
8385
8519
  return plan;
8386
8520
  const mergedTargets = plan.targets.map((target) => {
8387
- const specTarget = specTargetsByDrillStepIndex.get(target.drillStepIndex);
8521
+ const specTarget = specTargetsByDrillIdentity.get(foldTargetDrillIdentity(actions, target));
8388
8522
  return specTarget === undefined ? target : { ...target, joinFields: specTarget.joinFields };
8389
8523
  });
8390
- const existingDrillStepIndexes = new Set(plan.targets.map((target) => target.drillStepIndex));
8391
- const newTargets = specPlan.targets.filter((target) => !existingDrillStepIndexes.has(target.drillStepIndex));
8524
+ const existingDrillIdentities = new Set(plan.targets.map((target) => foldTargetDrillIdentity(actions, target)));
8525
+ const newTargets = specPlan.targets.filter((target) => !existingDrillIdentities.has(foldTargetDrillIdentity(actions, target)));
8392
8526
  return { ...plan, targets: [...mergedTargets, ...newTargets] };
8393
8527
  });
8394
8528
  }
8395
8529
  // No exact (primaryStepIndex, primaryArrayPath) match, but a structural
8396
8530
  // plan on the SAME primaryStepIndex may still have independently detected
8397
- // a target at the SAME drillStepIndex a spec target names — just at the
8398
- // wrong array level (e.g. a shallower structural guess that happens to
8399
- // share the spec's drill-down call). That structural target's own
8400
- // primaryArrayPath/drillArrayPath/joinFields describe the heuristic's
8401
- // guess, not the flow author's declaration, so the spec target replaces
8402
- // it wholesale rather than being discarded by the mismatched-array-path
8403
- // branch below or partially merged as if the array level matched.
8404
- const specDrillStepIndices = new Set(specPlan.targets.map((target) => target.drillStepIndex));
8405
- const sharedDrillPlan = structuralPlans.find((plan) => endpointKey(actions[plan.primaryStepIndex].capture.url) === specPrimaryEndpointKey &&
8406
- plan.targets.some((target) => specDrillStepIndices.has(target.drillStepIndex)));
8531
+ // a target at the SAME drill ENDPOINT+array a spec target names — just at
8532
+ // the wrong array level (e.g. a shallower structural guess that happens to
8533
+ // share the spec's drill-down call). Matched by {@link
8534
+ // foldTargetDrillIdentity} (see the samePrimaryPlan branch above for why
8535
+ // the terminal+array, not the entry `drillStepIndex` alone, is the stable
8536
+ // identity to compare on) — not raw drillStepIndex equality:
8537
+ // buildFoldPlanFromSpec's own entryIndex resolution and the structural
8538
+ // heuristic's request-threading scan can each land on a different, but
8539
+ // equally valid, occurrence/hop of the SAME endpoint (or the same single
8540
+ // occurrence reached via a different chain entry point), and raw-index
8541
+ // equality only happens to hold when both resolutions pick the identical
8542
+ // index. That structural target's own primaryArrayPath/drillArrayPath/
8543
+ // joinFields describe the heuristic's guess, not the flow author's
8544
+ // declaration, so the spec target replaces it wholesale rather than being
8545
+ // discarded by the mismatched-array-path branch below or partially merged
8546
+ // as if the array level matched.
8547
+ const specDrillIdentities = new Set(specPlan.targets.map((target) => foldTargetDrillIdentity(actions, target)));
8548
+ const sharedDrillPlan = structuralPlansWithPerOccurrenceOverrides.find((plan) => endpointKey(actions[plan.primaryStepIndex].capture.url) === specPrimaryEndpointKey &&
8549
+ plan.targets.some((target) => specDrillIdentities.has(foldTargetDrillIdentity(actions, target))));
8407
8550
  if (sharedDrillPlan !== undefined) {
8408
- const remainingTargets = sharedDrillPlan.targets.filter((target) => !specDrillStepIndices.has(target.drillStepIndex));
8409
- const otherPlans = structuralPlans.filter((plan) => plan !== sharedDrillPlan);
8551
+ const remainingTargets = sharedDrillPlan.targets.filter((target) => !specDrillIdentities.has(foldTargetDrillIdentity(actions, target)));
8552
+ const otherPlans = structuralPlansWithPerOccurrenceOverrides.filter((plan) => plan !== sharedDrillPlan);
8410
8553
  const keptStructuralPlans = remainingTargets.length === 0
8411
8554
  ? otherPlans
8412
8555
  : [...otherPlans, { ...sharedDrillPlan, targets: remainingTargets }];
@@ -8421,10 +8564,13 @@ primaryIdentityAnchor = null) {
8421
8564
  // the independence guard below would otherwise do, since a
8422
8565
  // level-correcting spec's own drill indices can look entirely
8423
8566
  // independent) or silently dropped by the mismatched-array-path checks.
8424
- const supersededShallowerPlan = structuralPlans.find((plan) => endpointKey(actions[plan.primaryStepIndex].capture.url) === specPrimaryEndpointKey &&
8567
+ const supersededShallowerPlan = structuralPlansWithPerOccurrenceOverrides.find((plan) => endpointKey(actions[plan.primaryStepIndex].capture.url) === specPrimaryEndpointKey &&
8425
8568
  isDescendantArrayPath(specPlan.primaryArrayPath, plan.primaryArrayPath));
8426
8569
  if (supersededShallowerPlan !== undefined) {
8427
- return [...structuralPlans.filter((plan) => plan !== supersededShallowerPlan), specPlan];
8570
+ return [
8571
+ ...structuralPlansWithPerOccurrenceOverrides.filter((plan) => plan !== supersededShallowerPlan),
8572
+ specPlan,
8573
+ ];
8428
8574
  }
8429
8575
  // Keyed by the (primaryStepIndex, primaryArrayPath) pair, not
8430
8576
  // primaryStepIndex alone — a structural plan only ever consumed ITS OWN
@@ -8441,7 +8587,7 @@ primaryIdentityAnchor = null) {
8441
8587
  // contends for.
8442
8588
  const consumedIndices = new Set();
8443
8589
  const consumedPrimarySteps = new Set();
8444
- for (const plan of structuralPlans) {
8590
+ for (const plan of structuralPlansWithPerOccurrenceOverrides) {
8445
8591
  const planPrimaryEndpointKey = endpointKey(actions[plan.primaryStepIndex].capture.url);
8446
8592
  consumedPrimarySteps.add(`${planPrimaryEndpointKey}:${JSON.stringify(plan.primaryArrayPath)}`);
8447
8593
  for (const target of plan.targets) {
@@ -8452,19 +8598,27 @@ primaryIdentityAnchor = null) {
8452
8598
  !consumedPrimarySteps.has(`${specPrimaryEndpointKey}:${JSON.stringify(specPlan.primaryArrayPath)}`) &&
8453
8599
  specPlan.targets.every((target) => target.chain.every((chainIndex) => !consumedIndices.has(chainIndex)));
8454
8600
  if (specConsumesOnlyItsOwnIndices)
8455
- return [...structuralPlans, specPlan];
8601
+ return [...structuralPlansWithPerOccurrenceOverrides, specPlan];
8456
8602
  // The spec's primary endpoint/array is already consumed by a structural
8457
8603
  // plan on a DIFFERENT capture of the same re-issued primary operation
8458
8604
  // (paginated primary), but its own target chains touch no index that plan
8459
8605
  // already depends on — this is the same identity, not a raw-index
8460
8606
  // coincidence, so union the spec's targets onto it rather than silently
8461
- // dropping a declaration the flow author explicitly wrote.
8462
- const sameIdentityPlan = structuralPlans.find((plan) => endpointKey(actions[plan.primaryStepIndex].capture.url) === specPrimaryEndpointKey &&
8607
+ // dropping a declaration the flow author explicitly wrote. Membership in
8608
+ // "already covered" is decided by {@link foldTargetDrillIdentity} (see the
8609
+ // samePrimaryPlan branch above for why the terminal+array, not the entry
8610
+ // `drillStepIndex` alone, is the stable identity) — not raw drillStepIndex
8611
+ // equality. A spec target sharing a structural target's drill identity is
8612
+ // a re-declaration of that SAME drill-down (handled as a wholesale replace
8613
+ // by sharedDrillPlan above), not an independent one to union — so this
8614
+ // only ever unions genuinely new endpoints onto an already-identified
8615
+ // primary.
8616
+ const sameIdentityPlan = structuralPlansWithPerOccurrenceOverrides.find((plan) => endpointKey(actions[plan.primaryStepIndex].capture.url) === specPrimaryEndpointKey &&
8463
8617
  JSON.stringify(plan.primaryArrayPath) === JSON.stringify(specPlan.primaryArrayPath) &&
8464
- specPlan.targets.every((target) => !plan.targets.some((t) => t.drillStepIndex === target.drillStepIndex)));
8618
+ specPlan.targets.every((target) => !plan.targets.some((t) => foldTargetDrillIdentity(actions, t) === foldTargetDrillIdentity(actions, target))));
8465
8619
  if (sameIdentityPlan === undefined)
8466
- return [...structuralPlans];
8467
- return structuralPlans.map((plan) => plan === sameIdentityPlan ? { ...plan, targets: [...plan.targets, ...specPlan.targets] } : plan);
8620
+ return [...structuralPlansWithPerOccurrenceOverrides];
8621
+ return structuralPlansWithPerOccurrenceOverrides.map((plan) => plan === sameIdentityPlan ? { ...plan, targets: [...plan.targets, ...specPlan.targets] } : plan);
8468
8622
  }
8469
8623
  /**
8470
8624
  * The single fold-plan entry point: {@link detectDrillDownFoldPlan}'s
@@ -9836,7 +9990,19 @@ const httpClient = createHttpClient({ schema: ${pascal}ResponseSchema, bottlenec
9836
9990
  chainStep.capture.responseBody,
9837
9991
  ]),
9838
9992
  });
9839
- chainLines.push(` const ${chainStep.varName} = (await httpClient(\`${url}\`, {`, ` method: ${JSON.stringify(chainStep.capture.method)},`, ` schema: ${schemaExpr},`, ` })) as Record<string, unknown>;`);
9993
+ // This emitter never renders a per-hop `produces` accessor (unlike
9994
+ // emitMultiStepExecuteHttp's identical chain loop) — only the
9995
+ // chain terminal's response is ever read, via
9996
+ // `terminalStep.varName` inside emitFoldMatchAndMergeLines below.
9997
+ // A non-terminal hop chained purely to unlock a later request
9998
+ // (e.g. it sets server-side state via a threaded header) has no
9999
+ // reader for its own response, so binding it to a local declares a
10000
+ // variable nothing references (Biome noUnusedVariables).
10001
+ const bindsChainResponse = chainIndex === target.chainTerminalIndex;
10002
+ chainLines.push(bindsChainResponse
10003
+ ? ` const ${chainStep.varName} = (await httpClient(\`${url}\`, {`
10004
+ : ` await httpClient(\`${url}\`, {`, ` method: ${JSON.stringify(chainStep.capture.method)},`, ` schema: ${schemaExpr},`);
10005
+ chainLines.push(bindsChainResponse ? ` })) as Record<string, unknown>;` : ` });`);
9840
10006
  }
9841
10007
  const terminalStep = actionSteps[target.chainTerminalIndex];
9842
10008
  if (!terminalStep)