@skyramp/mcp 0.0.64 → 0.1.0-rc.1

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.
Files changed (40) hide show
  1. package/build/playwright/traceRecordingPrompt.js +30 -36
  2. package/build/prompts/architectPersona.js +19 -0
  3. package/build/prompts/test-maintenance/drift-analysis-prompt.js +11 -6
  4. package/build/prompts/test-maintenance/drift-analysis-prompt.test.js +49 -0
  5. package/build/prompts/test-maintenance/driftAnalysisSections.js +4 -2
  6. package/build/prompts/test-recommendation/test-recommendation-prompt.js +32 -17
  7. package/build/prompts/testbot/testbot-prompts.js +87 -97
  8. package/build/prompts/testbot/testbot-prompts.test.js +142 -0
  9. package/build/services/ScenarioGenerationService.js +2 -2
  10. package/build/services/ScenarioGenerationService.test.js +35 -0
  11. package/build/services/TestExecutionService.js +1 -1
  12. package/build/services/TestGenerationService.js +1 -0
  13. package/build/tools/code-refactor/modularizationTool.js +2 -2
  14. package/build/tools/executeSkyrampTestTool.js +4 -3
  15. package/build/tools/generate-tests/generateBatchScenarioRestTool.js +49 -20
  16. package/build/tools/generate-tests/generateContractRestTool.js +26 -4
  17. package/build/tools/generate-tests/generateIntegrationRestTool.js +44 -13
  18. package/build/tools/generate-tests/generateMockRestTool.js +1 -0
  19. package/build/tools/generate-tests/generateScenarioRestTool.js +17 -39
  20. package/build/tools/generate-tests/generateUIRestTool.js +69 -4
  21. package/build/tools/submitReportTool.js +20 -14
  22. package/build/tools/test-management/analyzeChangesTool.js +8 -3
  23. package/build/tools/test-management/analyzeChangesTool.test.js +85 -0
  24. package/build/types/RepositoryAnalysis.js +2 -12
  25. package/build/types/TestRecommendation.js +43 -1
  26. package/build/types/TestTypes.js +20 -7
  27. package/build/utils/AnalysisStateManager.js +13 -5
  28. package/build/utils/AnalysisStateManager.test.js +35 -0
  29. package/node_modules/playwright/lib/mcp/browser/browserServerBackend.js +3 -0
  30. package/node_modules/playwright/lib/mcp/browser/tab.js +8 -1
  31. package/node_modules/playwright/lib/mcp/browser/tools/keyboard.js +3 -2
  32. package/node_modules/playwright/lib/mcp/browser/tools/navigate.js +1 -1
  33. package/node_modules/playwright/lib/mcp/browser/tools/snapshot.js +4 -4
  34. package/node_modules/playwright/lib/mcp/browser/tools/tabs.js +5 -4
  35. package/node_modules/playwright/lib/mcp/browser/tools/wait.js +1 -1
  36. package/node_modules/playwright/lib/mcp/skyramp/exportTool.js +10 -9
  37. package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +304 -7
  38. package/node_modules/playwright/lib/mcp/test/skyRampExport.js +128 -20
  39. package/package.json +2 -2
  40. package/node_modules/playwright/lib/mcp/terminal/help.json +0 -32
@@ -14,44 +14,38 @@ export function registerTraceRecordingPrompt(server) {
14
14
  role: "user",
15
15
  content: {
16
16
  type: "text",
17
- text: `## Skyramp Trace Recording & UI Test Generation
18
-
19
- You have access to Playwright browser tools that let you interact with web applications.
20
- Use these tools to record a trace of browser interactions, then generate a Skyramp UI test from that trace.
21
-
22
- ### Flow
23
-
24
- 1. **Navigate**: ALWAYS call \`browser_navigate\` with the target URL as the very first step, even if the browser seems to already be on that page. This ensures a clean state.
25
- 2. **Understand the page**: Call \`browser_snapshot\` to see the current page state (ARIA tree).
26
- 3. **Interact**: Use \`browser_click\`, \`browser_type\`, \`browser_select_option\`, etc. to perform the user interactions described in the prompt.
27
- 4. **Repeat steps 2-3** until all interactions are complete. Assertions are automatically added at strategic points during export.
28
- 5. **Export the trace**: Call \`skyramp_export_zip\` with an output path (e.g. \`skyramp_export.zip\`). This produces a zip containing the JSONL trace and HAR network recording. Assertions are auto-injected based on API calls detected in the HAR.
29
- 6. **Generate the test**: Call \`skyramp_ui_test_generation\` with \`playwrightInput\` set to the absolute path of the zip file from step 5.
30
-
31
- ### Tips
32
- - **To type into a field**: Just use \`browser_type\` it automatically clears the field and types the new value. Do NOT press Ctrl+A or any keyboard shortcuts before typing.
33
- - If a \`browser_click\` or \`browser_type\` fails because the element reference is stale (page updated), call \`browser_snapshot\` to refresh the page state and retry.
34
- - Use \`browser_snapshot\` liberally — it helps you understand what elements are available.
35
- - The trace automatically deduplicates retries: if you navigate back to the start URL and redo steps, only the last complete attempt is exported.
36
- - After generating the test, the tool will suggest running \`skyramp_modularization\` for code quality.
37
- - **Dropdown/Select components**: For custom dropdowns (Radix, MUI, etc.) that show as \`combobox\` in the snapshot, do NOT use \`browser_select_option\` it only works on native \`<select>\` elements. Instead: (1) click the combobox to open the dropdown, (2) call \`browser_snapshot\` to see the options in a \`listbox\`, (3) click the desired \`option\`. This three-step pattern is required for all custom dropdown components.
38
- - **Always take a snapshot after each interaction** that changes the page (click, form submit, navigation) to see the updated state before proceeding.
39
-
40
- ### Critical rules for clicking
41
- - **NEVER click container/wrapper divs** (e.g. elements with "container" in their test-id). Always click the actual interactive element inside: a \`button\`, \`link\`, or \`input\`.
42
- - When the snapshot shows a container with a button inside, click the **button**, not the container. For example, if you see \`div "add-order-products-container" > button "Add"\`, click the button "Add", not the container.
43
- - To submit forms, click the submit \`button\` (e.g. "Add Order", "Submit"), never the form container.
44
- - After selecting a product from a dropdown, click the "Add" button to confirm, not the surrounding container.
17
+ text: `## Skyramp UI Test Recording
18
+
19
+ You are a Skyramp Integration Architect. Your role is to record browser interactions with zero hallucination: every action must be grounded in what \`browser_snapshot\` returns. If an element is not visible in the snapshot, do not interact with it.
20
+
21
+ ### Required workflow
22
+
23
+ Before starting, output a \`<thinking>\` block that maps each step of the user's intent to the specific browser interactions required. Do not call any tool until this mapping is complete.
24
+
25
+ Then execute in strict order:
26
+
27
+ 1. **Navigate**: Call \`browser_navigate\` with the target URL. Always do this first, even if the browser appears to be on the correct page.
28
+ 2. **Snapshot**: Call \`browser_snapshot\` to get the current ARIA tree and element refs.
29
+ 3. **Interact**: Call the appropriate tool (\`browser_click\`, \`browser_type\`, \`browser_hover\`, etc.) using refs from the snapshot.
30
+ 4. **Repeat steps 2–3** for each user action until all steps are complete.
31
+ 5. **Export**: Call \`skyramp_export_zip\` with \`outputPath\` set to the absolute zip path (same directory and base name as the test file, replacing \`.spec.ts\` with \`.zip\`). Do NOT ask the user first — call it automatically.
32
+ 6. **Generate**: Call \`skyramp_ui_test_generation\` with \`playwrightInput\` set to the absolute zip path from step 5.
33
+
34
+ ### Cross-tool rules
35
+
36
+ - **After every action that changes the page**, call \`browser_snapshot\` before the next interaction refs become stale after navigation, clicks that trigger page updates, and form submissions.
37
+ - **Iframe content** appears inline in the snapshot — interact with those elements using their refs normally.
38
+ - **Trace deduplication**: if you retry from the start URL, only the last complete attempt is exported.
39
+ - **After generating the test**, run \`skyramp_modularization\` for code quality.
45
40
 
46
41
  ### Assertions
47
- If the user requests assertions, you MUST call \`browser_assert\` at the appropriate points. Always provide the \`expected\` value.
48
- - \`type: "text"\` — verify element contains expected text (e.g., product name appears after creation)
49
- - \`type: "value"\` — verify input field has expected value (e.g., price field shows "29.99")
50
-
51
- ### Important
52
- - Do NOT ask the user before calling \`skyramp_export_zip\` call it automatically as the final step.
53
- - Do NOT write JSONL or HAR files manuallythe export tool handles everything.
54
- - Do NOT reuse existing zip files from previous sessions — always record fresh.
42
+ Call \`browser_assert\` when the user requests verification. Always provide the \`expected\` value.
43
+ - \`type: "text"\` — verify an element contains expected text
44
+ - \`type: "value"\` — verify an input field has an expected value
45
+
46
+ ### Constraints
47
+ - Do NOT write JSONL or HAR files manually — \`skyramp_export_zip\` handles everything.
48
+ - Do NOT reuse zip files from previous sessionsalways record fresh.
55
49
  `,
56
50
  },
57
51
  },
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Skyramp Integration Architect persona injected into generation tool descriptions.
3
+ *
4
+ * In TestBot environments (ENABLE_SKYRAMP_TESTBOT=true), the persona is injected
5
+ * once as a system prompt via `claude --system-prompt` rather than repeating it in
6
+ * every tool description. In that case this string is omitted from the tool description
7
+ * to avoid wasting context tokens.
8
+ *
9
+ * In IDE/MCP-direct environments, it is included in each tool description so the
10
+ * model has the role context available without a separate system prompt.
11
+ */
12
+ export const SKYRAMP_ARCHITECT_PERSONA = `You are acting as a Skyramp Integration Architect. Your responsibility is to map the user's test intent to the Skyramp generation spec with precision. No guessing — derive all parameters from the codebase, workspace config, and provided context only.`;
13
+ /**
14
+ * Returns the persona prefix for use in tool descriptions.
15
+ * Returns an empty string when running inside TestBot (persona is injected via system prompt instead).
16
+ */
17
+ export function getPersonaPrefix() {
18
+ return process.env.ENABLE_SKYRAMP_TESTBOT ? '' : `${SKYRAMP_ARCHITECT_PERSONA}\n\n`;
19
+ }
@@ -30,7 +30,16 @@ No existing Skyramp tests found in repository.
30
30
  `;
31
31
  const scannedSection = scannedEndpoints.length > 0
32
32
  ? `## Scanned Endpoints (${scannedEndpoints.length})
33
- ${scannedEndpoints.map((ep) => `- ${Array.isArray(ep.methods) ? ep.methods.join("|") : ep.method} ${ep.path}`).join("\n")}
33
+ ${scannedEndpoints.map((ep) => {
34
+ let methods;
35
+ if (Array.isArray(ep.methods)) {
36
+ methods = ep.methods.map((m) => (typeof m === "string" ? m : m.method)).join("|");
37
+ }
38
+ else {
39
+ methods = ep.method;
40
+ }
41
+ return `- ${methods} ${ep.path}`;
42
+ }).join("\n")}
34
43
  `
35
44
  : "";
36
45
  // In inline mode (testbot), skip the context header — existing tests and diff
@@ -70,9 +79,5 @@ ${buildUpdateExecutionRules()}
70
79
 
71
80
  ${buildAddRecommendationGuidelines()}
72
81
 
73
- ${buildDriftOutputChecklist(existingTests.length, newEndpointCount, inlineMode)}
74
-
75
- After completing the assessment above, call \`skyramp_actions\` with \`stateFile: "${stateFile}"\`
76
-
77
- **CRITICAL**: Do NOT create any .json or .md files. Only call skyramp_actions when done.`;
82
+ ${buildDriftOutputChecklist(existingTests.length, newEndpointCount, inlineMode, stateFile)}`;
78
83
  }
@@ -0,0 +1,49 @@
1
+ import { buildDriftAnalysisPrompt } from "./drift-analysis-prompt.js";
2
+ describe("buildDriftAnalysisPrompt - scanned endpoints rendering", () => {
3
+ // Reproduces the [object Object] bug: skeletonEndpoints from analyzeChangesTool
4
+ // stores methods as objects { method: string, ... }, not plain strings.
5
+ const skeletonMethodObjects = [
6
+ {
7
+ path: "/api/v1/",
8
+ methods: [{ method: "GET", description: "", queryParams: [], authRequired: true, sourceFile: "main.py", interactions: [] }],
9
+ resourceGroup: "v1",
10
+ pathParams: [],
11
+ },
12
+ {
13
+ path: "/api/v1/orders",
14
+ methods: [
15
+ { method: "GET", description: "", queryParams: [], authRequired: true, sourceFile: "orders.py", interactions: [] },
16
+ { method: "POST", description: "", queryParams: [], authRequired: true, sourceFile: "orders.py", interactions: [] },
17
+ ],
18
+ resourceGroup: "orders",
19
+ pathParams: [],
20
+ },
21
+ ];
22
+ it("renders HTTP methods as strings, not [object Object]", () => {
23
+ const prompt = buildDriftAnalysisPrompt({
24
+ existingTests: [],
25
+ scannedEndpoints: skeletonMethodObjects,
26
+ repositoryPath: "/repo",
27
+ stateFile: "/tmp/state.json",
28
+ });
29
+ expect(prompt).not.toContain("[object Object]");
30
+ expect(prompt).toContain("GET /api/v1/");
31
+ expect(prompt).toContain("GET|POST /api/v1/orders");
32
+ // CTA should appear exactly once (not duplicated)
33
+ const ctaCount = (prompt.match(/call `skyramp_actions`/g) || []).length;
34
+ expect(ctaCount).toBe(1);
35
+ });
36
+ it("also works with plain string methods (ScannedEndpoint format)", () => {
37
+ const stringMethods = [
38
+ { path: "/api/v1/products", methods: ["GET", "POST"], sourceFile: "products.py" },
39
+ ];
40
+ const prompt = buildDriftAnalysisPrompt({
41
+ existingTests: [],
42
+ scannedEndpoints: stringMethods,
43
+ repositoryPath: "/repo",
44
+ stateFile: "/tmp/state.json",
45
+ });
46
+ expect(prompt).not.toContain("[object Object]");
47
+ expect(prompt).toContain("GET|POST /api/v1/products");
48
+ });
49
+ });
@@ -163,12 +163,14 @@ Apply to **new test functions you are adding** and **existing functions that cov
163
163
 
164
164
  ${ENHANCE_ASSERTIONS_FOR_INTEGRATION_AND_CONTRACTPROVIDER}`;
165
165
  }
166
- export function buildDriftOutputChecklist(existingTestCount, newEndpointCount, inlineMode = false) {
166
+ export function buildDriftOutputChecklist(existingTestCount, newEndpointCount, inlineMode = false, stateFile) {
167
167
  const finalStep = inlineMode
168
168
  ? `### Final step
169
169
  Apply all maintenance actions (UPDATE / REGENERATE / DELETE) directly by editing the test files. New test generation (ADD) is handled separately in the next step.`
170
170
  : `### Final step
171
- After completing all assessments above, call \`skyramp_actions\` with the stateFile to execute the recommended changes.`;
171
+ After completing all assessments above, call \`skyramp_actions\` with \`stateFile: "${stateFile}"\` to execute the recommended changes.
172
+
173
+ **CRITICAL**: Do NOT create any .json or .md files. Only call skyramp_actions when done.`;
172
174
  // In inline mode, existing test counts are unknown at prompt-build time —
173
175
  // they come from skyramp_analyze_changes at runtime. Skip the count headers.
174
176
  const existingTestSection = inlineMode
@@ -1,6 +1,7 @@
1
1
  import * as crypto from "crypto";
2
2
  import { WorkspaceAuthType } from "../../utils/workspaceAuth.js";
3
3
  import { buildToolWorkflows, buildTestPatternGuidelines, buildTestQualityCriteria, buildTestExamples, buildGenerationRules, getAuthSnippets, MAX_TESTS_TO_GENERATE, MAX_RECOMMENDATIONS, MAX_CRITICAL_TESTS, } from "./recommendationSections.js";
4
+ import { CATEGORY_PRIORITY, TEST_CATEGORIES } from "../../types/TestRecommendation.js";
4
5
  function formatTestLocations(locs) {
5
6
  const entries = Object.entries(locs || {});
6
7
  if (entries.length === 0)
@@ -9,18 +10,11 @@ function formatTestLocations(locs) {
9
10
  " If a GENERATE item's resource path matches a path listed here, UPDATE that file instead of creating a new one.\n" +
10
11
  entries.map(([type, files]) => " - [" + type + "] " + files).join("\n");
11
12
  }
12
- const CATEGORY_PRIORITY = {
13
- new_endpoint: "CRITICAL", // diff-direct scenarios always fill GENERATE slots first
14
- security_boundary: "HIGH",
15
- business_rule: "HIGH",
16
- data_integrity: "HIGH",
17
- breaking_change: "HIGH",
18
- auth: "HIGH",
19
- workflow: "MEDIUM",
20
- "error-handling": "MEDIUM",
21
- "data-validation": "MEDIUM",
22
- crud: "LOW",
23
- };
13
+ // ── Priority-tier ordering (replaces numeric CATEGORY_WEIGHTS) ──
14
+ // Categories map to HIGH / MEDIUM / LOW tiers.
15
+ // Within a tier, novelty (new > modified > existing) breaks ties,
16
+ // then cross-resource, step count, and finally the deterministic SHA-256 seed.
17
+ // CATEGORY_PRIORITY and PriorityTier imported from ../../types/TestRecommendation.js
24
18
  const PRIORITY_ORDER = { CRITICAL: 4, HIGH: 3, MEDIUM: 2, LOW: 1 };
25
19
  const NOVELTY_ORDER = { new: 3, modified: 2, existing: 1 };
26
20
  function classifyNovelty(scenario, diffContext) {
@@ -201,11 +195,32 @@ Seed: ${seed} | Endpoints: ${endpointCount} | Budget: ${generateItems.length + (
201
195
 
202
196
  **Step 0 — Existing-test cross-check (MANDATORY before executing anything)**
203
197
  For every GENERATE item below, check its endpoint path and test type against the Existing Tests list (further down in the prompt).
204
- - **Contract tests**: If an existing contract test already covers that resource path → UPDATE the existing file instead of creating a new one. This does NOT count toward \`newTestsCreated\` — backfill from ADDITIONAL candidates to fill the open ADD slot.
198
+ - **Contract tests**: If an existing contract test already covers that resource path → UPDATE the existing file instead of creating a new one. This does NOT count toward \`newTestsCreated\` — backfill from ADDITIONAL candidates to fill the open ADD slot using this priority order:
199
+ 1. **PR-endpoint edge cases first**: Look for integration test candidates covering error paths, boundary values, or alternative scenarios for the SAME endpoints changed in the PR diff. If no suitable candidate exists in ADDITIONAL, derive one from your source-code enrichment findings. These are always the highest-value backfill.
200
+ 2. **Same-resource other scenarios**: Other HTTP methods or flows on the same resource group touched by the PR.
201
+ 3. **Cross-resource workflows involving the PR endpoint**: Integration scenarios that include the PR's changed endpoint as one of the steps.
202
+ 4. **Unrelated endpoint coverage (last resort)**: Tests for endpoints with no connection to the PR diff, only when ALL options above have been exhausted or would only produce UPDATEs (not new files).
203
+ **NEVER backfill with a test for a completely unrelated resource (e.g. \`POST /reviews\` when the PR only changes \`/orders\`) if any PR-endpoint edge-case integration test is feasible.**
205
204
  - **Integration/scenario tests**: Always generate as a new file via the scenario pipeline, even if an existing integration test covers the same resource. A new multi-step scenario is a distinct test. Count it toward \`newTestsCreated\`.
206
205
  - **UI tests**: Always generate as a new file. Count toward \`newTestsCreated\`.
207
206
 
208
- **Step 1 — Source-Code Enrichment (MANDATORY before executing anything)**
207
+ **Step 1 — Diversity check (MANDATORY before executing anything)**
208
+ Review the GENERATE list and verify that each item exercises a **distinct code path** — not just different input values on the same path.
209
+
210
+ **What NOT to do (these are all violations — if you catch yourself doing any of these, STOP and replace one item):**
211
+ - Do NOT generate two integration tests that both send a successful PUT/PATCH to the same endpoint and only differ in the request body values (e.g. 10% discount vs 5% discount vs 100% discount — these are the SAME test with different numbers)
212
+ - Do NOT generate two tests with the same step sequence (e.g. both are POST→PUT→GET or both are POST→PUT) where the only variation is the payload
213
+ - Do NOT count a "boundary value" as a separate test if the code path is identical to the happy path (e.g. discount=100% still returns 200 just like discount=10% — that is the same code path)
214
+ - Do NOT use different scenario names to disguise duplicate tests (e.g. "orders-put-add-items-recalculate" and "orders-put-new-endpoint-happy-path" are duplicates if both POST an order then PUT with items and expect 200)
215
+
216
+ **What TO do — each GENERATE item must exercise a different code path. Good diversity means a mix of:**
217
+ - One **happy-path** integration test (the richest scenario: create prerequisites → call the new endpoint → verify computed fields and child collections)
218
+ - One **error-path** test (trigger a distinct HTTP error status: 404 for non-existent resource, 422 for invalid input, 400 for malformed request — pick whichever the source code actually handles)
219
+ - One **state-variation** test (different operation on the same endpoint that hits different logic: empty items array, removing items instead of adding, updating quantity without changing products)
220
+
221
+ For each duplicate pair found, keep the richer item and replace the other with a test from a different category above. The replacement still targets the same PR endpoint and counts as a GENERATE item. Move the displaced item to ADDITIONAL.
222
+
223
+ **Step 2 — Source-Code Enrichment (MANDATORY before executing anything)**
209
224
  Read the source code for ALL changed files. Look for:
210
225
  - **Auth middleware** (passport, jwt.verify, authMiddleware, @requires_auth, Depends(get_current_user), @UseGuards, EnsureSessionDep, session middleware) — if found, override \`authHeader\` and \`authScheme\` in scenario and contract tool calls even if workspace.yml says authType: none. Exception: for \`skyramp_integration_test_generation\` with \`scenarioFile\`, omit auth params entirely if workspace has \`api.authType\` set (workspace handles it); if workspace has no \`authType\`, pass \`authHeader\` only.
211
226
  - Business rules and formulas (e.g. total_cost = compute * rate + memory * rate)
@@ -227,7 +242,7 @@ Quality gate — ask all three questions:
227
242
  2. "Does this test exercise a real workflow or catch a real bug?" → YES = at least MEDIUM
228
243
  3. "Does this test cover a mutation that modifies child items and triggers total/amount recalculation?" → YES = HIGH priority, and prefer it for GENERATE over simple single-field update tests for the same endpoint
229
244
 
230
- Assign category: security_boundary | business_rule | data_integrity | breaking_change | auth | workflow | error-handling | data-validation | crud
245
+ Assign category: ${TEST_CATEGORIES.join(" | ")}
231
246
 
232
247
  ${buildTestPatternGuidelines()}
233
248
 
@@ -247,7 +262,7 @@ When a qualifying candidate is inserted: place it HIGH before MEDIUM before LOW;
247
262
 
248
263
  **Unique constraints:** Unique-constraint scenarios (duplicate POST → expect 409) are pre-drafted for all resources. Before keeping them, check whether the storage backend actually enforces uniqueness — look for SQL \`UNIQUE\` indexes, Mongoose \`unique: true\`, Prisma \`@unique\`, or explicit duplicate-check logic in the source. If the backend is Redis, an in-memory store, or a schema-less DB with no explicit unique constraint in the changed files, move the unique-constraint scenario to ADDITIONAL with a note that enforcement is unconfirmed — do NOT generate it as a GENERATE item.
249
264
 
250
- **Step 2 — Execute merged plan in rank order**
265
+ **Step 3 — Execute merged plan in rank order**
251
266
  Replace any scenario that pairs unrelated resources with one reflecting actual FK relationships in the codebase.
252
267
  Use realistic request bodies from source code schemas; verify response data (not just status codes).
253
268
 
@@ -266,7 +281,7 @@ ${buildGenerationRules(isUIOnlyPR)}
266
281
 
267
282
  **Critical-category minimum:** At least ${Math.min(MAX_CRITICAL_TESTS, maxGen)} of the ${maxGen} GENERATE items MUST be from HIGH-priority categories (security_boundary, business_rule, data_integrity, breaking_change). The pre-ranked plan below already prioritises this — only override if source-code enrichment reveals a higher-value candidate.
268
283
 
269
- ### GENERATE (process these EXACTLY as listed, in order — do NOT reorder or replace any item with a different scenario; if Step 0 converts an item to UPDATE, backfill the ADD slot from ADDITIONAL)
284
+ ### GENERATE (process these EXACTLY as listed, in order — after completing Steps 0–2 above; if Step 0 converts an item to UPDATE, backfill the ADD slot from ADDITIONAL following the priority order in Step 0)
270
285
 
271
286
  ${generateBlocks || " (no pre-ranked generate items — draft your own based on endpoint analysis)"}${reserveUIGenSlot ? `
272
287