import { isContractConsumerModeEnabled } from "../../utils/featureFlags.js";
import { resolveServiceDetailsRef } from "../../utils/utils.js";
import { WorkspaceAuthType, getAuthScheme, isAuthorizationHeaderName, AUTH_MIDDLEWARE_PATTERNS_STR } from "../../utils/workspaceAuth.js";
// Cached at module-load — flags are process-wide and cannot change per call.
const CONSUMER_MODE_ENABLED = isContractConsumerModeEnabled();
const SERVICE_REFS = resolveServiceDetailsRef();
export const MAX_TESTS_TO_GENERATE = 3;
export const MAX_RECOMMENDATIONS = 20;
export const MAX_CRITICAL_TESTS = 3;
/**
 * Error string emitted by skyramp_integration_test_generation when both
 * an explicit authHeader and a workspace api.authType are passed simultaneously.
 * Both the tool description and all prompt locations import this constant,
 * so every occurrence is character-for-character identical by construction.
 */
export const AUTH_CONFLICT_ERROR_MSG = "Auth header and auth type cannot be supported at the same time.";
export function buildArchitectPreamble(isDiffScope) {
    if (isDiffScope) {
        return `You are acting as a Skyramp Integration Architect. You will receive a branch diff — changed endpoints, source code interactions, and existing tests. Your responsibility is to map test intent to the Skyramp generation spec with precision. No guessing — derive all parameters from the codebase, workspace config, and provided context only.

Your task:
1. **Recommend AND generate tests for this PR's changes** — prioritize tests most likely to catch real production bugs (state machine violations, cross-resource data integrity, computed field errors, security boundary bypasses) over trivial coverage
2. **Immediately generate the top-priority tests** by calling Skyramp MCP generation tools — only these types: **integration, contract, E2E, UI**. Never smoke. Never fuzz.
3. **Avoid duplicate coverage** — if an existing test already covers an endpoint + test type, choose a different angle instead

Every tool parameter must trace to a concrete source: repository analysis, source code, or OpenAPI spec. Do not invent field names, request shapes, base URLs, or auth values. If a required value is unknown, read the relevant source file before calling the tool.`;
    }
    return `You are acting as a Skyramp Integration Architect. You will receive a full repository analysis — all endpoints, source code interactions, and existing tests. Your responsibility is to map test intent to the Skyramp generation spec with precision. No guessing — derive all parameters from the codebase, workspace config, and provided context only.

Your task:
1. **Produce a comprehensive test recommendation catalog** grouped by test type (E2E → UI → Integration → Contract), ranked by production risk within each type
2. **Present each recommendation with a complete, ready-to-use Skyramp tool call** — fill in all field names, endpoint URLs, request shapes, and auth parameters from source code so the catalog is immediately actionable
3. **Do not call any generation tools** — your output is the catalog itself; it is executed on demand

Replace every \`<…from source>\` placeholder with actual values before presenting. Do not invent values — read the source file if a value is unclear.`;
}
export function buildContextFetchingGuidance(sessionId) {
    if (!sessionId)
        return "";
    return `<context_fetching_protocol>
## Execution Plan Context
Before calling any tool, replace every \`<from source>\` placeholder in the tool call parameters with actual values read from the relevant source file (handler, schema, or model). Do not proceed with placeholders still present — every parameter must trace to a concrete source.
</context_fetching_protocol>`;
}
export function buildReasoningProtocol() {
    return `<reasoning_protocol>
## Parameter Grounding Rule
Before each GENERATE tool call, confirm WHERE each key value comes from:

- **requestBody / responseBody fields** → source code schema (Zod, Pydantic, DTO), enriched scenario, or OpenAPI spec. **The generation tool rejects empty \`{}\` request bodies for POST/PUT/PATCH** — read the source schema first if the fields are unknown.
- **endpointURL** → workspace \`baseUrl\` + endpoint path (both required — never path alone)
- **authHeader / authScheme** → workspace config or OpenAPI \`securitySchemes\`
- **FK path params** → chained from a prior step's response (check the actual field name — it may be \`id\`, \`uuid\`, \`_id\`, or a resource-specific \`*_id\` field). The chaining source can be a response body (POST or GET), a response header (e.g. \`Location\`), or a cookie — not hardcoded
- **Names / string values** → realistic; append timestamp suffix to avoid re-run conflicts

## Ranking Rule
For each GENERATE item, include one sentence in your output (before the tool calls) stating the specific bug or failure it targets — derived from \`bugCatchingTarget\` or your source-code reading. Example: "Targets: order total miscalculation — total_amount = sum(item.price × item.quantity) should recompute when items array changes."

If \`bugCatchingTarget\` is empty for a GENERATE item, derive it from source code before including the item. A GENERATE slot without a specific bug target belongs in ADDITIONAL.

If a value cannot be sourced, read the relevant source file before calling the tool. Do not proceed with invented values.
</reasoning_protocol>`;
}
function serializeAuthCallParams(params) {
    const parts = [`authHeader: "${params.authHeader}"`];
    if (params.authScheme !== undefined) {
        parts.push(`authScheme: "${params.authScheme}"`);
    }
    if (params.authToken !== undefined) {
        parts.push(`authToken: "${params.authToken}"`);
    }
    return parts.join(", ");
}
/**
 * Returns the authScheme snippet for a given authHeader + authType combination.
 *
 * Delegates scheme resolution to getAuthScheme() in workspaceAuth.ts — the single
 * source of truth — so this file no longer maintains its own slug→scheme table.
 */
export function getAuthSnippets(authHeaderValue, authType, explicitScheme) {
    if (!authHeaderValue) {
        return { authSchemeSnippet: "", authTokenSnippet: "" };
    }
    // Only Authorization headers carry an authScheme prefix.
    if (!isAuthorizationHeaderName(authHeaderValue)) {
        return { authSchemeSnippet: "", authTokenSnippet: "" };
    }
    if (authType && authType !== WorkspaceAuthType.None) {
        // Resolve via the canonical workspaceAuth mapping, passing explicit scheme if set.
        const wsType = authType;
        const scheme = getAuthScheme(wsType, explicitScheme);
        // Use !== undefined so intentionally empty scheme ("" = raw token, no prefix) is preserved.
        if (scheme !== undefined) {
            return { authSchemeSnippet: `, authScheme: "${scheme}"`, authTokenSnippet: "" };
        }
    }
    return { authSchemeSnippet: ', authScheme: "<scheme e.g. Bearer, Basic, Token or empty>"', authTokenSnippet: "" };
}
export const PATH_PARAM_UUID_GUIDANCE = `**Path parameters:** keep the placeholder in \`endpointURL\` (e.g. \`/coupons/{coupon_id}\`). ` +
    `Pass the value via \`pathParams\` (e.g. \`coupon_id=<random-uuid-v4>\`). ` +
    `Use example values from the OpenAPI schema if available; otherwise generate a fresh random UUID v4 — not all-zeros or repeated-digit patterns.`;
export function buildTestPatternGuidelines() {
    return `### Test Pattern Guidelines

#### Tier 1 — Base Patterns
- CRUD lifecycle per resource group (Create → Read → Update → Delete)
- Auth flow (Register → Login → Access protected → Token refresh → Logout)
- Pagination & filtering (boundary values, empty results, large page sizes)
- Error responses (400, 401, 403, 404, 409, 422 — each with a specific trigger)

#### Tier 2 — Code-Informed Patterns (higher value, look for these in source code)
- **Middleware chains**: If auth/rate-limit/logging middleware exists, test the chain (e.g., rate limit hit → auth still checked → correct error returned)
- **N+1 query risk**: If list endpoints join related data (e.g., orders with products), test with large datasets
- **State machines**: If resources have status transitions (draft→published→archived), test invalid transitions (e.g., archived→draft should fail)
- **Cascade deletes**: Only recommend after reading source code to confirm which resource holds the FK. The resource with the FK is the child; the one it points to is the parent. Example: if orders.product_id references products, then products is the parent — deleting a product tests whether orders are protected or cascade-deleted. Getting this backwards (treating the child as the parent) produces a nonsensical test.
- **Race conditions**: If concurrent writes are possible (inventory deduction, counter increment), test concurrent requests
- **Computed fields**: If response contains derived values (total, average, count), verify computation with known inputs (e.g., total_cost = compute_seconds * rate + memory_mb * rate + external_cost)
- **Mutation with collection modification**: If PUT/PATCH endpoints accept arrays of child items (e.g., order line items, cart products, invoice entries), test adding/removing items and verify that derived totals (e.g., total_amount, subtotal, item_count) are recalculated correctly. This is the most common source of user-reported bugs — always prioritize it for GENERATE over simple field-update tests.
    The PATCH/PUT request body should include the child collection array field(s) defined for that endpoint (e.g., "items" with FK references like "product_id" and a quantity field) chained from prior POST responses. A PATCH that only sends metadata fields (e.g., discount_type, status, notes) without modifying the child collection is NOT a valid mutation-recalc test — it will pass even when the item/total logic is broken. Before writing assertions, inspect the source code or OpenAPI spec to identify (1) the actual child collection field name and its FK/quantity/price sub-fields, and (2) how derived totals are calculated (including any discounts, taxes, or fees). Then assert: the child FK fields match chained IDs, quantities match sent values, and totals match the computation from the source code
- **Webhook/event side effects**: If endpoints trigger async operations, test that side effects occur (e.g., POST /orders triggers notification)
- **Cross-user isolation**: If resources are owned by users, test that user B cannot access/modify user A's resources (GET /users/{other_id}/data → 403 Forbidden)
- **Range/boundary invariants**: If business rules cap values (max retries, min balance, discount ≤ subtotal), test the boundary (e.g., set retries to max+1 → expect rejection)
- **Breaking change migration**: If the diff renames a route, changes auth headers, or removes a required field, test both old path (should 404) and new path (should succeed)`;
}
export function buildTestQualityCriteria() {
    return `### What Makes a Good Test

**Integration tests** should demonstrate cross-resource data flow — step A creates data
that step B depends on (e.g., create product → create order referencing that product's ID →
verify order contains correct product). Single-resource CRUD alone is not an integration test.
Use actual field names and values from the source code schema or OpenAPI schema (not \`{}\` or invented field names); verify response data, not just status codes.
When a PUT/PATCH updates a resource with child collections (e.g., order items), the request body
MUST include the child array with FK references chained from prior steps — and assertions MUST
verify the actual child items in the response (product_id, quantity, unit_price), not just
top-level metadata like discount or status.

**Contract tests** (single-step) are the right choice for: error-handling scenarios on a single
endpoint (e.g., PATCH/GET/DELETE a nonexistent resource → 404, POST with invalid payload → 422),
validation boundary checks, and any test that exercises one endpoint's API contract in isolation.
Do NOT add setup steps just to avoid hardcoding an ID — use a realistic hardcoded nonexistent ID
(e.g., 99999 or a random UUID) and keep it a single-step contract test.

**E2E tests** should follow realistic user journeys end-to-end: browse products → search →
add to cart → checkout. Verify that frontend actions trigger the correct API calls and
that the UI reflects backend state.

**UI tests** should exercise component behavior and interaction flows: fill form → validate
inputs → submit → see confirmation. Include visual state changes (loading, error, empty)
and accessibility checks.`;
}
export function buildGenerationRules(isUIOnlyPR) {
    return `### Generation Guidelines

**Available test types:** integration, contract, E2E, UI. **No smoke or fuzz tests.**
Choose based on what adds the most value for this PR's changes.

${CONSUMER_MODE_ENABLED ? `**Contract test mode — signal-based selection:**
- **Consumer contract** (\`consumerMode: true\`): Look for outbound HTTP client code (fetch, axios, httpx, requests, http.Client), service client classes, or calls to external base URLs. If an endpoint's implementation makes downstream calls, that downstream boundary is a consumer contract test candidate.
- **Provider contract** (\`providerMode: true\`): Look for new or modified endpoint handlers, route changes, or response shape modifications. If the diff adds/changes an endpoint this service owns, that is a provider contract test candidate.
- **Both modes** (\`providerMode: true, consumerMode: true\`) — produces the same output as omitting both flags (generates provider and consumer contract tests). Use when the diff contains BOTH provider signals (new/modified endpoint handlers) AND consumer signals (outbound HTTP client calls to another service).` : `**Contract tests — provider-only:**
Only provider-side contract tests are supported. Recommend a contract test (\`providerMode: true\`) when the diff adds or modifies an endpoint handler, route, or response shape that this service owns.`}

**Scenario fidelity:** Every workflow scenario should reflect the actual resource
relationships in the code. If the pre-drafted scenarios don't match the real data model,
replace them with accurate ones.
${isUIOnlyPR ? `
**UI-only PR** — no backend changes. UI tests are most relevant.
Use Playwright browser tools to auto-record traces and generate UI tests.
` : `
When no Playwright trace exists, use the Playwright browser tools (\`browser_navigate\`,
\`browser_click\`, etc.) to record a trace, then export via \`skyramp_export_zip\` for UI tests.
`}
`;
}
export function buildVerificationChecklist(topN, maxGen, codeReviewStepLabel = "0") {
    const minTotal = Math.min(maxGen + 1, topN);
    return `<verification>
Before finalizing your output, verify:
1. **Count**: Total recommendation count equals the total you stated in your Budget Plan (between ${minTotal} and ${topN}). Your GENERATE + ADDITIONAL counts must match the split you committed to. Not fewer than your stated Budget Plan total.
2. **Distinct paths**: Each GENERATE item targets a distinct code path — no two share the same HTTP method + endpoint + expected status.
3. **Auth parameters are consistent** across all tool calls (same authHeader and authScheme).
4. Every endpointURL includes both the base URL and the path (not just the base, e.g. \`http://host/api/v1/orders/{id}\`).
5. **All \`<from source>\` placeholders** you received have been replaced with actual values derived from source code — no \`<...>\` remain in your output.
6. **Real request shapes**: requestBody for POST/PUT/PATCH uses actual field names from source (not \`{}\`). GET search/filter uses \`queryParams\`, not \`requestBody\`.
7. **scenarioFile**: \`skyramp_integration_test_generation\` uses the exact \`filePath\` returned by \`skyramp_batch_scenario_test_generation\` — not a guessed or hardcoded filename.
8. **bugCatchingTarget**: Every GENERATE integration test that targets a business rule, formula, or constraint has a non-empty \`bugCatchingTarget\`.
9. **FK chaining**: In multi-step integration tests, path params sourced from a prior step's response (e.g. \`order_id\` from step 1) use \`chainsFrom\` — not hardcoded IDs.
10. **Concrete scenario names**: No GENERATE item uses a placeholder name ending in a numeric suffix (e.g. \`ui-test-for-changed-component-1\`, \`ui-test-from-trace-2\`). Derive the name from the actual changed component or flow: if the diff touches \`LinkCard.tsx\`, the scenario name should be \`link-card-pin-toggle\` or \`link-card-edit-description\`, not \`ui-test-for-changed-component-1\`. The changed file list is available above — use it.
11. **Issue coverage**: If \`<bug_found>\` blocks exist from Step ${codeReviewStepLabel} (Code Review), verify that the highest-severity flaw (HIGH or CRITICAL) has at least one GENERATE item directly targeting it (its pass/fail outcome depends on whether that bug exists). At most one promotion per run. If the promoted flaw lacks a dedicated GENERATE item, promote or create one before proceeding. Additional HIGH/CRITICAL flaws beyond the first should appear in ADDITIONAL at highest priority.
12. **Code Review completeness**: Did you produce a \`<function_review>\` block for EVERY changed function/handler in Step ${codeReviewStepLabel}? If any function is missing a review, you skipped the correctness analysis for it. Go back and complete it before finalizing.
</verification>`;
}
export function buildFewShotExamples() {
    return `<examples>
<example index="1" type="integration_recommendation">
<thinking>
**Parameter grounding**:
- baseURL: "http://localhost:8000" (workspace api.baseUrl)
- steps[0].requestBody fields "name", "price": ProductCreate schema fields (src/models/product.py)
- steps[1].requestBody "product_id": FK to products — chained from step 0 response id
- steps[1].requestBody "quantity": OrderCreate schema field (src/models/order.py)
- responseBody "total_amount": 89.97 = 29.99 × 3 — from order total formula (src/services/order_service.py: total = sum(item.price * item.quantity))
- authHeader/authScheme: workspace config (Authorization / Bearer)
</thinking>

**#1 — GENERATE** | integration | business_rule | new
Scenario: orders-create-with-product-total-calc (3 steps)
  1. POST /api/v1/products → 201: Create product with known price
  2. POST /api/v1/orders → 201: Create order referencing product_id from step 1, quantity=3
  3. GET /api/v1/orders/{order_id} → 200: Verify total_amount = 29.99 × 3 = 89.97
bugCatchingTarget: "total_amount = sum(item.price × item.quantity) — wrong if multiplication is skipped or items list is ignored"
Tool calls:
  skyramp_batch_scenario_test_generation({ scenarioName: "orders-create-with-product-total-calc", destination: "localhost", baseURL: "http://localhost:8000", authHeader: "Authorization", authScheme: "Bearer", steps: [
    { method: "POST", path: "/api/v1/products", statusCode: 201, requestBody: "{\"name\": \"Widget-1713000000\", \"price\": 29.99}" },
    { method: "POST", path: "/api/v1/orders", statusCode: 201, requestBody: "{\"product_id\": \"chained\", \"quantity\": 3}" },
    { method: "GET", path: "/api/v1/orders/{order_id}", statusCode: 200, responseBody: "{\"id\": \"chained\", \"total_amount\": 89.97, \"items\": [{\"product_id\": \"chained\", \"quantity\": 3, \"unit_price\": 29.99}]}" }
  ] })
  skyramp_integration_test_generation({ scenarioFile: "<filePath returned by skyramp_batch_scenario_test_generation above>" })
Reasoning: Catches a broken total calculation before it ships — the most common source of order-related bug reports.
</example>

<example index="2" type="contract_recommendation">
<thinking>
**Parameter grounding**:
- endpointURL: "http://localhost:8000/api/v1/products/{product_id}" (workspace baseUrl + path from endpoint listing)
- method: "DELETE" (route definition, uppercase)
- pathParams "product_id=<random-uuid-v4>": hardcoded non-existent ID — no setup step needed for a 404 test; use a fresh UUID v4, not all-zeros
- authHeader/authScheme: workspace config (Authorization / Bearer)
- No requestData — DELETE carries no body
</thinking>

**#2 — GENERATE** | contract | error_handling | new
DELETE /api/v1/products/{product_id} → 404
Tool: skyramp_contract_test_generation({ endpointURL: "http://localhost:8000/api/v1/products/{product_id}", method: "DELETE", authHeader: "Authorization", authScheme: "Bearer", pathParams: "product_id=<random-uuid-v4>" })
Reasoning: Catches a missing 404 guard on DELETE — verifies the handler returns 404 for non-existent resources rather than a 500 or silent no-op.
</example>

<example index="3" type="additional_recommendation">
#5 [ADDITIONAL] | integration | security_boundary | existing
  Scenario: orders-unauthorized-cross-user-access (POST /api/v1/orders → GET /api/v1/orders/{order_id} as different user → 403)
  Validates: Cross-user isolation — user B cannot read user A's orders.
</example>
</examples>`;
}
export function buildToolWorkflows(authHeaderValue, authTypeValue = "", explicitScheme) {
    const isAuthorizationHeader = isAuthorizationHeaderName(authHeaderValue);
    const noAuth = !authHeaderValue;
    let authGuidance;
    let authParams;
    if (noAuth) {
        authGuidance = `**Auth Verification Required:** The workspace config indicates no authentication, but you MUST verify this independently before omitting auth:
1. **OpenAPI spec** \u2192 check \`securitySchemes\` / \`securityDefinitions\` for \`type: http\`, \`type: apiKey\`, or \`type: oauth2\`
2. **Source code** \u2192 look for known auth signals (${AUTH_MIDDLEWARE_PATTERNS_STR}).
3. **Route definitions** \u2192 check if routes have auth decorators or middleware applied
4. **Still unknown** \u2192 proceed with \`authHeader: ""\` and note "auth pattern unrecognized" in your recommendation description.
If you find auth requirements, pass the appropriate \`authHeader\` and \`authScheme\` to EVERY tool call. Only pass \`authHeader: ""\` if you confirm the API is truly unauthenticated.`;
        authParams = { authHeader: "" };
    }
    else if (isAuthorizationHeader) {
        // Use getAuthScheme (single source of truth) instead of capitalizing the raw slug.
        // Pass explicitScheme so custom schemes from workspace.yml (e.g. "Ghost", "Hawk") are used.
        const wsType = authTypeValue;
        const resolvedScheme = (authTypeValue && authTypeValue !== "none")
            ? (getAuthScheme(wsType, explicitScheme) ?? "<scheme e.g. Bearer, Token or empty>")
            : "<scheme e.g. Bearer, Token or empty>";
        authParams = { authHeader: "Authorization", authScheme: resolvedScheme };
        if (authTypeValue) {
            authGuidance = `**Auth Scheme:** The workspace \`api.authType\` is \`"${authTypeValue}"\`.
**Where to find the scheme** (check in order):
1. **OpenAPI spec** \u2192 look at \`securitySchemes\` / \`securityDefinitions\` for \`type: http, scheme: bearer\` or \`type: apiKey\`
2. **Source code** \u2192 auth middleware signals: ${AUTH_MIDDLEWARE_PATTERNS_STR}
3. **Workspace config** \u2192 use \`api.authType\` value as the scheme if source is inconclusive
Pass the prefix as \`authScheme\` (e.g., \`"Bearer"\`, \`"Token"\`, \`"Basic"\`). If the API uses raw tokens with no prefix, pass \`authScheme: ""\`.
**Do NOT guess the scheme.**
To skip auth entirely, pass \`authHeader: ""\`.`;
        }
        else {
            authGuidance = `**Auth Scheme:** No \`api.authType\` in workspace config.
**Where to find the scheme** (check in order):
1. **OpenAPI spec** \u2192 look at \`securitySchemes\` / \`securityDefinitions\` for \`type: http, scheme: bearer\` or \`type: apiKey\`
2. **Source code** \u2192 auth middleware signals: ${AUTH_MIDDLEWARE_PATTERNS_STR}
3. **Fallback** \u2192 use \`"Bearer"\` only if the project clearly uses JWT or OAuth; otherwise pass \`authScheme: ""\`
Pass the prefix as \`authScheme\` (e.g., \`"Bearer"\`, \`"Token"\`, \`"Basic"\`). If the API uses raw tokens with no prefix, pass \`authScheme: ""\`.
**Do NOT guess the scheme.**
To skip auth for unauthenticated endpoints, pass \`authHeader: ""\`.`;
        }
    }
    else {
        authParams = { authHeader: authHeaderValue };
        authGuidance = `**Auth Header:** \`${authHeaderValue}\` is a non-Authorization header — a placeholder token is auto-generated. Just pass \`authHeader\` — do NOT pass \`authScheme\` (it is only for Authorization headers and will be ignored).
To skip auth for unauthenticated endpoints, pass \`authHeader: ""\`.`;
    }
    const hasWorkspaceAuthType = !!authTypeValue && authTypeValue !== "none";
    const authCallParams = serializeAuthCallParams(authParams);
    // For skyramp_integration_test_generation with scenarioFile: omit auth entirely when workspace
    // has authType (workspace handles generation-time auth). Without authType, pass authHeader only.
    // NOTE: scenario generation always uses full authCallParams so auth is embedded in the test file
    // and the executor sends the correct Authorization header at run time.
    const authHeaderOnlyParams = serializeAuthCallParams({ authHeader: authParams.authHeader });
    const integrationAuthNote = hasWorkspaceAuthType
        ? `omit ALL auth params (passing auth alongside workspace authType causes "${AUTH_CONFLICT_ERROR_MSG}").`
        : `pass \`${authHeaderOnlyParams}\` only (no \`authScheme\`, no \`authToken\`).`;
    const authHeaderLine = noAuth
        ? `**No Auth (from workspace config):** Workspace indicates no authentication. **Verify independently** — if you find auth in the OpenAPI spec or source code, override with the correct \`authHeader\` and \`authScheme\`.`
        : `**Auth params:** \`${authCallParams}\` — pass to EVERY tool call below.`;
    return `## How to Generate Tests — Tool Workflows

**Contract**: The following tool signatures are strict technical contracts. Every parameter should match the schema exactly. Omit optional parameters rather than guessing values. If a required field cannot be resolved, fetch context first.

**Before every tool call**: Confirm WHERE each key value comes from — source code schema, enriched scenario, or OpenAPI spec. See Mandatory Reasoning Protocol above.

${authHeaderLine}
${authGuidance}

**For multi-endpoint workflows (integration tests) — Batch Scenario → Integration pipeline:**
1. Call \`skyramp_batch_scenario_test_generation\` with ALL steps in a single call: \`scenarioName\`, \`destination\`,
   \`baseURL\`, \`${authCallParams}\`, and a \`steps\` array where each element has \`method\`, \`path\`, \`requestBody\` OR \`queryParams\`, \`responseBody\`, \`statusCode\`.
   \`statusCode\` is optional — defaults: POST→201, DELETE→204, GET/PUT/PATCH→200. Only override for non-standard codes.
   **OpenAPI spec is NOT required.** \`apiSchema\` is OPTIONAL — omit it if no spec exists.
   **CRITICAL — Query params vs request body:**
   - For **POST/PUT/PATCH**: use \`requestBody\` with realistic field values from source code schemas.
   - For **GET/DELETE with search/filter/pagination**: use \`queryParams\`. Do not put query parameters in \`requestBody\` for GET requests — GET request bodies are non-standard and may be ignored or rejected.
   - For **GET by ID**: no \`requestBody\` or \`queryParams\` needed — the ID is in the path.
   \`responseBody\` should match the actual API response shape from source code (including all fields
   returned by the controller — e.g., \`id\`, \`ownerId\`, \`createdAt\`, included relations like \`collection\`, \`tags\`).
   Wrap in \`{"response": ...}\` if the API uses an envelope pattern. If omitted, a synthetic response is generated.
   Inspect the source code to determine the correct request AND response body shapes — avoid sending \`{}\`.
   **For PATCH/PUT mutation-recalc scenarios:** The request body should include the child
   collection array (e.g. \`"items": [{"product_id": <chained from prior POST>, "quantity": 2}]\`).
   Never send a PATCH that only modifies metadata (discount, status) without also including the
   items/products collection — such a test will not catch collection-level or total-recalculation bugs.
   Use unique names with timestamp suffix to avoid conflicts on re-runs.
   For GET/PUT/DELETE with path IDs, use a placeholder — chaining resolves the real ID.
2. Produces a \`scenario_<name>.json\` in the same \`outputDir\` as the test files (not \`.skyramp/\`).
3. Call \`skyramp_integration_test_generation\` with \`scenarioFile\`: ${integrationAuthNote}
   Do NOT pass \`chainingKey\` — defaults to \`response.id\`. After generation, the testbot
   will verify and fix path param chaining in the generated test.

**For single-endpoint tests (contract):**
\`skyramp_{type}_test_generation\` with \`endpointURL\` (full URL incl. base + path), \`method\`,
\`${authCallParams}\`, and \`requestData\` from source code schemas.
If an OpenAPI spec exists, ALSO pass \`apiSchema\` — it enables schema-aware validation
(contract tests verify response structure against the spec).
Without a spec, \`endpointURL\` alone is sufficient.
${PATH_PARAM_UUID_GUIDANCE}

${CONSUMER_MODE_ENABLED ? `**Contract test mode selection — set based on this service's role at the boundary:**
- \`providerMode: true\` — this service IS the API; validates the implementation matches the spec.
  Use for new or modified endpoints this codebase owns.
- \`consumerMode: true\` — this service CALLS another API; validates outbound requests conform to the downstream contract.
  Use when the endpoint's implementation makes HTTP calls to external services (look for fetch/axios/httpx/http.Client/service clients).
  A request-aware mock stands in for the real downstream service — no live dependency needed.
- **Both modes** (\`providerMode: true, consumerMode: true\`) — same output as omitting both flags. Generates both consumer and provider contract tests. Use when the diff contains BOTH provider signals (new/modified endpoint handlers) AND consumer signals (outbound HTTP client calls to another service).` : `**Contract tests — provider-only:**
Only provider-side contract tests are supported. Pass \`providerMode: true\` for new or modified endpoints this codebase owns.`}

**For UI tests:**
1. \`browser_navigate\` to the target URL (from workspace \`api.baseUrl\`)
2. \`browser_snapshot\` to see the page (ARIA tree)
3. Interact using \`browser_click\`, \`browser_type\`, \`browser_fill_form\`, etc.
4. \`browser_snapshot\` after each interaction that changes the page
5. \`skyramp_export_zip\` with an **absolute** output path: \`<repositoryPath>/.skyramp/<test_name>_trace.zip\`
6. \`skyramp_ui_test_generation\` with \`playwrightInput\` = the **absolute** path of the exported zip, and \`outputDir\` = ${SERVICE_REFS.frontendTestDirRef} (e.g. \`frontend/tests\`). Do NOT use the backend service's testDirectory — UI tests must go in the frontend service's test directory.

Tips: For custom dropdowns (Radix, MUI): click combobox → snapshot → click option (NOT \`browser_select_option\`).

**For E2E tests (mixed PR — both backend and frontend changed):**
Only generate when pre-existing backend trace \`.json\` AND Playwright \`.zip\` both exist in the repo.
Use them directly with \`skyramp_e2e_test_generation\`.
If traces are missing, skip E2E and add to \`additionalRecommendations\` with instructions to record them first.`;
}
