@skyramp/mcp 0.2.8-rc.1 → 0.2.9
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.
- package/build/index.js +0 -7
- package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.js +3 -1
- package/build/prompts/sut-setup/modes/dockerComposePrompt.js +3 -1
- package/build/prompts/sut-setup/shared.d.ts +20 -0
- package/build/prompts/sut-setup/shared.js +69 -7
- package/build/prompts/test-maintenance/actionsInstructions.js +5 -1
- package/build/prompts/test-maintenance/drift-analysis-prompt.d.ts +15 -2
- package/build/prompts/test-maintenance/drift-analysis-prompt.js +58 -3
- package/build/prompts/test-maintenance/driftAnalysisSections.js +7 -22
- package/build/prompts/test-maintenance/driftAnalysisShared.d.ts +14 -0
- package/build/prompts/test-maintenance/driftAnalysisShared.js +62 -0
- package/build/prompts/test-maintenance/uiDriftAnalysisSections.d.ts +38 -0
- package/build/prompts/test-maintenance/uiDriftAnalysisSections.js +228 -0
- package/build/prompts/test-recommendation/scopeAssessment.js +12 -2
- package/build/prompts/testbot/testbot-prompts.js +3 -2
- package/build/services/TestDiscoveryService.d.ts +12 -9
- package/build/services/TestDiscoveryService.js +125 -51
- package/build/services/TestDiscoveryService.test.js +235 -15
- package/build/services/TestExecutionService.d.ts +1 -1
- package/build/services/TestExecutionService.js +7 -7
- package/build/services/TestExecutionService.test.js +4 -1
- package/build/tools/submitReportTool.d.ts +5 -5
- package/build/tools/submitReportTool.js +11 -4
- package/build/tools/submitReportTool.test.js +2 -0
- package/build/tools/test-management/actionsTool.js +54 -36
- package/build/tools/test-management/analyzeChangesTool.js +11 -17
- package/build/tools/test-management/analyzeTestHealthTool.js +182 -8
- package/build/tools/test-management/analyzeTestHealthTool.test.d.ts +1 -0
- package/build/tools/test-management/analyzeTestHealthTool.test.js +468 -0
- package/build/tools/workspace/initializeWorkspaceTool.js +2 -9
- package/build/tools/workspace/initializeWorkspaceTool.test.js +9 -4
- package/build/types/TestAnalysis.d.ts +3 -0
- package/build/types/TestbotReport.d.ts +1 -1
- package/build/utils/AnalysisStateManager.d.ts +3 -21
- package/build/utils/docker.test.js +1 -1
- package/build/utils/versions.d.ts +3 -3
- package/build/utils/versions.js +1 -1
- package/build/workspace/workspace.d.ts +21 -21
- package/build/workspace/workspace.js +7 -4
- package/build/workspace/workspace.test.js +65 -6
- package/package.json +2 -2
package/build/index.js
CHANGED
|
@@ -59,13 +59,6 @@ const server = new McpServer({
|
|
|
59
59
|
},
|
|
60
60
|
instructions: `Skyramp MCP Server — generates and executes API tests (fuzz, contract, integration, E2E, UI).
|
|
61
61
|
|
|
62
|
-
## Workspace Initialization (REQUIRED before the first Skyramp tool call)
|
|
63
|
-
If the workspace root is a git repo AND \`.skyramp/workspace.yml\` does not exist:
|
|
64
|
-
1. Call \`skyramp_init_scan\` with \`workspacePath\` → follow the returned instructions to discover all services.
|
|
65
|
-
2. Call \`skyramp_init_workspace\` with \`workspacePath\`, \`services\`, and the \`scanToken\` from step 1.
|
|
66
|
-
3. Proceed with the originally requested tool.
|
|
67
|
-
Skip only if: not a git repo, \`.skyramp/workspace.yml\` already exists, or user explicitly declines.
|
|
68
|
-
|
|
69
62
|
## Rules
|
|
70
63
|
- NEVER show CLI commands. NEVER attempt to install or configure the Skyramp CLI. ALWAYS use the MCP tools provided.
|
|
71
64
|
- For UI and E2E tests, there are TWO recording modes:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { TESTBOT_WORKFLOW_PATH, buildContextBlock, buildCommonSutErrorsSection, buildLocalValidationSection, buildInputsBody, buildSutDefinitionBlock, } from "../shared.js";
|
|
1
|
+
import { TESTBOT_WORKFLOW_PATH, buildContextBlock, buildCommonSutErrorsSection, buildLocalValidationSection, buildInputsBody, buildSeedDataDiscoverySection, buildSutDefinitionBlock, buildWorkspaceInitSection, } from "../shared.js";
|
|
2
2
|
import { getPersonaPrefix } from "../../personas.js";
|
|
3
3
|
import { PromptPlan } from "../../test-recommendation/promptPlan.js";
|
|
4
4
|
// ── Step body builders ────────────────────────────────────────────────────────
|
|
@@ -70,6 +70,7 @@ function buildAdaptRestrictionsBody() {
|
|
|
70
70
|
// ── PromptPlan declaration ────────────────────────────────────────────────────
|
|
71
71
|
const _adaptPlan = new PromptPlan()
|
|
72
72
|
.addPhase("adapt", "", { headerLevel: "hidden", stepFormat: "hash" })
|
|
73
|
+
.step("INIT_WORKSPACE", "Initialise the Skyramp workspace file", () => buildWorkspaceInitSection())
|
|
73
74
|
.step("SCAN", "Understand the System Under Test in this repository", () => "")
|
|
74
75
|
.subStep("READ_SOURCES", "Read workflows and infra files", buildReadSourcesBody)
|
|
75
76
|
.subStep("CLASSIFY_STEPS", "Classify source workflow steps", buildClassifyStepsBody)
|
|
@@ -80,6 +81,7 @@ const _adaptPlan = new PromptPlan()
|
|
|
80
81
|
.subStep("PATTERN_C", "Pattern C — GHA steps replace service startup + Testbot lifecycle validation", buildPatternCBody)
|
|
81
82
|
.subStep("PATTERN_D", "Pattern D — Fully wrapped by GHA steps", buildPatternDBody)
|
|
82
83
|
.step("INPUTS", "Plan the Testbot action inputs", () => buildInputsBody())
|
|
84
|
+
.step("SEED_DATA", "Discover database seed-data conventions", () => buildSeedDataDiscoverySection())
|
|
83
85
|
.step("ADAPT", "Edit the Testbot workflow and generate SUT files", () => "")
|
|
84
86
|
.subStep("EDIT_WORKFLOW", "Edit the workflow file", buildEditWorkflowBody)
|
|
85
87
|
.subStep("CONFIGURE_AUTH", "Configure auth", buildConfigureAuthBody)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buildContextBlock, buildInputsBody, buildCommonSutErrorsSection, buildLocalValidationSection, buildSutDefinitionBlock, } from "../shared.js";
|
|
1
|
+
import { buildContextBlock, buildInputsBody, buildCommonSutErrorsSection, buildLocalValidationSection, buildSeedDataDiscoverySection, buildSutDefinitionBlock, buildWorkspaceInitSection, } from "../shared.js";
|
|
2
2
|
import { getPersonaPrefix } from "../../personas.js";
|
|
3
3
|
import { PromptPlan } from "../../test-recommendation/promptPlan.js";
|
|
4
4
|
// ── Step body builders ────────────────────────────────────────────────────────
|
|
@@ -260,9 +260,11 @@ ${GENERATE_DOCKER_COMPOSE_INSTRUCTIONS}`;
|
|
|
260
260
|
// ── PromptPlan declaration ────────────────────────────────────────────────────
|
|
261
261
|
const _composePlan = new PromptPlan()
|
|
262
262
|
.addPhase("compose", "", { headerLevel: "hidden", stepFormat: "hash" })
|
|
263
|
+
.step("INIT_WORKSPACE", "Initialise the Skyramp workspace file", () => buildWorkspaceInitSection())
|
|
263
264
|
.step("SCAN", "Understand the System Under Test in this repository", buildScanComposeBody)
|
|
264
265
|
.step("DECIDE", "Decide SUT setup strategy", buildDecideStrategyBody)
|
|
265
266
|
.step("INPUTS", "Plan the Testbot action inputs", () => buildInputsBody())
|
|
267
|
+
.step("SEED_DATA", "Discover database seed-data conventions", () => buildSeedDataDiscoverySection())
|
|
266
268
|
.step("BUILD", "Edit the Testbot workflow and generate SUT files", () => "")
|
|
267
269
|
.subStep("PR_ACCURATE", "Build PR-accurate SUT", buildPrAccurateSutSection)
|
|
268
270
|
.subStep("APPLY", "Apply the chosen strategy", buildApplyStrategyBody)
|
|
@@ -21,6 +21,26 @@ export interface SutPromptArgs {
|
|
|
21
21
|
*/
|
|
22
22
|
export declare function buildSutDefinitionBlock(): string;
|
|
23
23
|
export declare function buildContextBlock(args: SutPromptArgs): string;
|
|
24
|
+
/**
|
|
25
|
+
* Seed-data discovery block — rendered as a SUT-bootstrap step so the agent
|
|
26
|
+
* looks for existing customer seed conventions BEFORE inventing new ones.
|
|
27
|
+
* A fresh database makes list / search / filter / pagination endpoint tests
|
|
28
|
+
* shallow (only HTTP-status assertable, never response content), which
|
|
29
|
+
* collapses the data-diversity dimension of the eval. Most repos already
|
|
30
|
+
* have a seed mechanism — wire that in instead of generating one.
|
|
31
|
+
*
|
|
32
|
+
* Rendered as a top-level PromptPlan step between INPUTS and the
|
|
33
|
+
* ADAPT/BUILD step in every SUT setup mode.
|
|
34
|
+
*/
|
|
35
|
+
export declare function buildSeedDataDiscoverySection(): string;
|
|
36
|
+
/**
|
|
37
|
+
* Workspace file initialisation block — rendered as the first instruction
|
|
38
|
+
* in every SUT setup mode, before any local validation, workflow editing,
|
|
39
|
+
* or SUT-file generation. Seeds `.skyramp/workspace.yml` with the service
|
|
40
|
+
* inventory so testbot's SUT autocommit picks it up alongside the
|
|
41
|
+
* generated `.skyramp/sut/*` files.
|
|
42
|
+
*/
|
|
43
|
+
export declare function buildWorkspaceInitSection(): string;
|
|
24
44
|
export declare function buildLocalValidationSection(): string;
|
|
25
45
|
/**
|
|
26
46
|
* Returns the canonical "add to the Testbot workflow" block: every input that
|
|
@@ -31,6 +31,59 @@ export function buildContextBlock(args) {
|
|
|
31
31
|
lines.push(`</context>`);
|
|
32
32
|
return lines.join("\n");
|
|
33
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Seed-data discovery block — rendered as a SUT-bootstrap step so the agent
|
|
36
|
+
* looks for existing customer seed conventions BEFORE inventing new ones.
|
|
37
|
+
* A fresh database makes list / search / filter / pagination endpoint tests
|
|
38
|
+
* shallow (only HTTP-status assertable, never response content), which
|
|
39
|
+
* collapses the data-diversity dimension of the eval. Most repos already
|
|
40
|
+
* have a seed mechanism — wire that in instead of generating one.
|
|
41
|
+
*
|
|
42
|
+
* Rendered as a top-level PromptPlan step between INPUTS and the
|
|
43
|
+
* ADAPT/BUILD step in every SUT setup mode.
|
|
44
|
+
*/
|
|
45
|
+
export function buildSeedDataDiscoverySection() {
|
|
46
|
+
return `**First, decide if this step applies.** Skip it entirely (in your summary state \`SEED_DATA: N/A — <reason>\`, naming the specific bullet below that applied — e.g. "no stateful service", "no list endpoints in scope", "already seeded by init container") and move on when ANY of the following is true:
|
|
47
|
+
- The SUT has no database / stateful service (it's a pure stateless API, a static asset server, a webhook receiver, etc.).
|
|
48
|
+
- The compose stack contains no database container (no \`postgres\`, \`mysql\`, \`mariadb\`, \`mongodb\`, \`redis\` as the primary store, \`sqlite\`, etc.) and no application-managed persistent store.
|
|
49
|
+
- The PR diff and the existing test surface focus exclusively on auth, single-resource CRUD (POST/GET-one/PUT/DELETE on \`/foo/{id}\`), or compute endpoints — no list / search / filter / pagination / aggregate endpoints will be exercised.
|
|
50
|
+
- You can confirm from the SCAN step's findings that representative records already exist (e.g. seeded by an init container, by a migration, or by the auth/setup flow you already wired).
|
|
51
|
+
|
|
52
|
+
Only proceed with the rest of this step when the SUT has a database AND the tests will exercise list/search/filter/pagination endpoints. Otherwise an empty database is acceptable and inventing seed data adds noise.
|
|
53
|
+
|
|
54
|
+
When this step DOES apply: database services usually start empty in CI, so tests against list / search / filter / pagination endpoints will then only see \`[]\` and can assert nothing beyond HTTP status — a low data-diversity outcome. Look for an existing seeding mechanism in the repo and wire it into the SUT bootstrap.
|
|
55
|
+
|
|
56
|
+
**Discover existing project conventions first** — most repos already have one of:
|
|
57
|
+
1. A named script: \`scripts/seed*\`, \`db/seeds/*\`, \`prisma/seed.{ts,js}\`, \`apps/api/seed/*\`, \`tests/fixtures/seed*\`. Search: \`find . -iname '*seed*' -not -path '*/node_modules/*' -not -path '*/.git/*'\`.
|
|
58
|
+
2. A package.json / Makefile target: \`npm run seed\`, \`npm run db:seed\`, \`pnpm seed\`, \`yarn seed\`, \`make seed\`, \`make fixtures\`. Check \`package.json\` \`scripts\` and the project's \`Makefile\`.
|
|
59
|
+
3. A migration with sample data: Django \`loaddata\`, Rails \`db:seed\`, Knex \`seed:run\`, Prisma \`db seed\`, Alembic data migrations.
|
|
60
|
+
4. A docker-compose init service: existing compose files sometimes have a \`*-seed\`, \`*-fixtures\`, or \`init-*\` service that runs once on bring-up.
|
|
61
|
+
|
|
62
|
+
**Wire it in:**
|
|
63
|
+
- **Compose init service** — add it (or its dependency) to the testbot compose so \`targetSetupCommand\` brings it up alongside the rest. Use \`depends_on\` with \`condition: service_completed_successfully\` on dependent services so they only start after seeding finishes.
|
|
64
|
+
- **Script needing an admin/API token** (most common) — chain it INTO \`authTokenCommand\` AFTER the token is created and BEFORE the script prints the token to stdout. Route seed stdout/stderr to stderr so the captured token isn't corrupted. Make the seed call best-effort (\`|| echo "seed failed" >&2\`) so a flaky seed doesn't block the run — the token is still returned and PRs whose tests don't need seed data keep working.
|
|
65
|
+
- **Migration with sample data** — ensure the migration runs as part of \`targetSetupCommand\` (some ORMs auto-run on container start, others need an explicit \`migrate\` / \`db:seed\` step before tests can hit the API).
|
|
66
|
+
|
|
67
|
+
**If NO existing convention is found**, generate a minimal seed step (≥3 entities per primary resource the tests will query, e.g., ≥3 repos / ≥3 issues / ≥3 users for a forge; ≥3 products / ≥3 orders for a shop). Call this out explicitly in your summary so the user knows a richer fixture may be needed for production-quality tests.
|
|
68
|
+
|
|
69
|
+
**Never** hard-code production data, real customer credentials, or PII in seeds. Seed identifiers should be obviously test-only (\`acme-co\`, \`alpha-svc\`, \`testbot-admin\`).`;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Workspace file initialisation block — rendered as the first instruction
|
|
73
|
+
* in every SUT setup mode, before any local validation, workflow editing,
|
|
74
|
+
* or SUT-file generation. Seeds `.skyramp/workspace.yml` with the service
|
|
75
|
+
* inventory so testbot's SUT autocommit picks it up alongside the
|
|
76
|
+
* generated `.skyramp/sut/*` files.
|
|
77
|
+
*/
|
|
78
|
+
export function buildWorkspaceInitSection() {
|
|
79
|
+
return `Before generating any SUT setup files, check whether \`.skyramp/workspace.yml\` exists at the root of the repository being bootstrapped (the path shown next to "Repository path:" in the context block above — the repo whose PR triggered Testbot; in multi-repo runs this is the primary repo, not the sibling repos checked out as related repos). If it doesn't:
|
|
80
|
+
1. Call \`skyramp_init_scan\` with \`workspacePath\` set to that repository root → follow the returned instructions to discover all services.
|
|
81
|
+
2. Call \`skyramp_init_workspace\` with the same \`workspacePath\`, \`services\`, and the \`scanToken\` from step 1.
|
|
82
|
+
|
|
83
|
+
This produces a baseline \`.skyramp/workspace.yml\` listing the services Testbot will test. Later steps in this SUT bootstrap run write \`.skyramp/sut/*\` files alongside it. You might have to update the per-service \`runtimeDetails.serverStartCommand\` to match the new SUT files created, so Testbot brings up the services correctly.
|
|
84
|
+
|
|
85
|
+
Also verify each service's \`api.baseUrl\` includes the API path prefix (e.g. \`/api\`, \`/api/v1\`, \`/v1\`) — not just \`host:port\`. A bare host means every test request hits the root and 404s. Derive the prefix from the strongest available signal: OpenAPI \`servers[].url\` or \`basePath\`; the app's routing config (FastAPI \`root_path\`, Express \`app.use('/api/v1', ...)\`, Rails routes prefix, Django \`urlpatterns\` mount); or the longest common leading path across the existing test suite's endpoints. If none of those yield a prefix, leave it bare AND flag it in your summary so the user can review.`;
|
|
86
|
+
}
|
|
34
87
|
export function buildLocalValidationSection() {
|
|
35
88
|
return `Before reporting success, exercise the adapted workflow locally. Testbot's external fix loop only retries when SUT lifecycle commands are set on the Testbot action. When the SUT is brought up by surrounding GHA steps (skipTargetSetup: 'true'), the fix loop is skipped — so the local check below is the only safety net before the workflow is committed.
|
|
36
89
|
Run commands one at a time in your shell — individual execution pinpoints failures far faster than running the whole workflow at once. Any non-zero exit is a fix-needed signal: adjust the workflow, re-run the failing command, and do not proceed until it passes.
|
|
@@ -39,7 +92,7 @@ Run commands one at a time in your shell — individual execution pinpoints fail
|
|
|
39
92
|
b. \`.skyramp/sut/get-auth-token.sh\` if present — run it and confirm it prints a non-empty token to stdout.
|
|
40
93
|
c. Validate any standalone Dockerfile referenced by GHA \`steps:\` (not by a compose service) with \`docker build\`. Do NOT also run \`docker build\` on Dockerfiles that belong to a compose service — \`docker compose build\` (next step) already builds them in one BuildKit graph; doubling up wastes ~5-10 GB of cache per service.
|
|
41
94
|
d. Every docker-compose file the workflow references — run \`docker compose -f <path> config\` to validate the YAML, then \`docker compose -f <path> build\`.
|
|
42
|
-
e. **Disk hygiene between heavy builds.** Between any two build commands (Dockerfile, compose, helper scripts that build), if the runner is under ~5 GB free or you see \`no space left on device\`,
|
|
95
|
+
e. **Disk hygiene between heavy builds.** Between any two build commands (Dockerfile, compose, helper scripts that build), if the runner is under ~5 GB free or you see \`no space left on device\`, prune docker BuildKit cache and dangling images before the next build (keep the running stack intact). Skipping this on heavy SUTs exhausts the standard runner disk mid-validation.
|
|
43
96
|
2. Validate the SUT lifecycle — choose the branch that matches the chosen pattern:
|
|
44
97
|
a. If lifecycle commands are set on the Testbot action (skipTargetSetup is unset):
|
|
45
98
|
i. Run \`targetSetupCommand\` — confirm exit 0.
|
|
@@ -68,8 +121,16 @@ Target lifecycle inputs (control how Testbot starts, validates, and stops the SU
|
|
|
68
121
|
b. For PR-source builds use \`docker compose up -d --build\` or the equivalent for helm/script-based SUTs.
|
|
69
122
|
c. With a dedicated compose file (reused or generated), point at it explicitly: \`docker compose -f .skyramp/sut/docker-compose.testbot.yml --project-directory . up -d --build\`.
|
|
70
123
|
d. Override whenever the source workflow's start sequence differs from the default.
|
|
71
|
-
2. \`targetReadyCheckCommand\` — polling command
|
|
72
|
-
a. **CRITICAL:
|
|
124
|
+
2. \`targetReadyCheckCommand\` — polling command Testbot runs repeatedly after setup until it exits 0 (service ready) or the timeout fires.
|
|
125
|
+
a. **CRITICAL: must cover EVERY service the tests will hit, not just the backend.** Chain checks with \`&&\` so the command exits 0 only when ALL services respond. A single-service check on a multi-service SUT is the leading cause of "element not visible" / "heading not visible" UI test failures — Playwright lands on a partially-rendered page because only the API was up when the gate passed. Inventory of services to include:
|
|
126
|
+
- Every API service in workspace.yml with an \`api.baseUrl\` → curl an unauthenticated endpoint (health / version / status).
|
|
127
|
+
- Every UI service with a UI \`baseUrl\` (or a frontend port in compose) → curl \`/\` or a known asset like \`/assets/index.js\` (more reliable than \`/\` for SPAs that hydrate lazily).
|
|
128
|
+
- Skip auth-required endpoints — they 401 before the SUT is testable and mask the readiness signal.
|
|
129
|
+
Examples:
|
|
130
|
+
- API only: \`curl -sf http://localhost:8080/api/health\`
|
|
131
|
+
- API + UI: \`curl -sf http://localhost:8080/api/health && curl -sf http://localhost:3000/\`
|
|
132
|
+
Required in every pattern (explicit \`targetSetupCommand\`, the \`docker compose up -d\` default, AND \`skipTargetSetup: 'true'\`). Without a real readiness signal (neither this nor a workspace service \`baseUrl\`), testbot's fail-early gate throws \`Unable to verify service readiness\` and the action exits non-zero. It is also what gates a backgrounded \`targetSetupCommand\` — without it Testbot can't tell when the service is actually listening.
|
|
133
|
+
b. Set \`targetReadyCheckDiagnosticsCommand\` to a command that explains why the SUT isn't healthy — output is surfaced in the failure PR comment when the ready check times out. For docker-compose SUTs, scope it to the project's compose file (container state + recent service logs). For non-docker SUTs, a tail of the server log.
|
|
73
134
|
3. \`targetReadyCheckTimeout\` — seconds to wait for \`targetReadyCheckCommand\` to succeed.
|
|
74
135
|
a. Default: \`1800\` (30 min) — sized for cold \`docker compose --build\` runs on multi-service stacks. The loop exits on the first success, so fast services pay no extra cost. Override only if you want to fail faster on stuck setups (e.g. \`'300'\` for a small single-service SUT).
|
|
75
136
|
4. \`targetTeardownCommand\` — shell command that stops and cleans up the SUT after tests (e.g., \`docker compose down -v\`, or \`docker compose -f .skyramp/sut/docker-compose.testbot.yml --project-directory . down -v\` for a dedicated compose file).
|
|
@@ -89,19 +150,20 @@ Auth inputs (used by Testbot to authenticate against the running SUT during test
|
|
|
89
150
|
b. Use \`\${{ secrets.SKYRAMP_UI_CREDENTIALS }}\` if the secret exists, otherwise use credentials seeded during SUT setup.
|
|
90
151
|
Workflow / job level settings (set on the workflow or job, not as \`skyramp/testbot\` action inputs):
|
|
91
152
|
8. Secrets / environment variables — pass every secret/env var the setup needs into the workflow via an \`env:\` block or \`secrets: inherit\`, and add any dependency-resolution steps/jobs the SUT requires.
|
|
92
|
-
9. \`runs-on\` runner label —
|
|
153
|
+
9. \`runs-on\` runner label — leave whatever value the Testbot installer wrote and the source workflow used. Don't override it. Picking a runner SKU is an account-level decision that depends on provisioning the agent can't see from inside the repo; a wrong choice queues the job forever with no runner ever picking it up. If the SUT genuinely doesn't fit on the chosen runner, the disk-pressure mitigation in "Common failure modes" #9 handles it; an unprovisioned-runner stall does not.
|
|
93
154
|
|
|
94
155
|
#### What to do
|
|
95
156
|
Edit \`${TESTBOT_WORKFLOW_PATH}\` in place (already created by the Testbot installer) — do NOT recreate it. Add only what the SUT needs onto the existing \`skyramp/testbot\` action step, alongside the inputs the installer already added:
|
|
96
157
|
1. The 4 target lifecycle inputs — \`targetSetupCommand\`, \`targetReadyCheckCommand\`, \`targetReadyCheckTimeout\`, \`targetTeardownCommand\`.
|
|
97
158
|
2. The 2 auth inputs when the SUT needs them — \`authTokenCommand\` and/or \`uiCredentials\`.
|
|
98
159
|
3. Any secrets / environment variables and dependency-resolution steps/jobs the setup needs (input 8).
|
|
99
|
-
4.
|
|
160
|
+
4. Leave the existing \`runs-on\` value untouched (input 9).
|
|
100
161
|
5. Use a daemon/detached form for \`targetSetupCommand\` — \`docker compose up -d --build\`, \`nohup npm start &\`, \`./server &\`, \`pnpm --prefix ./apps/studio start &\`. The command should launch the service and exit; \`targetReadyCheckCommand\` is what gates readiness. Testbot backgrounds foreground-blocking commands (\`npm start\`, \`python app.py\`, \`./server\`) automatically after a 5-minute timeout as a safety net, so they will eventually work — but daemon mode is faster (no 5-minute wait) and surfaces startup crashes clearly via the command's exit code. If no daemon form is available and the repo has no \`docker-compose.yml\`, wrap the foreground command in compose (a \`Dockerfile\` + \`docker-compose.yml\` running \`npm start\`) so it can be launched with \`docker compose up -d --build\`. Only fall back to a GHA pre-step with \`&\` (e.g. \`run: npm start &\`) if Docker is not feasible.
|
|
162
|
+
6. Stack-specific testability flags — some frameworks gate test-harness affordances behind a container env var. Common cases: Flutter web needs \`IS_TESTING=true\` so \`ensureSemantics()\` exposes the widget tree to Playwright (without it every selector finds nothing even though the app is up); some React/Angular builds need \`NODE_ENV\` / framework-specific build flags for \`testID\` propagation. Set these in the compose \`environment:\` block on the affected service (or as a \`Dockerfile ENV\`) — NOT as a shell var in \`targetSetupCommand\`. The var must be in the container's env at boot, not just the shell that runs the setup command. If selectors find nothing but the app is up, check for a missing flag like this first.
|
|
101
163
|
|
|
102
164
|
#### What not to do
|
|
103
165
|
1. IMPORTANT — do NOT remove or change the existing \`sutSetupMode\` input (nor \`sutSourceWorkflowFile\` / \`sutSourceDockerComposeFile\`). Leave \`sutSetupMode\` exactly as provided: Testbot flips it to \`none\` automatically after validation succeeds, and the committed \`sutSetupMode\` value is the signal that tells the next CI run to validate this adapted workflow rather than run tests. Deleting it or setting it to \`none\` breaks the bootstrap/validation cycle.
|
|
104
|
-
2. Do not point the SUT at a prebuilt upstream image (e.g. \`image: org/app:latest\` or a fixed commit SHA from \`main\`) for any application service under test. The image will lag the PR and Testbot will validate stale code.
|
|
166
|
+
2. Do not point the SUT at a prebuilt upstream image (e.g. \`image: org/app:latest\` or a fixed commit SHA from \`main\`) for any application service under test. The image will lag the PR and Testbot will validate stale code. In case of multi-mode containers (\`Dockerfile.bdd\`, \`Dockerfile.e2e\`, \`Dockerfile.integration\`, \`Dockerfile.test\`), create the necessary file in \`.skyramp/sut/\` with a PR-sourced build (e.g. \`.skyramp/sut/Dockerfile.bdd\` referenced from the testbot compose's \`build:\`). Do not edit existing Dockerfiles or compose files in the repo. If the multi-mode container's bootstrap step depends on a seeded user, ensure SEED_DATA creates it before this container tries to log in.
|
|
105
167
|
3. Do not point the SUT at a remote staging or production environment for application services under test. Staging code drifts from PR source and turns Testbot's results into noise. External infra is acceptable only for sidecar dependencies the PR does not change (e.g. a managed test database).
|
|
106
168
|
4. Do not use a foreground-blocking command as \`targetSetupCommand\` (e.g. \`npm start\`, \`pnpm --prefix ./apps/studio start\`, \`python app.py\`, \`prefect server start\`, \`./server\`) — see input 5 in "What to do" above for daemon forms and the compose-wrap fallback.
|
|
107
169
|
5. Do not leave server processes running between fix-loop retries. If \`targetSetupCommand\` starts a direct process (not \`docker compose\`), always set \`targetTeardownCommand\` to kill it (e.g. \`pkill -f "node dist/index.js"\` or \`docker compose down\`). Without teardown, retried setup attempts will fail with \`EADDRINUSE\` because the previous process still holds the port.
|
|
@@ -127,6 +189,6 @@ export function buildCommonSutErrorsSection() {
|
|
|
127
189
|
6. permission_denied — script is not executable (\`chmod +x\`) or volume mount has wrong ownership.
|
|
128
190
|
7. auth_endpoint_404 / auth_credentials_invalid — the auth script hits a wrong path, or the database has no seeded user. The auth script must create the user before login when starting from a fresh DB.
|
|
129
191
|
8. missing env vars / secrets — required env vars are unset on the runner. Either default them in the script or pass them through workflow \`env:\`.
|
|
130
|
-
9. no_space_left — the runner ran out of disk space (\`No space left on device\`).
|
|
192
|
+
9. no_space_left — the runner ran out of disk space (\`No space left on device\`). Prune the docker BuildKit cache and dangling images at the start of both \`targetSetupCommand\` and \`targetTeardownCommand\` so each fix-loop retry begins with a clean slate; without it, cache from the previous attempt accumulates across retries on heavy SUTs. Do not change \`runs-on\` — see input 9 above.
|
|
131
193
|
Do not add an in-prompt fix loop — Testbot's external validator will retry with concrete error context if any of these occur.`;
|
|
132
194
|
}
|
|
@@ -15,7 +15,11 @@ export function buildFileRenameStrategy() {
|
|
|
15
15
|
}
|
|
16
16
|
/** Strategy string for in-place UPDATE edits. */
|
|
17
17
|
export function buildUpdateStrategy() {
|
|
18
|
-
return `For each file in update_context, apply the changes described in context. Preserve all existing test logic — only add or adjust what is described in context.
|
|
18
|
+
return `For each file in update_context, apply the changes described in context. Preserve all existing test logic — only add or adjust what is described in context.
|
|
19
|
+
|
|
20
|
+
UPDATE is an in-place edit to the existing baseline file — do not create a new test file to cover the same page or component.
|
|
21
|
+
|
|
22
|
+
After applying all edits, call skyramp_enhance_assertions with each updated file path.`;
|
|
19
23
|
}
|
|
20
24
|
/** Strategy string for REGENERATE — call generation tool to overwrite the file. */
|
|
21
25
|
export function buildRegenerateStrategy() {
|
|
@@ -1,4 +1,17 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type UiDriftParams = {
|
|
2
|
+
tests: Array<{
|
|
3
|
+
testFile: string;
|
|
4
|
+
}>;
|
|
5
|
+
changedFrontendFiles: string[];
|
|
6
|
+
blueprintCaptured?: boolean;
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Build the drift analysis prompt.
|
|
10
|
+
* - apiTests: tests to assess for API endpoint drift (always included unless empty and ui is provided).
|
|
11
|
+
* - ui: when provided, appends a UI drift section for component/browser tests.
|
|
12
|
+
* Omit when no frontend files changed or no UI tests exist.
|
|
13
|
+
*/
|
|
14
|
+
export declare function buildDriftAnalysisPrompt(stateFile: string | undefined, apiTests: Array<{
|
|
2
15
|
testFile: string;
|
|
3
16
|
source?: string;
|
|
4
|
-
}
|
|
17
|
+
}>, ui?: UiDriftParams): string;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { buildActionDecisionTree, buildCheckAdditiveFields, buildCheckEndpointExistence, buildCheckResponseShape, buildCheckAuthAndAuthorization, buildCheckBehavioralContract, buildCheckAssignAction, buildDriftOutputChecklist, } from "./driftAnalysisSections.js";
|
|
2
|
+
import { buildUiActionDecisionTree, buildUiCheckRouteExistence, buildUiCheckSelectors, buildUiCheckPageObjects, buildUiCheckBehavioralChanges, buildUiCheckAssignAction, buildUiCheckExportedSymbols, buildUiDriftOutputChecklist, } from "./uiDriftAnalysisSections.js";
|
|
3
|
+
import { RECOMMENDATIONS_INSTRUCTION } from "./driftAnalysisShared.js";
|
|
2
4
|
import { PromptPlan } from "../test-recommendation/promptPlan.js";
|
|
3
|
-
const
|
|
5
|
+
const _apiPlan = new PromptPlan()
|
|
4
6
|
.addPhase("maintenance", "Test Maintenance Assessment", {
|
|
5
7
|
headerLevel: "##",
|
|
6
8
|
stepFormat: "hash",
|
|
@@ -14,6 +16,59 @@ const _plan = new PromptPlan()
|
|
|
14
16
|
.subStep("ASSIGN_ACTION", "Assign action", () => buildCheckAssignAction())
|
|
15
17
|
.step("CALL_TOOL", "Submit recommendations", (p) => buildDriftOutputChecklist(p.stateFile, p.existingTests))
|
|
16
18
|
.done();
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
const _uiPlan = new PromptPlan()
|
|
20
|
+
.addPhase("ui_maintenance", "UI Test Drift Assessment", {
|
|
21
|
+
headerLevel: "##",
|
|
22
|
+
stepFormat: "hash",
|
|
23
|
+
})
|
|
24
|
+
.step("UI_SYMBOL_PRESCAN", "Exported symbol pre-scan — find additional impacted tests via grep", (p) => buildUiCheckExportedSymbols(p.changedFrontendFiles))
|
|
25
|
+
.step("UI_ASSESS", "UI Action Decision Tree — assess each UI test against the diff and blueprints", () => buildUiActionDecisionTree())
|
|
26
|
+
.subStep("UI_ROUTE_EXISTENCE", "Route existence", () => buildUiCheckRouteExistence())
|
|
27
|
+
.subStep("UI_SELECTORS", "Selector validity", () => buildUiCheckSelectors())
|
|
28
|
+
.subStep("UI_PAGE_OBJECTS", "Page object/fixture existence", () => buildUiCheckPageObjects())
|
|
29
|
+
.subStep("UI_BEHAVIOR", "Component behavior changes", () => buildUiCheckBehavioralChanges())
|
|
30
|
+
.subStep("UI_ASSIGN_ACTION", "Assign action", () => buildUiCheckAssignAction())
|
|
31
|
+
.step("UI_CALL_TOOL", "Submit recommendations", (p) => buildUiDriftOutputChecklist(p.stateFile, p.tests))
|
|
32
|
+
.done();
|
|
33
|
+
/**
|
|
34
|
+
* Build the drift analysis prompt.
|
|
35
|
+
* - apiTests: tests to assess for API endpoint drift (always included unless empty and ui is provided).
|
|
36
|
+
* - ui: when provided, appends a UI drift section for component/browser tests.
|
|
37
|
+
* Omit when no frontend files changed or no UI tests exist.
|
|
38
|
+
*/
|
|
39
|
+
export function buildDriftAnalysisPrompt(stateFile, apiTests, ui) {
|
|
40
|
+
const parts = [];
|
|
41
|
+
// Include API drift when there are API tests, or when UI drift is not running
|
|
42
|
+
// (ensures skyramp_actions is always reachable even if apiTests is empty).
|
|
43
|
+
if (apiTests.length > 0 || !ui) {
|
|
44
|
+
parts.push(`<drift_analysis_rules>\n${_apiPlan.render({ stateFile, existingTests: apiTests })}\n</drift_analysis_rules>`);
|
|
45
|
+
}
|
|
46
|
+
if (ui) {
|
|
47
|
+
const signalNote = ui.blueprintCaptured
|
|
48
|
+
? `blueprintCaptured=true — use \`browser_blueprint_diff\` for tests that navigate pages.`
|
|
49
|
+
: `blueprintCaptured=false — use source diff only. Read each test to determine what it exercises.`;
|
|
50
|
+
const contextHeader = `<ui_analysis_context>
|
|
51
|
+
**UI Test Drift Analysis Context**
|
|
52
|
+
|
|
53
|
+
**Signal:** ${signalNote}
|
|
54
|
+
|
|
55
|
+
**Changed Frontend Files:**
|
|
56
|
+
${ui.changedFrontendFiles.length > 0
|
|
57
|
+
? ui.changedFrontendFiles.map(f => `- ${f}`).join("\n")
|
|
58
|
+
: "(none)"}
|
|
59
|
+
|
|
60
|
+
**Tests to Assess:**
|
|
61
|
+
${ui.tests.length > 0
|
|
62
|
+
? ui.tests.map(t => `- ${t.testFile}`).join("\n")
|
|
63
|
+
: "(none)"}
|
|
64
|
+
|
|
65
|
+
**Instructions:**
|
|
66
|
+
For each test above, read it to determine its type, then apply the scope gate from the decision tree. Assign the appropriate action (DELETE, REGENERATE, UPDATE, VERIFY, IGNORE).
|
|
67
|
+
</ui_analysis_context>
|
|
68
|
+
|
|
69
|
+
`;
|
|
70
|
+
parts.push(contextHeader + `<ui_drift_analysis_rules>\n${_uiPlan.render({ stateFile, tests: ui.tests, changedFrontendFiles: ui.changedFrontendFiles, blueprintCaptured: ui.blueprintCaptured })}\n</ui_drift_analysis_rules>`);
|
|
71
|
+
}
|
|
72
|
+
parts.push(RECOMMENDATIONS_INSTRUCTION);
|
|
73
|
+
return parts.join("\n\n");
|
|
19
74
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Modular section builders for the Drift Analysis prompt,
|
|
2
|
+
* Modular section builders for the API Drift Analysis prompt,
|
|
3
3
|
* mirroring the recommendationSections.ts pattern.
|
|
4
4
|
*/
|
|
5
5
|
import { AUTH_MIDDLEWARE_PATTERNS_STR } from "../../utils/workspaceAuth.js";
|
|
6
|
+
import { buildApiAssignAction } from "./driftAnalysisShared.js";
|
|
6
7
|
// TODO: Replace the open-ended diff-line categories below with a two-tier structure:
|
|
7
8
|
// Tier 1 — mechanical patterns (specific token + file-path criteria, zero judgment needed).
|
|
8
9
|
// Tier 2 — residual open-ended scan for anything observable not covered by tier 1.
|
|
@@ -177,24 +178,9 @@ Diff signals and actions:
|
|
|
177
178
|
**Reachability for behavioral changes:** The service/interface scope gate still applies — if the test targets a completely different service or protocol, IGNORE. However, do NOT use the absence of a route or output layer file in the diff as grounds for IGNORE. Behavioral changes (new error branches, new validation, status-code changes) are observable from any test calling the same endpoint, even when the logic lives in an internal handler, middleware, or utility file rather than the route file itself.`;
|
|
178
179
|
}
|
|
179
180
|
export function buildCheckAssignAction() {
|
|
180
|
-
return
|
|
181
|
-
|
|
182
|
-
**Every action requires a specific rationale — including IGNORE:**
|
|
183
|
-
- UPDATE / REGENERATE / DELETE: quote the specific diff line that triggered it.
|
|
184
|
-
- VERIFY: name the uncertain element (e.g. "model-only change, cannot confirm field is exposed without checking the output layer").
|
|
185
|
-
- IGNORE: name the changed file(s) from the diff AND explain why they cannot reach this test's endpoint (e.g. "diff only touches \`auth/session-service.js\` — this test targets \`/api/v1/orders\` which has no session dependency"). You must reference at least one specific changed file from the diff. Generic "unrelated endpoint" or "service boundary" without naming the changed file is not sufficient.
|
|
186
|
-
|
|
187
|
-
- If the Additive Fields check flagged a new field with output layer signal confirmed in the diff → action is UPDATE. If the Additive Fields check returned VERIFY (model-only signal, no output layer change) → action remains VERIFY.
|
|
181
|
+
return buildApiAssignAction(`- If the Additive Fields check flagged a new field with output layer signal confirmed in the diff → action is UPDATE. If the Additive Fields check returned VERIFY (model-only signal, no output layer change) → action remains VERIFY.
|
|
188
182
|
- **Scope gate:** If the changed code is clearly not reachable through the service or base URL this test targets, assign IGNORE.
|
|
189
|
-
- **When uncertain, use VERIFY not IGNORE:** If the diff touches a model or migration and this test's endpoint reads from that model, assign VERIFY — you cannot confirm from the diff alone whether the field is exposed
|
|
190
|
-
- **Pre-commit verification — confirm all three before finalizing UPDATE/REGENERATE/DELETE:**
|
|
191
|
-
1. You can quote a specific diff line this test's endpoint observes that triggered the action.
|
|
192
|
-
2. The changed code is reachable through this test's service and base URL.
|
|
193
|
-
3. For REGENERATE: every assertion in the file is invalid, not just some — if you can patch N paths, it is UPDATE.
|
|
194
|
-
If any check fails, downgrade to VERIFY or IGNORE.
|
|
195
|
-
- **For user-written (external) tests** marked \`[external]\` in the test list:
|
|
196
|
-
- UPDATE is permitted — targeted edits only (fix renamed URL, add assertion for new field, add a new test case for a new scenario). Match the style of the existing file: use the same test framework, assertion helpers, and request pattern already present.
|
|
197
|
-
- REGENERATE and DELETE are **not permitted** — assign those actions in your recommendations but \`skyramp_actions\` will surface them as report-only findings for the developer to act on. Do NOT attempt to rewrite or delete a user-authored test file.`;
|
|
183
|
+
- **When uncertain, use VERIFY not IGNORE:** If the diff touches a model or migration and this test's endpoint reads from that model, assign VERIFY — you cannot confirm from the diff alone whether the field is exposed.`);
|
|
198
184
|
}
|
|
199
185
|
export function buildCheckAdditiveFields() {
|
|
200
186
|
return `Even if existing assertions still pass, new response fields on a covered endpoint need a new assertion. A field being optional or nullable does not make it IGNORE — it still needs to be covered.
|
|
@@ -213,7 +199,7 @@ To determine which applies: look at whether an explicit output layer (DTO, seria
|
|
|
213
199
|
**Do not assign IGNORE because the field is optional or nullable** — "won't break existing assertions" is not the bar. The bar is "does the test now cover this endpoint's full contract?" A new field, even optional, means the contract changed and the test is incomplete.`;
|
|
214
200
|
}
|
|
215
201
|
export function buildDriftOutputChecklist(stateFile, existingTests) {
|
|
216
|
-
const finalStep = `Call \`skyramp_actions\` with \`stateFile: "${stateFile ?? "<stateFile>"}"
|
|
202
|
+
const finalStep = `Call \`skyramp_actions\` with \`stateFile: "${stateFile ?? "<stateFile>"}"\`.`;
|
|
217
203
|
const hasTests = (existingTests?.length ?? 0) > 0;
|
|
218
204
|
const testList = hasTests
|
|
219
205
|
? existingTests.map((t) => {
|
|
@@ -225,7 +211,7 @@ export function buildDriftOutputChecklist(stateFile, existingTests) {
|
|
|
225
211
|
? `\nNo existing tests were found. Call \`skyramp_actions\` with \`recommendations: []\` and explain in your report why no maintenance was needed (e.g. "no existing tests cover the changed endpoints" or "PR adds a new endpoint with no prior coverage").\n`
|
|
226
212
|
: "";
|
|
227
213
|
const symbolDiscoveryStep = hasTests
|
|
228
|
-
? `**Symbol import discovery (before assessing tests):** If the diff adds or modifies class, function, or type definitions, grep the repository for test files that import those symbol names. Add any matched test files not already in the list below to your assessment — these files may need updating even if their path doesn't match the changed resource name. Include all discovered files in the \`recommendations[]\` passed to \`skyramp_actions
|
|
214
|
+
? `**Symbol import discovery (before assessing tests):** If the diff adds or modifies class, function, or type definitions, grep the repository for test files that import those symbol names. Add any matched test files not already in the list below to your assessment — these files may need updating even if their path doesn't match the changed resource name. Include all discovered files in the \`recommendations[]\` passed to \`skyramp_actions\`.`
|
|
229
215
|
: "";
|
|
230
216
|
const existingTestSection = `**Existing tests (${existingTests?.length ?? 0} total) — assess ALL of the following:**
|
|
231
217
|
${testList}
|
|
@@ -257,8 +243,7 @@ Test: {testFile}
|
|
|
257
243
|
Action: DELETE
|
|
258
244
|
Rationale: DELETE because {quoted diff signal}; all covered endpoints removed
|
|
259
245
|
\`\`\`
|
|
260
|
-
|
|
261
|
-
Be concise — one line per IGNORE/VERIFY entry.`;
|
|
246
|
+
Be concise — one line per VERIFY/IGNORE entry.`;
|
|
262
247
|
const sections = [symbolDiscoveryStep, existingTestSection, finalStep].filter(Boolean);
|
|
263
248
|
return `<output_format>
|
|
264
249
|
Complete ALL of the following:
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/** Shared drift prompt sections (assign-action, external test policy) for API and UI prompts. */
|
|
2
|
+
/** One source of truth for the recommendations[] contract passed to skyramp_actions. */
|
|
3
|
+
export declare const RECOMMENDATIONS_INSTRUCTION = "Pass VERIFY, UPDATE, REGENERATE, and DELETE entries to `recommendations[]`. Omit IGNORE.";
|
|
4
|
+
export declare function buildSharedExternalTestPolicy(): string;
|
|
5
|
+
/**
|
|
6
|
+
* Assign-action section for the API drift prompt.
|
|
7
|
+
* @param domainNotes API-specific bullets (additive-fields guidance, scope-gate note).
|
|
8
|
+
*/
|
|
9
|
+
export declare function buildApiAssignAction(domainNotes: string): string;
|
|
10
|
+
/**
|
|
11
|
+
* Assign-action section for the UI drift prompt.
|
|
12
|
+
* @param domainNotes UI-specific bullets (component/widget matching notes).
|
|
13
|
+
*/
|
|
14
|
+
export declare function buildUiAssignAction(domainNotes: string): string;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/** Shared drift prompt sections (assign-action, external test policy) for API and UI prompts. */
|
|
2
|
+
/** One source of truth for the recommendations[] contract passed to skyramp_actions. */
|
|
3
|
+
export const RECOMMENDATIONS_INSTRUCTION = `Pass VERIFY, UPDATE, REGENERATE, and DELETE entries to \`recommendations[]\`. Omit IGNORE.`;
|
|
4
|
+
export function buildSharedExternalTestPolicy() {
|
|
5
|
+
return `**For user-written (external) tests** marked \`[external]\` in the test list:
|
|
6
|
+
- UPDATE is permitted — pass the exact file that needs editing as \`testFile\`. This can be the test file itself, a snapshot file, a page object, or a fixture. \`skyramp_actions\` will edit that file using \`updateInstructions\`; application source files are automatically rejected.
|
|
7
|
+
- REGENERATE and DELETE: assign these actions when the evidence clearly warrants them (page gone → DELETE, component structure fundamentally changed → REGENERATE). \`skyramp_actions\` handles execution restrictions at apply time — assign the action that the evidence supports. Leave user-authored test files unchanged; edits go only through \`skyramp_actions\` with explicit \`updateInstructions\`. VERIFY means "uncertain, need more context" — assign DELETE or REGENERATE when you have confirmed the failure, and VERIFY only when you genuinely cannot determine impact without more information.`;
|
|
8
|
+
}
|
|
9
|
+
/** Shared boilerplate: VERIFY rule, pre-commit structure, and external policy. */
|
|
10
|
+
function buildAssignActionCore(opts) {
|
|
11
|
+
return `Based on the above checks, choose the action (IGNORE / VERIFY / UPDATE / REGENERATE / DELETE) and provide a 1-2 sentence rationale.
|
|
12
|
+
|
|
13
|
+
**Every action requires a specific rationale — including IGNORE:**
|
|
14
|
+
- UPDATE / REGENERATE / DELETE: ${opts.triggerEvidence}.
|
|
15
|
+
- VERIFY: name the uncertain element (e.g. ${opts.verifyExample}).
|
|
16
|
+
- IGNORE: ${opts.ignoreGuidance}.
|
|
17
|
+
|
|
18
|
+
${opts.domainNotes}
|
|
19
|
+
|
|
20
|
+
**When uncertain, use VERIFY not IGNORE:** If the diff touches code this test could exercise but you cannot confirm impact without reading more context, assign VERIFY — do not default to IGNORE.
|
|
21
|
+
|
|
22
|
+
**Pre-commit verification — confirm before finalizing UPDATE/REGENERATE/DELETE:**
|
|
23
|
+
1. You can quote a specific diff line${opts.blueprintSuffix} this test observes that triggered the action.
|
|
24
|
+
2. ${opts.reachabilityCheck}
|
|
25
|
+
3. ${opts.regenerateCheck}
|
|
26
|
+
4. ${opts.deleteCheck}
|
|
27
|
+
If any check fails, downgrade to VERIFY or IGNORE.
|
|
28
|
+
|
|
29
|
+
${buildSharedExternalTestPolicy()}`;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Assign-action section for the API drift prompt.
|
|
33
|
+
* @param domainNotes API-specific bullets (additive-fields guidance, scope-gate note).
|
|
34
|
+
*/
|
|
35
|
+
export function buildApiAssignAction(domainNotes) {
|
|
36
|
+
return buildAssignActionCore({
|
|
37
|
+
triggerEvidence: "quote the specific diff line that triggered it",
|
|
38
|
+
ignoreGuidance: `name the changed file(s) from the diff AND explain why they cannot reach this test's endpoint (e.g. "diff only touches \`auth/session-service.js\` — this test targets \`/api/v1/orders\` which has no session dependency"). You must reference at least one specific changed file. Generic "unrelated endpoint" or "service boundary" without naming the changed file is not sufficient`,
|
|
39
|
+
verifyExample: `"model-only change, cannot confirm field is exposed without checking the output layer"`,
|
|
40
|
+
blueprintSuffix: "",
|
|
41
|
+
reachabilityCheck: "The changed code is reachable through this test's service and base URL.",
|
|
42
|
+
regenerateCheck: "For REGENERATE: every assertion in the file is invalid, not just some — if you can patch N paths, it is UPDATE.",
|
|
43
|
+
deleteCheck: "For DELETE: all covered endpoints no longer exist.",
|
|
44
|
+
domainNotes,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Assign-action section for the UI drift prompt.
|
|
49
|
+
* @param domainNotes UI-specific bullets (component/widget matching notes).
|
|
50
|
+
*/
|
|
51
|
+
export function buildUiAssignAction(domainNotes) {
|
|
52
|
+
return buildAssignActionCore({
|
|
53
|
+
triggerEvidence: "quote the specific diff line and/or blueprint element that triggered it",
|
|
54
|
+
ignoreGuidance: `name the specific route or component and why the diff cannot reach it (e.g. "diff only touches \`/api/v1/users\` — this test targets \`/dashboard\`")`,
|
|
55
|
+
verifyExample: `"component is imported but not directly rendered — cannot confirm impact without reading the parent"`,
|
|
56
|
+
blueprintSuffix: " or blueprint element",
|
|
57
|
+
reachabilityCheck: "The changed code is reachable through this test's routes (browser tests) or a render/mount call in the test that references a changed component (component/widget tests).",
|
|
58
|
+
regenerateCheck: "For REGENERATE: the interaction model changed fundamentally — the test's action sequence has no migration path. If selectors changed but the flow (navigate → interact → assert) can still be expressed with targeted edits, it is UPDATE regardless of how many selectors changed.",
|
|
59
|
+
deleteCheck: "For DELETE: the route is completely gone (browser tests) or the rendered component was deleted (component/widget tests).",
|
|
60
|
+
domainNotes,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the UI Action Decision Tree section.
|
|
3
|
+
* This is the main entry point for UI drift analysis.
|
|
4
|
+
*/
|
|
5
|
+
export declare function buildUiActionDecisionTree(): string;
|
|
6
|
+
/**
|
|
7
|
+
* Pre-scan step: agent greps changed files' exported symbols across the test
|
|
8
|
+
* directory to catch tests that server-side discovery missed — aliased imports,
|
|
9
|
+
* indirect renders, or any test the blueprint diff doesn't directly reference.
|
|
10
|
+
*/
|
|
11
|
+
export declare function buildUiCheckExportedSymbols(changedFrontendFiles?: string[]): string;
|
|
12
|
+
/**
|
|
13
|
+
* Check if routes targeted by the UI test still exist.
|
|
14
|
+
*/
|
|
15
|
+
export declare function buildUiCheckRouteExistence(): string;
|
|
16
|
+
/**
|
|
17
|
+
* Check if selectors used by the UI test still exist in the blueprints.
|
|
18
|
+
*/
|
|
19
|
+
export declare function buildUiCheckSelectors(): string;
|
|
20
|
+
/**
|
|
21
|
+
* Check if page objects/fixtures referenced by the test still exist.
|
|
22
|
+
*/
|
|
23
|
+
export declare function buildUiCheckPageObjects(): string;
|
|
24
|
+
/**
|
|
25
|
+
* Check for component behavior changes that affect assertions.
|
|
26
|
+
*/
|
|
27
|
+
export declare function buildUiCheckBehavioralChanges(): string;
|
|
28
|
+
/**
|
|
29
|
+
* Assign the final UI drift action.
|
|
30
|
+
*/
|
|
31
|
+
export declare function buildUiCheckAssignAction(): string;
|
|
32
|
+
/**
|
|
33
|
+
* Build the UI drift output checklist.
|
|
34
|
+
*/
|
|
35
|
+
export declare function buildUiDriftOutputChecklist(stateFile?: string, tests?: Array<{
|
|
36
|
+
testFile: string;
|
|
37
|
+
source?: string;
|
|
38
|
+
}>): string;
|