@skyramp/mcp 0.4.1 → 0.4.2-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.
- package/build/adapters/jestAdapter.js +23 -5
- package/build/adapters/mochaAdapter.js +7 -1
- package/build/adapters/playwrightAdapter.js +3 -1
- package/build/adapters/pytestAdapter.js +14 -3
- package/build/commands/localDevTestChangesCommand.js +1 -0
- package/build/commands/recommendTestsAndExecuteCommand.js +5 -6
- package/build/commands/testThisEndpointCommand.js +16 -4
- package/build/index.js +34 -4
- package/build/playwright/blueprintDigest.js +28 -7
- package/build/playwright/registerPlaywrightTools.js +33 -28
- package/build/playwright/traceRecordingPrompt.js +9 -2
- package/build/prompts/code-reuse.js +95 -29
- package/build/prompts/fix-error-prompt.d.ts +9 -1
- package/build/prompts/fix-error-prompt.js +31 -28
- package/build/prompts/local-dev/local-dev-plan.d.ts +3 -0
- package/build/prompts/local-dev/local-dev-plan.js +3 -1
- package/build/prompts/local-dev/local-dev-prompts.d.ts +1 -1
- package/build/prompts/local-dev/local-dev-prompts.js +30 -3
- package/build/prompts/modularization/integration-test-modularization.js +13 -6
- package/build/prompts/modularization/ui-test-modularization.js +1 -1
- package/build/prompts/personas.js +1 -1
- package/build/prompts/pom-aware-code-reuse.js +7 -9
- package/build/prompts/reuse-hand-off.d.ts +64 -0
- package/build/prompts/reuse-hand-off.js +130 -0
- package/build/prompts/shared-helper-policy.d.ts +88 -2
- package/build/prompts/shared-helper-policy.js +145 -11
- package/build/prompts/sut-setup/modes/adaptWorkflowPrompt.js +1 -1
- package/build/prompts/sut-setup/modes/dockerComposePrompt.js +1 -1
- package/build/prompts/sut-setup/shared.d.ts +4 -1
- package/build/prompts/sut-setup/shared.js +2 -2
- package/build/prompts/test-maintenance/actionsInstructions.d.ts +19 -11
- package/build/prompts/test-maintenance/actionsInstructions.js +47 -26
- package/build/prompts/test-maintenance/drift-analysis-prompt.js +3 -2
- package/build/prompts/test-maintenance/driftAnalysisSections.js +5 -3
- package/build/prompts/test-maintenance/driftAnalysisShared.js +4 -2
- package/build/prompts/test-maintenance/uiDriftAnalysisSections.js +7 -4
- package/build/prompts/test-recommendation/recommendationSections.js +19 -8
- package/build/prompts/test-recommendation/registerRecommendTestsPrompt.js +5 -2
- package/build/prompts/test-recommendation/test-recommendation-prompt.js +10 -7
- package/build/prompts/testbot/testbot-prompts.js +123 -55
- package/build/recommendation/answers.js +18 -9
- package/build/recommendation/pullRequestText.js +1 -1
- package/build/recommendation/registerPlan.js +31 -17
- package/build/recommendation/subjectStep.d.ts +8 -10
- package/build/recommendation/subjectStep.js +17 -16
- package/build/recommendation/types.d.ts +71 -17
- package/build/recommendation/types.js +9 -15
- package/build/recommendation/verifierContracts.d.ts +7 -3
- package/build/recommendation/verifierContracts.js +8 -4
- package/build/recommendation/verifiers/changedFile.js +11 -13
- package/build/recommendation/verifiers/citedPath.d.ts +8 -0
- package/build/recommendation/verifiers/citedPath.js +16 -2
- package/build/recommendation/verifiers/coverage.d.ts +2 -2
- package/build/recommendation/verifiers/coverage.js +190 -134
- package/build/recommendation/verifiers/defects.js +56 -16
- package/build/recommendation/verifiers/deliveredMatchesPlan.d.ts +7 -1
- package/build/recommendation/verifiers/deliveredMatchesPlan.js +28 -14
- package/build/recommendation/verifiers/endpointGrounded.js +14 -8
- package/build/recommendation/verifiers/existingCoverage.d.ts +6 -0
- package/build/recommendation/verifiers/existingCoverage.js +30 -21
- package/build/recommendation/verifiers/expectedOutcome.js +17 -8
- package/build/recommendation/verifiers/expectedValueSourced.js +54 -43
- package/build/recommendation/verifiers/issueTraceability.d.ts +5 -0
- package/build/recommendation/verifiers/issueTraceability.js +64 -7
- package/build/recommendation/verifiers/removedElementGuarded.js +19 -11
- package/build/recommendation/verifiers/reportedCategory.js +9 -4
- package/build/recommendation/verifiers/requirementSourced.js +34 -21
- package/build/recommendation/verifiers/screenRoute.js +15 -10
- package/build/recommendation/verifiers/statedDifference.js +15 -8
- package/build/recommendation/verifiers/uiElementGrounded.js +50 -16
- package/build/resources/analysisResources.js +7 -3
- package/build/resources/progressResource.js +4 -2
- package/build/resources/sutSetupResource.js +20 -2
- package/build/resources/testbotResource.js +19 -1
- package/build/services/AnalyticsService.js +3 -1
- package/build/services/ScenarioGenerationService.js +5 -5
- package/build/services/TestDiscoveryService.js +21 -5
- package/build/services/TestExecutionService.js +63 -21
- package/build/services/TestGenerationService.d.ts +1 -1
- package/build/services/TestGenerationService.js +43 -24
- package/build/skills/enhanceAssertionsSkill.d.ts +45 -0
- package/build/skills/enhanceAssertionsSkill.js +103 -0
- package/build/skills/fixTestImportErrorsSkill.d.ts +2 -2
- package/build/skills/fixTestImportErrorsSkill.js +2 -2
- package/build/skills/skillFiles.d.ts +38 -0
- package/build/skills/skillFiles.js +94 -0
- package/build/skills/validateAssertionAlignmentSkill.d.ts +34 -0
- package/build/skills/validateAssertionAlignmentSkill.js +59 -0
- package/build/tool-phases.js +4 -0
- package/build/tools/auth/loginTool.js +3 -1
- package/build/tools/auth/logoutTool.js +3 -1
- package/build/tools/code-refactor/alignAssertionsTool.d.ts +2 -0
- package/build/tools/code-refactor/alignAssertionsTool.js +51 -0
- package/build/tools/code-refactor/assertion-state.d.ts +1 -1
- package/build/tools/code-refactor/assertion-state.js +1 -1
- package/build/tools/code-refactor/assertionOperations.d.ts +44 -0
- package/build/tools/code-refactor/assertionOperations.js +82 -0
- package/build/tools/code-refactor/assertionSkillTools.d.ts +3 -0
- package/build/tools/code-refactor/assertionSkillTools.js +86 -0
- package/build/tools/code-refactor/codeReuseTool.js +27 -3
- package/build/tools/code-refactor/enhanceAssertionsTool.js +19 -83
- package/build/tools/code-refactor/gate-markers.d.ts +51 -0
- package/build/tools/code-refactor/gate-markers.js +95 -0
- package/build/tools/code-refactor/retrofit-state.d.ts +3 -1
- package/build/tools/code-refactor/retrofit-state.js +44 -2
- package/build/tools/code-refactor/reuse-outcome.d.ts +23 -1
- package/build/tools/code-refactor/reuse-outcome.js +36 -13
- package/build/tools/code-refactor/reuse-state.d.ts +90 -16
- package/build/tools/code-refactor/reuse-state.js +191 -61
- package/build/tools/code-refactor/utils-verify-gates.d.ts +4 -0
- package/build/tools/code-refactor/utils-verify-gates.js +252 -27
- package/build/tools/code-refactor/verify-gates.d.ts +15 -1
- package/build/tools/code-refactor/verify-gates.js +36 -4
- package/build/tools/enrichTestWithMocksTool.d.ts +1 -1
- package/build/tools/enrichTestWithMocksTool.js +9 -5
- package/build/tools/executeSkyrampTestTool.d.ts +22 -1
- package/build/tools/executeSkyrampTestTool.js +571 -271
- package/build/tools/execution-video-state.js +1 -1
- package/build/tools/fixErrorTool.js +5 -6
- package/build/tools/generate-tests/batchMockGenerationTool.js +1 -1
- package/build/tools/generate-tests/generateBatchScenarioRestTool.js +114 -73
- package/build/tools/generate-tests/generateContractRestTool.js +34 -16
- package/build/tools/generate-tests/generateE2ERestTool.d.ts +1 -0
- package/build/tools/generate-tests/generateE2ERestTool.js +9 -1
- package/build/tools/generate-tests/generateIntegrationRestTool.js +22 -7
- package/build/tools/generate-tests/generateMockRestTool.js +3 -1
- package/build/tools/generate-tests/generateUIRestTool.d.ts +2 -0
- package/build/tools/generate-tests/generateUIRestTool.js +11 -2
- package/build/tools/generate-tests/loadTestSchema.js +1 -3
- package/build/tools/generate-tests/planGuard.js +6 -3
- package/build/tools/generate-tests/scenarioFileIdentity.js +4 -1
- package/build/tools/generate-tests/scenarioLint.js +17 -5
- package/build/tools/generate-tests/trace-reuse-guard.js +5 -2
- package/build/tools/generateEnrichedIntegrationTestTool.js +9 -3
- package/build/tools/one-click/oneClickTool.js +3 -1
- package/build/tools/preflightMockCheckTool.js +23 -7
- package/build/tools/runExistingTestsTool.d.ts +1 -1
- package/build/tools/runExistingTestsTool.js +43 -21
- package/build/tools/submitReportTool.d.ts +51 -12
- package/build/tools/submitReportTool.js +860 -161
- package/build/tools/test-management/actionsTool.js +210 -51
- package/build/tools/test-management/analyzeChangesTool.d.ts +4 -5
- package/build/tools/test-management/analyzeChangesTool.js +127 -68
- package/build/tools/test-management/analyzeTestHealthTool.js +35 -13
- package/build/tools/test-management/registerTestPlanTool.d.ts +44 -34
- package/build/tools/test-management/registerTestPlanTool.js +255 -111
- package/build/tools/test-management/resolveScreenTool.js +33 -9
- package/build/tools/trace/startTraceCollectionTool.js +3 -1
- package/build/tools/trace/stopTraceCollectionTool.js +41 -5
- package/build/tools/verifyTestDependenciesTool.d.ts +3 -0
- package/build/tools/verifyTestDependenciesTool.js +54 -0
- package/build/tools/workspace/initScanWorkspaceTool.js +9 -3
- package/build/tools/workspace/initializeWorkspaceTool.js +3 -1
- package/build/types/AssertionOutcome.d.ts +1 -1
- package/build/types/EnhanceType.d.ts +6 -0
- package/build/types/EnhanceType.js +1 -0
- package/build/types/ExternalTestExecution.d.ts +2 -2
- package/build/types/RepositoryAnalysis.d.ts +0 -40
- package/build/types/ReuseOutcome.d.ts +96 -4
- package/build/types/ReuseOutcome.js +16 -0
- package/build/types/StepMethod.js +20 -6
- package/build/types/TestAnalysis.d.ts +10 -2
- package/build/types/TestExecution.d.ts +45 -0
- package/build/types/TestRecommendation.d.ts +1 -1
- package/build/types/TestRecommendation.js +4 -1
- package/build/types/TestTypes.d.ts +13 -0
- package/build/types/TestTypes.js +34 -3
- package/build/types/TestbotPromptOptions.d.ts +9 -1
- package/build/types/TestbotReport.d.ts +45 -9
- package/build/utils/AnalysisStateManager.d.ts +137 -27
- package/build/utils/AnalysisStateManager.js +227 -43
- package/build/utils/assertion-verify/api-shared-lints.js +34 -16
- package/build/utils/assertion-verify/metrics.js +39 -6
- package/build/utils/assertion-verify/ui-lints.js +4 -2
- package/build/utils/branchDiff.js +47 -12
- package/build/utils/canonicalJson.js +3 -1
- package/build/utils/dartRouteExtractor.js +36 -7
- package/build/utils/fixAttempts.d.ts +26 -0
- package/build/utils/fixAttempts.js +109 -0
- package/build/utils/frontendSelectors.js +23 -4
- package/build/utils/generatedTestRecord.d.ts +19 -0
- package/build/utils/generatedTestRecord.js +61 -0
- package/build/utils/gitStaging.js +7 -2
- package/build/utils/initAgent.js +26 -6
- package/build/utils/language-helper.js +39 -45
- package/build/utils/pathMatching.js +2 -1
- package/build/utils/pathSignatures.js +6 -2
- package/build/utils/planMatchKeys.d.ts +2 -2
- package/build/utils/planMatchKeys.js +20 -14
- package/build/utils/pom-catalog-parse.js +7 -2
- package/build/utils/pom-scope/import-expansion.js +6 -1
- package/build/utils/pom-scope/index.js +50 -12
- package/build/utils/pom-scope/scoring.js +13 -3
- package/build/utils/pom-scope/selector-extractor.js +16 -3
- package/build/utils/pom-scope/strip.d.ts +8 -0
- package/build/utils/pom-scope/strip.js +238 -0
- package/build/utils/pom-scope/testIdDiscovery.js +14 -2
- package/build/utils/pom-verify/__fixtures__/af-style/asset-list.page.d.ts +1 -1
- package/build/utils/pom-verify/__fixtures__/af-style/asset-list.page.js +4 -2
- package/build/utils/pom-verify/__fixtures__/af-style/report.iframe.page.js +3 -1
- package/build/utils/pom-verify/__fixtures__/af-style/workflow-footer.page.js +3 -1
- package/build/utils/pom-verify/bindings.js +5 -1
- package/build/utils/pom-verify/calls.js +9 -2
- package/build/utils/pom-verify/verify.js +27 -5
- package/build/utils/pr-comment-parser.js +20 -7
- package/build/utils/proxy-terminal.d.ts +19 -1
- package/build/utils/proxy-terminal.js +346 -21
- package/build/utils/removedUiElements.js +1 -1
- package/build/utils/reportLanguage.js +35 -7
- package/build/utils/reportVerification.d.ts +14 -8
- package/build/utils/reportVerification.js +19 -19
- package/build/utils/repositorySlug.d.ts +32 -0
- package/build/utils/repositorySlug.js +77 -0
- package/build/utils/reuseRouting.js +6 -2
- package/build/utils/runSerialized.d.ts +3 -0
- package/build/utils/runSerialized.js +39 -0
- package/build/utils/screenRoutes.js +74 -18
- package/build/utils/sourceRouteExtractor.js +13 -6
- package/build/utils/telemetry.d.ts +1 -0
- package/build/utils/telemetry.js +8 -5
- package/build/utils/testDependencyPolicy.d.ts +9 -0
- package/build/utils/testDependencyPolicy.js +788 -0
- package/build/utils/testExecutionRecord.d.ts +90 -0
- package/build/utils/testExecutionRecord.js +267 -0
- package/build/utils/testFileClassification.js +3 -1
- package/build/utils/trace-parser.js +62 -14
- package/build/utils/urlPath.js +3 -1
- package/build/utils/utils-verify/action-key.d.ts +4 -0
- package/build/utils/utils-verify/action-key.js +2 -2
- package/build/utils/utils-verify/allow.d.ts +122 -3
- package/build/utils/utils-verify/allow.js +146 -21
- package/build/utils/utils-verify/body-reach.d.ts +120 -0
- package/build/utils/utils-verify/body-reach.js +335 -0
- package/build/utils/utils-verify/call-sites.d.ts +6 -1
- package/build/utils/utils-verify/call-sites.js +72 -8
- package/build/utils/utils-verify/delivered-imports.d.ts +43 -0
- package/build/utils/utils-verify/delivered-imports.js +84 -0
- package/build/utils/utils-verify/fixed-sleep.d.ts +96 -0
- package/build/utils/utils-verify/fixed-sleep.js +461 -0
- package/build/utils/utils-verify/in-house.d.ts +93 -0
- package/build/utils/utils-verify/in-house.js +719 -0
- package/build/utils/utils-verify/incumbent.d.ts +3 -0
- package/build/utils/utils-verify/incumbent.js +75 -0
- package/build/utils/utils-verify/index.d.ts +1 -0
- package/build/utils/utils-verify/index.js +1 -0
- package/build/utils/utils-verify/language-spec.d.ts +26 -10
- package/build/utils/utils-verify/language-spec.js +217 -20
- package/build/utils/utils-verify/locate.d.ts +26 -0
- package/build/utils/utils-verify/locate.js +125 -23
- package/build/utils/utils-verify/module-name.d.ts +40 -0
- package/build/utils/utils-verify/module-name.js +98 -0
- package/build/utils/utils-verify/parse.d.ts +46 -6
- package/build/utils/utils-verify/parse.js +176 -38
- package/build/utils/utils-verify/retrofit-equivalence.d.ts +5 -0
- package/build/utils/utils-verify/retrofit-equivalence.js +4 -2
- package/build/utils/utils-verify/stage.d.ts +5 -0
- package/build/utils/utils-verify/stage.js +47 -2
- package/build/utils/utils-verify/status-once.d.ts +62 -0
- package/build/utils/utils-verify/status-once.js +207 -0
- package/build/utils/utils-verify/typecheck.d.ts +61 -0
- package/build/utils/utils-verify/typecheck.js +338 -0
- package/build/utils/utils-verify/verify.d.ts +76 -1
- package/build/utils/utils-verify/verify.js +516 -44
- package/build/utils/versions.d.ts +3 -3
- package/build/utils/versions.js +1 -1
- package/build/utils/workspaceAuth.js +107 -37
- package/build/workspace/queryParamResolution.js +11 -4
- package/build/workspace/workspace.js +7 -7
- package/node_modules/playwright/ThirdPartyNotices.txt +19 -19
- package/node_modules/playwright/lib/mcp/skyramp/assertTool.js +8 -2
- package/node_modules/playwright/lib/mcp/skyramp/common/cssValue.js +87 -0
- package/node_modules/playwright/lib/mcp/skyramp/loadTraceTool.js +31 -0
- package/node_modules/playwright/lib/mcp/skyramp/skyRampImport.js +3 -0
- package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +181 -15
- package/node_modules/playwright/lib/mcp/test/skyRampExport.js +24 -0
- package/node_modules/playwright/lib/transform/babelBundleImpl.js +2353 -190
- package/node_modules/playwright/node_modules/playwright-core/ThirdPartyNotices.txt +62 -34
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/.package-lock.json +72 -41
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/@hono/node-server/dist/serve-static.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/@hono/node-server/dist/serve-static.mjs +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/@hono/node-server/package.json +2 -2
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/body-parser/README.md +16 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/body-parser/index.js +4 -20
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/body-parser/lib/read.js +17 -17
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/body-parser/lib/types/json.js +60 -32
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/body-parser/lib/types/raw.js +3 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/body-parser/lib/types/text.js +3 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/body-parser/lib/types/urlencoded.js +16 -20
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/body-parser/lib/utils.js +18 -16
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/body-parser/node_modules/content-type/LICENSE +22 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/body-parser/node_modules/content-type/README.md +71 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/body-parser/node_modules/content-type/dist/index.d.ts +46 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/body-parser/node_modules/content-type/dist/index.js +176 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/body-parser/node_modules/content-type/dist/index.js.map +1 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/body-parser/node_modules/content-type/package.json +52 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/body-parser/package.json +23 -10
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/es-object-atoms/CHANGELOG.md +21 -14
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/es-object-atoms/isObject.d.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/es-object-atoms/package.json +6 -7
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/es-object-atoms/tsconfig.json +1 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/fast-uri/index.js +266 -45
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/fast-uri/lib/schemes.js +9 -4
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/fast-uri/lib/utils.js +396 -92
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/fast-uri/package.json +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/fast-uri/test/component-safe-serialization.test.js +163 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/fast-uri/test/equal.test.js +31 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/fast-uri/test/fixtures/uri-js-parse.json +2 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/fast-uri/test/ipv6-canonical.test.js +34 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/fast-uri/test/ipv6-validation.test.js +124 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/fast-uri/test/malformed-percent.test.js +77 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/fast-uri/test/malformed-urn.test.js +61 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/fast-uri/test/parse.test.js +7 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/fast-uri/test/query-fragment-normalization.test.js +33 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/fast-uri/test/reserved-path-normalization.test.js +109 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/fast-uri/test/scheme-validation.test.js +124 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/fast-uri/test/security-normalization.test.js +101 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/fast-uri/test/security.test.js +301 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/fast-uri/test/urn-full-input.test.js +29 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/fast-uri/test/websocket-query-preservation.test.js +24 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hasown/CHANGELOG.md +7 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hasown/index.d.ts +0 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hasown/package.json +4 -5
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/client/client.js +30 -16
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/client/utils.js +4 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/context.js +32 -13
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/helper/accepts/accepts.js +36 -2
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/helper/proxy/index.js +4 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/helper/ssg/ssg.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/helper/ssg/utils.js +30 -10
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/helper/streaming/sse.js +5 -4
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/hono-base.js +10 -8
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/jsx/base.js +44 -23
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/jsx/components.js +25 -26
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/jsx/context.js +5 -5
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/jsx/dom/render.js +2 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/jsx/dom/server.js +5 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/jsx/hooks/index.js +16 -13
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/jsx/intrinsic-element/components.js +3 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/jsx/streaming.js +4 -5
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/cache/index.js +103 -8
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/compress/index.js +5 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/cors/index.js +17 -14
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/csrf/index.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/etag/digest.js +47 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/etag/index.js +7 -4
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/jwk/jwk.js +9 -4
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/jwt/jwt.js +9 -4
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/language/language.js +10 -6
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/method-not-allowed/index.js +90 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/pretty-json/index.js +3 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/middleware/secure-headers/secure-headers.js +16 -7
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/request.js +20 -13
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/router/linear-router/router.js +7 -2
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/router/pattern-router/router.js +3 -9
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/router/reg-exp-router/node.js +65 -59
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/router/reg-exp-router/router.js +71 -128
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/router/reg-exp-router/trie.js +14 -5
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/router/trie-router/node.js +47 -70
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/router/trie-router/router.js +3 -11
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/router/utils.js +27 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/router.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/utils/accept.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/utils/body.js +21 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/utils/cookie.js +4 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/utils/ipaddr.js +5 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/utils/stream.js +12 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/cjs/utils/url.js +19 -11
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/client/client.js +30 -16
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/client/utils.js +4 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/context.js +32 -13
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/helper/accepts/accepts.js +36 -2
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/helper/proxy/index.js +4 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/helper/ssg/ssg.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/helper/ssg/utils.js +30 -10
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/helper/streaming/sse.js +5 -4
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/hono-base.js +10 -8
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/jsx/base.js +41 -23
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/jsx/components.js +26 -27
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/jsx/context.js +6 -6
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/jsx/dom/render.js +2 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/jsx/dom/server.js +5 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/jsx/hooks/index.js +16 -13
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/jsx/intrinsic-element/components.js +4 -4
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/jsx/streaming.js +5 -6
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/cache/index.js +103 -8
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/compress/index.js +5 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/cors/index.js +17 -14
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/csrf/index.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/etag/digest.js +47 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/etag/index.js +7 -4
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/jwk/jwk.js +9 -4
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/jwt/jwt.js +9 -4
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/language/language.js +10 -6
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/method-not-allowed/index.js +68 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/pretty-json/index.js +3 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/middleware/secure-headers/secure-headers.js +16 -7
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/request.js +21 -14
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/router/linear-router/router.js +7 -2
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/router/pattern-router/router.js +3 -9
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/router/reg-exp-router/node.js +61 -58
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/router/reg-exp-router/router.js +77 -129
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/router/reg-exp-router/trie.js +14 -5
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/router/trie-router/node.js +47 -70
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/router/trie-router/router.js +3 -11
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/router/utils.js +5 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/router.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/adapter/aws-lambda/types.d.ts +9 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/client/types.d.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/context.d.ts +6 -2
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/hono-base.d.ts +4 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/jsx/base.d.ts +7 -2
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/jsx/dom/index.d.ts +5 -5
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/jsx/dom/intrinsic-element/components.d.ts +2 -2
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/jsx/dom/server.d.ts +5 -5
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/jsx/hooks/index.d.ts +8 -6
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/jsx/index.d.ts +5 -5
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/middleware/cache/index.d.ts +6 -4
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/middleware/combine/index.d.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/middleware/cors/index.d.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/middleware/jsx-renderer/index.d.ts +2 -2
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/middleware/jwk/jwk.d.ts +2 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/middleware/jwt/jwt.d.ts +2 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/middleware/method-not-allowed/index.d.ts +49 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/middleware/secure-headers/permissions-policy.d.ts +3 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/router/reg-exp-router/node.d.ts +4 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/router/reg-exp-router/trie.d.ts +2 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/router/trie-router/node.d.ts +1 -2
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/router/trie-router/router.d.ts +0 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/router/utils.d.ts +1 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/router.d.ts +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/utils/headers.d.ts +2 -2
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/types/utils/url.d.ts +5 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/utils/accept.js +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/utils/body.js +21 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/utils/cookie.js +5 -4
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/utils/ipaddr.js +5 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/utils/stream.js +12 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/dist/utils/url.js +17 -10
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/hono/package.json +11 -4
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/ip-address/README.md +173 -143
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/ip-address/dist/address-error.d.ts +11 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/ip-address/dist/address-error.js.map +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/ip-address/dist/common.d.ts +49 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/ip-address/dist/common.js +79 -4
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/ip-address/dist/common.js.map +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/ip-address/dist/ipv4.d.ts +78 -5
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/ip-address/dist/ipv4.js +119 -24
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/ip-address/dist/ipv4.js.map +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/ip-address/dist/ipv6.d.ts +151 -10
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/ip-address/dist/ipv6.js +316 -90
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/ip-address/dist/ipv6.js.map +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/ip-address/dist/v4/constants.d.ts +12 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/ip-address/dist/v4/constants.js +45 -2
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/ip-address/dist/v4/constants.js.map +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/ip-address/dist/v6/constants.d.ts +14 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/ip-address/dist/v6/constants.js +50 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/ip-address/dist/v6/constants.js.map +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/ip-address/package.json +6 -4
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/qs/.github/THREAT_MODEL.md +3 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/qs/CHANGELOG.md +26 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/qs/README.md +19 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/qs/dist/qs.js +25 -25
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/qs/eslint.config.mjs +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/qs/lib/parse.js +16 -6
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/qs/lib/stringify.js +23 -8
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/qs/lib/utils.js +57 -11
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/qs/package.json +8 -7
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/qs/test/parse.js +419 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/qs/test/stringify.js +317 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/qs/test/utils.js +206 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/side-channel/CHANGELOG.md +10 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/side-channel/README.md +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/side-channel/index.js +5 -2
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/side-channel/package.json +10 -10
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/side-channel/test/index.js +16 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/type-is/index.js +8 -18
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/type-is/node_modules/content-type/LICENSE +22 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/type-is/node_modules/content-type/README.md +71 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/type-is/node_modules/content-type/dist/index.d.ts +46 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/type-is/node_modules/content-type/dist/index.js +176 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/type-is/node_modules/content-type/dist/index.js.map +1 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/type-is/node_modules/content-type/package.json +52 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/node_modules/type-is/package.json +9 -5
- package/node_modules/playwright/node_modules/playwright-core/bundles/mcp/package-lock.json +72 -41
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/.package-lock.json +6 -6
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/brace-expansion/README.md +23 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/brace-expansion/index.js +265 -86
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/brace-expansion/package.json +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/ip-address/README.md +173 -143
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/ip-address/dist/address-error.d.ts +11 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/ip-address/dist/address-error.js.map +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/ip-address/dist/common.d.ts +49 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/ip-address/dist/common.js +79 -4
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/ip-address/dist/common.js.map +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/ip-address/dist/ipv4.d.ts +78 -5
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/ip-address/dist/ipv4.js +119 -24
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/ip-address/dist/ipv4.js.map +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/ip-address/dist/ipv6.d.ts +151 -10
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/ip-address/dist/ipv6.js +316 -90
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/ip-address/dist/ipv6.js.map +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/ip-address/dist/v4/constants.d.ts +12 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/ip-address/dist/v4/constants.js +45 -2
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/ip-address/dist/v4/constants.js.map +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/ip-address/dist/v6/constants.d.ts +14 -0
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/ip-address/dist/v6/constants.js +50 -3
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/ip-address/dist/v6/constants.js.map +1 -1
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/node_modules/ip-address/package.json +6 -4
- package/node_modules/playwright/node_modules/playwright-core/bundles/utils/package-lock.json +6 -6
- package/node_modules/playwright/node_modules/playwright-core/lib/cli/program.js +18 -9
- package/node_modules/playwright/node_modules/playwright-core/lib/mcpBundleImpl/index.js +47 -47
- package/node_modules/playwright/node_modules/playwright-core/lib/server/codegen/skyramp/jsonlReader.js +3 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/server/recorder/recorderRunner.js +42 -0
- package/node_modules/playwright/node_modules/playwright-core/lib/utilsBundleImpl/index.js +121 -121
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{index.B7KbSQcC.js → index.BAkLd5DX.js} +1 -1
- package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.html +1 -1
- package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
- package/node_modules/playwright/node_modules/playwright-core/src/cli/program.ts +31 -9
- package/node_modules/playwright/node_modules/playwright-core/src/server/codegen/skyramp/jsonlReader.ts +1 -0
- package/node_modules/playwright/node_modules/playwright-core/src/server/recorder/recorderRunner.ts +57 -1
- package/node_modules/playwright/package.json +2 -2
- package/package.json +7 -6
- package/plugin/prompts/code-reuse/hand-off.md +30 -0
- package/plugin/prompts/generate-tests/generation.md +1 -1
- package/plugin/prompts/plan-tests.md +14 -14
- package/plugin/prompts/testbot-task1.md +4 -2
- package/plugin/skills/enhance-assertions/SKILL.md +25 -0
- package/plugin/skills/enhance-assertions/reference/contract.md +51 -0
- package/plugin/skills/enhance-assertions/reference/integration.md +58 -0
- package/plugin/skills/enhance-assertions/reference/shared-rules.md +220 -0
- package/plugin/skills/enhance-assertions/reference/ui.md +373 -0
- package/plugin/skills/fix-test-import-errors/SKILL.md +44 -50
- package/plugin/skills/validate-assertion-alignment-post-execution/SKILL.md +32 -0
- package/plugin/skills/validate-assertion-alignment-post-execution/reference/checks.md +44 -0
- package/plugin/skills/validate-assertion-alignment-post-execution/reference/evidence.md +47 -0
- package/build/prompts/enhance-assertions/contractProviderAssertionsPrompt.d.ts +0 -2
- package/build/prompts/enhance-assertions/contractProviderAssertionsPrompt.js +0 -29
- package/build/prompts/enhance-assertions/integrationAssertionsPrompt.d.ts +0 -2
- package/build/prompts/enhance-assertions/integrationAssertionsPrompt.js +0 -36
- package/build/prompts/enhance-assertions/sharedAssertionRules.d.ts +0 -16
- package/build/prompts/enhance-assertions/sharedAssertionRules.js +0 -284
- package/build/prompts/enhance-assertions/uiAssertionsPrompt.d.ts +0 -2
- package/build/prompts/enhance-assertions/uiAssertionsPrompt.js +0 -388
- package/node_modules/playwright/node_modules/playwright-core/.DS_Store +0 -0
|
@@ -1,28 +1,33 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { logger } from "../utils/logger.js";
|
|
3
3
|
import * as fs from "fs/promises";
|
|
4
|
+
import { constants as fsConstants } from "fs";
|
|
4
5
|
import * as path from "path";
|
|
5
6
|
import { AnalyticsService } from "../services/AnalyticsService.js";
|
|
6
7
|
import { SCENARIO_CATEGORIES, categoryExpectsRed, } from "../types/TestRecommendation.js";
|
|
7
|
-
import { TestType } from "../types/TestTypes.js";
|
|
8
|
-
import { STEP_METHOD_DESCRIPTION, stepMethodSchema } from "../types/StepMethod.js";
|
|
8
|
+
import { TESTBOT_SUPPORTED_TEST_TYPES, TestType } from "../types/TestTypes.js";
|
|
9
|
+
import { STEP_METHOD_DESCRIPTION, stepMethodSchema, } from "../types/StepMethod.js";
|
|
9
10
|
import { DriftAction } from "../types/TestAnalysis.js";
|
|
10
11
|
import { TestExecutionStatus } from "../types/TestExecution.js";
|
|
11
12
|
import { IssueFoundCategory } from "../types/TestbotReport.js";
|
|
12
|
-
import { StateManager, runArtifactDir, getTestsRepoDir,
|
|
13
|
+
import { StateManager, runArtifactDir, getTestsRepoDir, resolveOwnRunStatePath, } from "../utils/AnalysisStateManager.js";
|
|
13
14
|
import { toolError, testFileMatches } from "../utils/utils.js";
|
|
14
|
-
import { isTestbotEnabled, } from "../utils/featureFlags.js";
|
|
15
|
-
import { answerFor, unknownAnswerObjections } from "../recommendation/answers.js";
|
|
16
|
-
import { checkDeliveredMatchesPlan, checkMaintenanceDelivered } from "../recommendation/verifiers/deliveredMatchesPlan.js";
|
|
15
|
+
import { isTestbotEnabled, isUtilsReuseEnabled, } from "../utils/featureFlags.js";
|
|
16
|
+
import { answerFor, unknownAnswerObjections, } from "../recommendation/answers.js";
|
|
17
|
+
import { checkDeliveredMatchesPlan, checkMaintenanceDelivered, } from "../recommendation/verifiers/deliveredMatchesPlan.js";
|
|
17
18
|
import { checkReportedCategoryMatchesPlan, checkRequirementConflictReported, } from "../recommendation/verifiers/reportedCategory.js";
|
|
18
|
-
import { checkDefectsReported, checkIssueTraceability } from "../recommendation/verifiers/issueTraceability.js";
|
|
19
|
+
import { checkDefectsReported, checkIssueTraceability, } from "../recommendation/verifiers/issueTraceability.js";
|
|
19
20
|
import { checkExpectedOutcomeAfterExecution, } from "../recommendation/verifiers/expectedOutcome.js";
|
|
20
21
|
import { findInvalidSourceCitations, findUnchangedFileClaims, listChangedFiles, listChangedFilesAcross, listChangedFilesAbs, } from "../utils/reportVerification.js";
|
|
21
22
|
import { isPlanOnlyMode } from "../utils/planOnlyMode.js";
|
|
22
23
|
import { getReportLanguage, isEnforcedReportLanguage, findLanguageViolations, findLanguageNearMisses, reportLanguageDisplayName, } from "../utils/reportLanguage.js";
|
|
23
24
|
import { canonicalTestPath, findAssertionRecordByFileName, rederiveAssertionOutcome, } from "./code-refactor/assertion-state.js";
|
|
24
|
-
import { rederiveReuse, REUSE_SUBMIT_MAX_REFUSALS, reuseChainSkipped, samePath, } from "./code-refactor/reuse-state.js";
|
|
25
|
+
import { rederiveReuse, deriveReuseFromDelivered, REUSE_SUBMIT_MAX_REFUSALS, reuseChainSkipped, samePath, } from "./code-refactor/reuse-state.js";
|
|
25
26
|
import { retrofitGate, } from "./code-refactor/retrofit-state.js";
|
|
27
|
+
import { onlyCommentLinesRemoved, stripGateMarkers, } from "./code-refactor/gate-markers.js";
|
|
28
|
+
import { parseKeptInline } from "./code-refactor/reuse-outcome.js";
|
|
29
|
+
import { hasUtilsHeader, locateUtilsFiles, parseUtilsAllows, realpath, utilsSpecForFile, } from "../utils/utils-verify/index.js";
|
|
30
|
+
import { verifyTestDependencyChanges } from "../utils/testDependencyPolicy.js";
|
|
26
31
|
// Mirrors the tools wired to planGuard: UI and E2E are not gated at generation
|
|
27
32
|
// time, so gating them here would be a report-time-only surprise.
|
|
28
33
|
const PLAN_GATED_TEST_TYPES = new Set([
|
|
@@ -65,7 +70,9 @@ const MAINTENANCE_COVERAGE_ACTIONS = new Set([
|
|
|
65
70
|
* an external test's REGENERATE keeps its real action and touches no file, so counting it
|
|
66
71
|
* would credit a claim nothing backs. */
|
|
67
72
|
function rowsLeavingCoverage(rows) {
|
|
68
|
-
return (rows ?? []).filter((row) => row?.action !== undefined &&
|
|
73
|
+
return (rows ?? []).filter((row) => row?.action !== undefined &&
|
|
74
|
+
MAINTENANCE_COVERAGE_ACTIONS.has(row.action) &&
|
|
75
|
+
row?.reportOnly !== true);
|
|
69
76
|
}
|
|
70
77
|
/** Objection ids for an untested declared change, `coverage:change:<id>`. The
|
|
71
78
|
* change table reads the agent's answer back off them. */
|
|
@@ -74,22 +81,13 @@ const TOOL_NAME = "skyramp_submit_report";
|
|
|
74
81
|
const DEFAULT_COMMIT_MESSAGE = "Added recommendations by Skyramp Testbot.";
|
|
75
82
|
// Per-repo attribution. In a multi-repo run, every report item carries the
|
|
76
83
|
// owner/repo it pertains to (the primary's owner/repo for primary objections, or a
|
|
77
|
-
// related repo's owner/repo for objections from that repo's diff).
|
|
78
|
-
//
|
|
79
|
-
// distinct repo. Single-repo runs may omit it.
|
|
84
|
+
// related repo's owner/repo for objections from that repo's diff). Trimmed, and
|
|
85
|
+
// a blank string is rejected: every item names its repo, single-repo runs too.
|
|
80
86
|
const repositoryField = z
|
|
81
87
|
.string()
|
|
82
|
-
.
|
|
83
|
-
.
|
|
84
|
-
|
|
85
|
-
* value to undefined so it is never serialized as a distinct empty repo
|
|
86
|
-
* (downstream consumers treat absence as "the primary repo"). */
|
|
87
|
-
function normalizeRepository(item) {
|
|
88
|
-
const trimmed = item.repository?.trim();
|
|
89
|
-
return trimmed
|
|
90
|
-
? { ...item, repository: trimmed }
|
|
91
|
-
: { ...item, repository: undefined };
|
|
92
|
-
}
|
|
88
|
+
.trim()
|
|
89
|
+
.min(1)
|
|
90
|
+
.describe("The 'owner/repo' this item pertains to (e.g. 'letsramp/api-insight'). Set it on every item — the primary's owner/repo for primary-repo objections, or a related repo's owner/repo for objections derived from that repository's diff.");
|
|
93
91
|
// videoPath is deliberately absent from this input contract: it is attached server-side
|
|
94
92
|
// from the run's execution records (see attachVideoPath), and zod strips any the model
|
|
95
93
|
// supplies anyway. SKYR-4156 is what happens when the agent owns that field instead.
|
|
@@ -347,7 +345,7 @@ const issueFoundSchema = z
|
|
|
347
345
|
.optional()
|
|
348
346
|
.describe("The `id` of the plan defect this issue reports, from the `defects` list you registered with skyramp_register_test_plan. Spell it as that list spells it: an id the plan does not declare traces nothing. " +
|
|
349
347
|
"Set it on the `category: bug` entry that reports the defect. A `defectId` on a `lint`, `type` or `config` entry does not report the defect — the report renders those in its own Configuration Errors section. " +
|
|
350
|
-
"CHECKED: every plan defect has a `bug` issue that names it, and a `bug` entry with no `plannedTestId` traces through this defect when a delivered test that expects to fail cites it, or when you answered `defects:untested:<id>` at plan time. Leave it out for an issue the plan did not declare."),
|
|
348
|
+
"CHECKED: every plan defect has a `bug` issue that names it, and a `bug` entry with no `plannedTestId` traces through this defect when a delivered test that expects to fail cites it, when a `maintains` entry you registered cites it, or when you answered `defects:untested:<id>` at plan time. Leave it out for an issue the plan did not declare."),
|
|
351
349
|
sourceFile: citationString.describe("Path of the application file whose code is missing or wrong, relative to the repository root (e.g. 'src/crud/products.py'). " +
|
|
352
350
|
"REQUIRED when category is 'bug'. " +
|
|
353
351
|
"For code that is MISSING — an unmounted router, an unregistered route, an import never added — name the file where the line should be, not the file that defines what is unmounted. " +
|
|
@@ -538,6 +536,12 @@ const testMaintenanceSchema = z.object({
|
|
|
538
536
|
"For failing runs: failure name and one-line root cause, e.g. " +
|
|
539
537
|
"'FAILED test_foo — check_schema fails, order_id=1 has discount from prior PATCH test'. " +
|
|
540
538
|
"Empty string for VERIFY/IGNORE/DELETE entries where no after-execution was run."),
|
|
539
|
+
/** The ids of the plan defects this maintained test's own run proves. Written
|
|
540
|
+
* at report time, not at plan time: by here the test has been executed and the
|
|
541
|
+
* agent is recording what it saw, so naming a defect here never displaces the
|
|
542
|
+
* planned test that also proves it. A defect may be named by a planned test, by
|
|
543
|
+
* a maintenance row, or by both. */
|
|
544
|
+
defects: z.array(z.string()).default([]),
|
|
541
545
|
// Server-populated from the verdict's own state section, falling back to the
|
|
542
546
|
// run's primary repo. Always set: a consumer that has to treat absence as
|
|
543
547
|
// "probably the primary" cannot tell a single-repo row from a mis-stamped one.
|
|
@@ -546,18 +550,28 @@ const testMaintenanceSchema = z.object({
|
|
|
546
550
|
beforeStatus: z.nativeEnum(TestExecutionStatus),
|
|
547
551
|
afterStatus: z.nativeEnum(TestExecutionStatus),
|
|
548
552
|
});
|
|
549
|
-
// LLM input contract: status fields are server-populated from stateFile
|
|
553
|
+
// LLM input contract: status fields are server-populated from stateFile, and
|
|
554
|
+
// `defects` is attached from the per-file detail the agent drafts, not from the
|
|
555
|
+
// verdict rows this schema describes.
|
|
550
556
|
const testMaintenanceLLMSchema = testMaintenanceSchema.omit({
|
|
551
557
|
beforeStatus: true,
|
|
552
558
|
afterStatus: true,
|
|
559
|
+
defects: true,
|
|
553
560
|
});
|
|
554
561
|
// LLM only drafts beforeDetails/afterDetails per file — action/testType/endpoint/description
|
|
555
562
|
// come from stateData.maintenanceVerdicts. Picked from testMaintenanceLLMSchema to avoid
|
|
556
563
|
// redeclaring those fields.
|
|
557
|
-
const testMaintenanceDetailSchema = testMaintenanceLLMSchema
|
|
564
|
+
const testMaintenanceDetailSchema = testMaintenanceLLMSchema
|
|
565
|
+
.pick({
|
|
558
566
|
testFilePath: true,
|
|
559
567
|
beforeDetails: true,
|
|
560
568
|
afterDetails: true,
|
|
569
|
+
})
|
|
570
|
+
.extend({
|
|
571
|
+
defects: z
|
|
572
|
+
.array(z.string())
|
|
573
|
+
.optional()
|
|
574
|
+
.describe("The ids of the plan defects this test's own run proves, from the `defects` list you registered with skyramp_register_test_plan. Name a defect here when this test failed on it — the failure you summarised in `afterDetails` is the evidence. Spell each id as that list spells it; the report is rejected if an id is not one the plan declares. Naming a defect here does not replace a planned test that proves it: name it here as well."),
|
|
561
575
|
});
|
|
562
576
|
/** The key an answer is matched by: the objection's id AND its message. The id
|
|
563
577
|
* alone is not unique — one verifier raises several different objections under
|
|
@@ -647,45 +661,156 @@ async function attachAssertionOutcome(test, outcomes, checkouts) {
|
|
|
647
661
|
return test;
|
|
648
662
|
return { ...test, assertions: await rederiveAssertionOutcome(record) };
|
|
649
663
|
}
|
|
664
|
+
/**
|
|
665
|
+
* The absolute path of a `newTestsCreated` row's delivered file, from the paths the
|
|
666
|
+
* generators recorded — or the reason it cannot be answered, which the row states.
|
|
667
|
+
*
|
|
668
|
+
* A created row carries a `fileName` the agent wrote, which may be a basename or a
|
|
669
|
+
* repository-relative path, so it cannot be resolved on its own. `generatedTests` is
|
|
670
|
+
* keyed by canonical absolute path and written by the generators, so the row selects
|
|
671
|
+
* from it through `testFileMatches` — the segment-aware predicate the delivered-
|
|
672
|
+
* matches-plan verifier already uses for this question. A row that names a directory
|
|
673
|
+
* matches only inside it; a bare basename matches on the name alone. Matching by
|
|
674
|
+
* basename and treating the directory as a tie-breaker let a single same-named file
|
|
675
|
+
* win against a row whose own path contradicted it, and the row then reported another
|
|
676
|
+
* file's helpers.
|
|
677
|
+
*
|
|
678
|
+
* Both sides are put in posix form first. That predicate reads a path-qualified
|
|
679
|
+
* candidate by its `/` separators while these keys carry the host's, so on Windows a
|
|
680
|
+
* uniquely recorded file matched nothing and its row said it could not be found.
|
|
681
|
+
* Untested: `path.sep` is `/` on the platforms this runs on, so no test on them can
|
|
682
|
+
* tell the normalization from its absence.
|
|
683
|
+
*
|
|
684
|
+
* An unresolved row returns a REASON rather than nothing. Nothing is read by the
|
|
685
|
+
* caller as "no file to derive from", which ships the ambiguous `reuse: null` this
|
|
686
|
+
* whole derivation exists to remove — so an ambiguity would have recreated the fault
|
|
687
|
+
* under a different cause.
|
|
688
|
+
*/
|
|
689
|
+
function generatedPathFor(fileName, generated) {
|
|
690
|
+
const posix = (p) => p.split(path.sep).join("/");
|
|
691
|
+
const wanted = posix(fileName);
|
|
692
|
+
const matches = Object.keys(generated).filter((p) => testFileMatches(posix(p), wanted));
|
|
693
|
+
if (matches.length === 1)
|
|
694
|
+
return matches[0];
|
|
695
|
+
if (matches.length === 0)
|
|
696
|
+
return {
|
|
697
|
+
reason: `this run recorded no generated file matching ${fileName}, so its imports could not be read`,
|
|
698
|
+
};
|
|
699
|
+
// Several specs of one name in different directories, and a row that names only
|
|
700
|
+
// the name. Reading the wrong spec's imports is worse than reading none.
|
|
701
|
+
return {
|
|
702
|
+
reason: `${matches.length} generated files match ${fileName} and this row does not say which, so its imports could not be read`,
|
|
703
|
+
};
|
|
704
|
+
}
|
|
650
705
|
async function attachReuseOutcome(test, outcomes, handOffs, retrofits,
|
|
651
706
|
// A blocking verdict measured on the delivered files is collected here rather
|
|
652
707
|
// than written into the row: the caller refuses the report on it. Required, not
|
|
653
708
|
// defaulted — a caller that forgot it would ship the report with no refusal and
|
|
654
709
|
// no compiler error. See the refusal below the row map.
|
|
655
|
-
blocking
|
|
710
|
+
blocking,
|
|
711
|
+
/** Declines this run removed from the delivered files. See stripDeliveredGateMarkers. */
|
|
712
|
+
carriedDeclines,
|
|
713
|
+
/** Filled with the carried declines a finding consumed. What consumed nothing is
|
|
714
|
+
* retired: permission that covers no fault authorises the NEXT one instead. */
|
|
715
|
+
consumed,
|
|
716
|
+
/** Filled with the files whose declines this pass actually MEASURED. Only those may
|
|
717
|
+
* be retired: a pass that read nothing consumes nothing, and deleting on that
|
|
718
|
+
* silence drops permission while the fault it covers is still in the file. */
|
|
719
|
+
evaluated,
|
|
720
|
+
/** The row's absolute path when the caller has it (a maintenance row does; a
|
|
721
|
+
* newTestsCreated row carries a basename only). With it, a record or hand-off
|
|
722
|
+
* that names a same-named spec in another directory is not this row's. */
|
|
723
|
+
specPath,
|
|
724
|
+
/** The delivered file this row is about, absolute — a maintenance row's own path,
|
|
725
|
+
* or the path the generators recorded for a newTestsCreated row. Used ONLY for the
|
|
726
|
+
* import-derived fallback below, never for record matching, so passing it cannot
|
|
727
|
+
* change which record a row attaches.
|
|
728
|
+
*
|
|
729
|
+
* A `{ reason }` says the row HAS a delivered file that could not be identified,
|
|
730
|
+
* and the row states that reason. Omitting it altogether says the row has no
|
|
731
|
+
* delivered file to read — a VERIFY row, or one this run never wrote. */
|
|
732
|
+
deliveredPath) {
|
|
656
733
|
// POM records describe browser specs; a basename collision with an API test's
|
|
657
734
|
// fileName must not attach them there. A utils-path record is attachable anywhere.
|
|
658
|
-
const
|
|
735
|
+
const byBasename = outcomes?.[path.basename(test.fileName)];
|
|
736
|
+
const record = specPath &&
|
|
737
|
+
byBasename?.testFilePath &&
|
|
738
|
+
!samePath(byBasename.testFilePath, specPath)
|
|
739
|
+
? undefined
|
|
740
|
+
: byBasename;
|
|
741
|
+
if (specPath && handOffs) {
|
|
742
|
+
handOffs = Object.fromEntries(Object.entries(handOffs).filter(([file]) => samePath(file, specPath)));
|
|
743
|
+
}
|
|
659
744
|
// A utils record carries the test type its verify ran under: a mismatch is a
|
|
660
745
|
// basename collision with another test, not this row's outcome.
|
|
661
746
|
const typeMatches = !record?.utils?.testType || record.utils.testType === test.testType;
|
|
662
747
|
const found = record && typeMatches && (test.testType === TestType.UI || record.utils)
|
|
663
748
|
? record
|
|
664
749
|
: undefined;
|
|
665
|
-
const rederived = found
|
|
750
|
+
const rederived = found
|
|
751
|
+
? await rederiveReuse(found, carriedDeclines)
|
|
752
|
+
: undefined;
|
|
753
|
+
consumed.push(...(rederived?.carriedConsumed ?? []));
|
|
754
|
+
// A measured pass answers for every decline it was handed, including the ones it
|
|
755
|
+
// found nothing for — that is the answer retirement needs. An unmeasured one
|
|
756
|
+
// (no record attached here, a missing spec identity, or a verifier that threw)
|
|
757
|
+
// answers for none of them, and its files stay out of this set.
|
|
758
|
+
if (rederived?.carriedEvaluated)
|
|
759
|
+
for (const d of carriedDeclines)
|
|
760
|
+
if (d.file)
|
|
761
|
+
evaluated.add(d.file);
|
|
666
762
|
const derived = rederived?.outcome;
|
|
667
763
|
if (rederived?.blocking)
|
|
668
764
|
blocking.push(rederived.blocking);
|
|
669
|
-
// Pre-existing generated tests this spec's reuse pass rewired (SKYR-4276 A4): the
|
|
670
|
-
// report names each with its recorded execution, so a reviewer sees that the
|
|
671
|
-
// module became a dependency of code they already owned — and that it still runs.
|
|
672
|
-
if (derived?.helpers && found?.testFilePath) {
|
|
673
|
-
const specPath = found.testFilePath;
|
|
674
|
-
const mine = retrofits.filter((r) => samePath(r.testFile, specPath));
|
|
675
|
-
if (mine.length > 0)
|
|
676
|
-
derived.helpers.retrofits = mine.map((r) => ({
|
|
677
|
-
file: path.basename(r.file),
|
|
678
|
-
...(r.execution ? { execution: r.execution } : {}),
|
|
679
|
-
}));
|
|
680
|
-
}
|
|
681
765
|
// The RAW record: a colliding record of any kind means reuse ran for this basename,
|
|
682
|
-
// and the chain claim must not be made on the filtered view
|
|
683
|
-
|
|
766
|
+
// and the chain claim must not be made on the filtered view — pendingReuseChain
|
|
767
|
+
// fails open on a record it cannot attribute to the spec in front of it, so
|
|
768
|
+
// claiming the chain was skipped here would put the report and the execute gate
|
|
769
|
+
// in direct disagreement. The hand-offs ARE the path-filtered view, which is the
|
|
770
|
+
// same exact-path lookup that gate makes.
|
|
771
|
+
const chainSkipped = await reuseChainSkipped(test.fileName, test.testType, byBasename, handOffs);
|
|
772
|
+
// No record and no chain claim leaves the row with `reuse: null`, which reads
|
|
773
|
+
// identically to "this test reused nothing" — and is read that way. The record is
|
|
774
|
+
// written by the verify pass, so a row only ever got one when the agent chose to
|
|
775
|
+
// call `skyramp_reuse_code` with `verify: true` on that file: a field describing
|
|
776
|
+
// the tool's own activity, presented as a fact about the delivered test. Contract
|
|
777
|
+
// rows are where it shows, because nothing hands the contract chain a reuse step,
|
|
778
|
+
// but the delivered file still imports and calls the helpers.
|
|
779
|
+
//
|
|
780
|
+
// So answer the row from the file instead. Only where there is nothing else to
|
|
781
|
+
// report: a row that already carries a record or a chain claim is untouched, and
|
|
782
|
+
// this can neither overwrite a measured value nor refuse a report.
|
|
783
|
+
const fallback = !derived && !chainSkipped && deliveredPath && isUtilsReuseEnabled()
|
|
784
|
+
? typeof deliveredPath === "string"
|
|
785
|
+
? await deriveReuseFromDelivered(deliveredPath)
|
|
786
|
+
: { notDerived: deliveredPath.reason }
|
|
787
|
+
: undefined;
|
|
684
788
|
const reuse = derived || chainSkipped
|
|
685
789
|
? { ...(derived ?? {}), ...(chainSkipped ? { chainSkipped } : {}) }
|
|
686
|
-
:
|
|
790
|
+
: fallback;
|
|
687
791
|
if (!reuse)
|
|
688
792
|
return test;
|
|
793
|
+
// Pre-existing generated tests this spec's reuse pass rewired (SKYR-4276 A4): the
|
|
794
|
+
// report names each with its recorded execution, so a reviewer sees that the module
|
|
795
|
+
// became a dependency of code they already owned — and that it still runs.
|
|
796
|
+
//
|
|
797
|
+
// Attached to WHICHEVER helpers outcome reached the row, from the row's own spec
|
|
798
|
+
// path. Gating it on a stored record instead dropped every retrofit from a row
|
|
799
|
+
// answered by the fallback — and the retrofit gate still refuses the report until
|
|
800
|
+
// each one has run, so the run was blocked on a disclosure the report never made.
|
|
801
|
+
const rowSpecPath = found?.testFilePath ??
|
|
802
|
+
(typeof deliveredPath === "string" ? deliveredPath : undefined);
|
|
803
|
+
if (reuse.helpers && rowSpecPath) {
|
|
804
|
+
const mine = retrofits.filter((r) => samePath(r.testFile, rowSpecPath));
|
|
805
|
+
if (mine.length > 0)
|
|
806
|
+
reuse.helpers = {
|
|
807
|
+
...reuse.helpers,
|
|
808
|
+
retrofits: mine.map((r) => ({
|
|
809
|
+
file: path.basename(r.file),
|
|
810
|
+
...(r.execution ? { execution: r.execution } : {}),
|
|
811
|
+
})),
|
|
812
|
+
};
|
|
813
|
+
}
|
|
689
814
|
// A record that re-derives to nothing (e.g. a utils path that wrote no file) must
|
|
690
815
|
// not attach an empty object — consumers treat presence as "reuse ran".
|
|
691
816
|
const hasContent = Object.values(reuse).some((v) => v !== undefined);
|
|
@@ -708,6 +833,164 @@ function attachVideoPath(row, videos) {
|
|
|
708
833
|
videoPath: videos?.[path.basename(row.testFilePath)]?.videoPath,
|
|
709
834
|
};
|
|
710
835
|
}
|
|
836
|
+
/**
|
|
837
|
+
* Remove every comment addressed to a Skyramp gate from the files this run delivers.
|
|
838
|
+
*
|
|
839
|
+
* Two gates ask the agent to write a decline INTO the delivered file — the utils
|
|
840
|
+
* verifier's `reuse-verify: allow` and the POM gate's `// kept inline:` — because a
|
|
841
|
+
* gate on a later run can only read what the repository holds. The customer ends up
|
|
842
|
+
* owning both, and a marker records the state at the moment it was written: nothing
|
|
843
|
+
* re-checks it when the file changes afterwards, so a delivered file can carry a
|
|
844
|
+
* claim its own code contradicts.
|
|
845
|
+
*
|
|
846
|
+
* Called only once the report is WRITTEN, which is the one point that is certainly
|
|
847
|
+
* the accepted path. A refusal must leave every marker in place: the agent repairs
|
|
848
|
+
* and resubmits, and a stripped marker would fire the gate again on a fault it has
|
|
849
|
+
* already declined. Nothing downstream reads a marker — the re-derivation that
|
|
850
|
+
* builds the report rows has already run, and the action stages current working-tree
|
|
851
|
+
* content before it commits (`git update-index --again`, plus the test-directory
|
|
852
|
+
* adds), so an edit made here reaches the commit without any change on that side.
|
|
853
|
+
*
|
|
854
|
+
* The accepted cost: a later run re-detects the same fault and the agent declines it
|
|
855
|
+
* again. That costs turns inside the run and never reaches the customer — and each
|
|
856
|
+
* run then judges the file in front of it rather than honouring a verdict taken
|
|
857
|
+
* against an older one.
|
|
858
|
+
*
|
|
859
|
+
* Best-effort throughout. The report is already on disk, so nothing here may throw.
|
|
860
|
+
*/
|
|
861
|
+
/**
|
|
862
|
+
* The delivered files one reuse record owns: the spec itself, and the shared modules
|
|
863
|
+
* located beside it.
|
|
864
|
+
*
|
|
865
|
+
* Only files this RUN changed. `locateUtilsFiles` also returns a module the spec
|
|
866
|
+
* merely imports, and a marker an older run left in one of those is not this run's to
|
|
867
|
+
* remove — editing it would put a file the run never touched into the customer's pull
|
|
868
|
+
* request. When the change list cannot be read the located set is used unchanged,
|
|
869
|
+
* which is the behaviour every other reuse check falls back to.
|
|
870
|
+
*/
|
|
871
|
+
export async function deliveredFilesOf(record, changed) {
|
|
872
|
+
const testFile = record.testFilePath;
|
|
873
|
+
if (!testFile)
|
|
874
|
+
return [];
|
|
875
|
+
// Both sides of the comparison are canonicalised HERE, because this function owns
|
|
876
|
+
// the comparison. `listChangedFilesAbs` resolves its paths and the located files are
|
|
877
|
+
// real paths, so on a repository whose path traverses a symlink the two spellings
|
|
878
|
+
// never matched and nothing was stripped — silently, and only on those setups.
|
|
879
|
+
const changedReal = changed
|
|
880
|
+
? new Set(await Promise.all([...changed].map(realpath)))
|
|
881
|
+
: undefined;
|
|
882
|
+
// The spec takes the same membership check as the modules beside it. Exempting it
|
|
883
|
+
// let the strip edit a spec this run never touched — the same fault as an
|
|
884
|
+
// imported-only module, and the same new diff in the customer's pull request.
|
|
885
|
+
const spec = await realpath(testFile);
|
|
886
|
+
const files = !changedReal || changedReal.has(spec) ? [spec] : [];
|
|
887
|
+
try {
|
|
888
|
+
for (const utilsFile of await locateUtilsFiles({
|
|
889
|
+
testFile,
|
|
890
|
+
language: record.language,
|
|
891
|
+
})) {
|
|
892
|
+
const real = await realpath(utilsFile);
|
|
893
|
+
if (!changedReal || changedReal.has(real))
|
|
894
|
+
files.push(real);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
catch (err) {
|
|
898
|
+
logger.warning("Could not locate the shared utils files for a delivered spec", { testFile, error: String(err) });
|
|
899
|
+
}
|
|
900
|
+
return files;
|
|
901
|
+
}
|
|
902
|
+
async function stripDeliveredGateMarkers(outcomes, retrofits,
|
|
903
|
+
/** Absolute paths this run changed, or undefined when they cannot be read. */
|
|
904
|
+
changed) {
|
|
905
|
+
const stripped = {};
|
|
906
|
+
const files = new Set();
|
|
907
|
+
// Each grammar has ONE file role its gate reads it from: `verifyUtils` reads
|
|
908
|
+
// `reuse-verify: allow` out of a located utils module, and the re-derivation reads
|
|
909
|
+
// `// kept inline:` out of the record's spec. Parsing both out of every file made a
|
|
910
|
+
// marker in the wrong place MORE powerful than the gate treats it: an allow the
|
|
911
|
+
// agent wrote into the spec is ignored where it sits, but was recorded here and
|
|
912
|
+
// then authorised the next matching helper fault. The strip still removes it —
|
|
913
|
+
// nothing addressed to the gate ships — it is only not carried.
|
|
914
|
+
const specPaths = new Set(await Promise.all(Object.values(outcomes ?? {})
|
|
915
|
+
.map((r) => r?.testFilePath)
|
|
916
|
+
.filter((p) => !!p)
|
|
917
|
+
.map(realpath)));
|
|
918
|
+
// Every path is canonicalised here and in the record, for the reason `reuseHandOffs`
|
|
919
|
+
// keys on `realpath`: a tool-supplied path may reach the same file through a symlink
|
|
920
|
+
// (macOS resolves /tmp to /private/tmp), so a raw key written by one source and read
|
|
921
|
+
// from another names one file twice.
|
|
922
|
+
// Every file this run CHANGED, when the change list can be read. Recording a reuse
|
|
923
|
+
// outcome is best-effort, so a changed file with no record would never be scanned —
|
|
924
|
+
// and its marker would ship, against what AC5 and this tool's description promise.
|
|
925
|
+
// The records are the fallback for when the change list is not available.
|
|
926
|
+
// Canonicalised on the way in, like every other source. `listChangedFilesAbs` only
|
|
927
|
+
// RESOLVES its names, against the root git was given, so a checkout reached through
|
|
928
|
+
// a symlink keeps that spelling — and the record would name a file the next
|
|
929
|
+
// submission looks up by real path, losing the decline it just carried.
|
|
930
|
+
if (changed)
|
|
931
|
+
for (const file of changed)
|
|
932
|
+
files.add(await realpath(file));
|
|
933
|
+
else
|
|
934
|
+
for (const record of Object.values(outcomes ?? {}))
|
|
935
|
+
for (const file of await deliveredFilesOf(record ?? {}, undefined))
|
|
936
|
+
files.add(file);
|
|
937
|
+
// Pre-existing generated tests this run rewired: the delivery commits them too.
|
|
938
|
+
for (const retrofit of retrofits)
|
|
939
|
+
files.add(await realpath(retrofit.file));
|
|
940
|
+
for (const file of files) {
|
|
941
|
+
const spec = utilsSpecForFile(file);
|
|
942
|
+
if (!spec)
|
|
943
|
+
continue;
|
|
944
|
+
try {
|
|
945
|
+
const before = await fs.readFile(file, "utf-8");
|
|
946
|
+
const { content, removed, unremovable } = stripGateMarkers(before, spec);
|
|
947
|
+
if (unremovable.length > 0) {
|
|
948
|
+
logger.warning("A gate-addressed comment could not be removed from a delivered file without risking its syntax — it ships as it stands", { file, markers: unremovable });
|
|
949
|
+
}
|
|
950
|
+
if (removed.length === 0)
|
|
951
|
+
continue;
|
|
952
|
+
// The last check before the customer's file changes, and the only one that does
|
|
953
|
+
// not depend on the strip being right. Every defect this had was a case nobody
|
|
954
|
+
// foresaw; this states the one edit that is permitted and refuses the rest, so
|
|
955
|
+
// an unforeseen case delivers the marker instead of a file that does not build.
|
|
956
|
+
if (!onlyCommentLinesRemoved(before, content, spec.commentPrefix)) {
|
|
957
|
+
logger.warning("The gate-comment strip would have changed more than whole comment lines — the file is left exactly as delivered", { file, removed });
|
|
958
|
+
continue;
|
|
959
|
+
}
|
|
960
|
+
await fs.writeFile(file, content, "utf-8");
|
|
961
|
+
// Parsed, not kept as text: what a later call needs is WHICH decline was made,
|
|
962
|
+
// so it can read it the way it reads a marker still in the file. Keeping the
|
|
963
|
+
// line and appending it back edited the customer's file a second time, and an
|
|
964
|
+
// agent that reformatted or reworded the decline in between defeated the
|
|
965
|
+
// substring match in both directions.
|
|
966
|
+
const text = removed.join("\n");
|
|
967
|
+
const declines = [
|
|
968
|
+
...((await hasUtilsHeader(file))
|
|
969
|
+
? parseUtilsAllows(text, spec).allows.map(({ kind, helper }) => ({
|
|
970
|
+
kind,
|
|
971
|
+
helper,
|
|
972
|
+
}))
|
|
973
|
+
: []),
|
|
974
|
+
...(specPaths.has(file)
|
|
975
|
+
? parseKeptInline(text).map(({ pageObject, reason }) => ({
|
|
976
|
+
keptInline: pageObject,
|
|
977
|
+
reason,
|
|
978
|
+
}))
|
|
979
|
+
: []),
|
|
980
|
+
];
|
|
981
|
+
if (declines.length > 0)
|
|
982
|
+
stripped[file] = declines;
|
|
983
|
+
logger.info("Removed gate-addressed comment(s) from a delivered file", {
|
|
984
|
+
file,
|
|
985
|
+
removed,
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
catch (err) {
|
|
989
|
+
logger.warning("Could not strip gate-addressed comments from a delivered file", { file, error: String(err) });
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
return stripped;
|
|
993
|
+
}
|
|
711
994
|
/** Two-space indent on every line, so a multi-line verdict reads as one bullet's body. */
|
|
712
995
|
function indent(text) {
|
|
713
996
|
return text
|
|
@@ -740,20 +1023,13 @@ function deduplicateById(items) {
|
|
|
740
1023
|
* basename. A file can own several rows: failed if ANY failed, first failure
|
|
741
1024
|
* carries the text; `Skipped` rows are evidence in neither direction. */
|
|
742
1025
|
function collectExecutionOutcomes(delivered, results) {
|
|
743
|
-
// Absent names nothing to disagree with, so it matches any — the same rule
|
|
744
|
-
// `checkEveryDeliveredTestRan` uses.
|
|
745
|
-
const sameRepository = (a, b) => {
|
|
746
|
-
const left = (a ?? "").trim();
|
|
747
|
-
const right = (b ?? "").trim();
|
|
748
|
-
return left === "" || right === "" || left === right;
|
|
749
|
-
};
|
|
750
1026
|
const outcomes = [];
|
|
751
1027
|
for (const test of delivered) {
|
|
752
1028
|
const plannedTestId = test.plannedTestId?.trim();
|
|
753
1029
|
if (!plannedTestId)
|
|
754
1030
|
continue;
|
|
755
1031
|
const rows = results.filter((row) => row.status !== "Skipped" &&
|
|
756
|
-
|
|
1032
|
+
row.repository === test.repository &&
|
|
757
1033
|
testFileMatches(row.testFilePath, test.fileName));
|
|
758
1034
|
if (rows.length === 0)
|
|
759
1035
|
continue;
|
|
@@ -819,23 +1095,15 @@ function checkOneEntryPerCandidate(delivered) {
|
|
|
819
1095
|
* a planned test with no outcome, so a filename the results do not carry was the
|
|
820
1096
|
* way past it. This is the check that says so. */
|
|
821
1097
|
function checkEveryDeliveredTestRan(delivered, results) {
|
|
822
|
-
// A side that names no repository names none to disagree with, so it matches
|
|
823
|
-
// any. A single-repo run fills the field on one list and not the other often
|
|
824
|
-
// enough that requiring both to agree reported tests that had plainly run.
|
|
825
|
-
const sameRepository = (a, b) => {
|
|
826
|
-
const left = (a ?? "").trim();
|
|
827
|
-
const right = (b ?? "").trim();
|
|
828
|
-
return left === "" || right === "" || left === right;
|
|
829
|
-
};
|
|
830
1098
|
return delivered
|
|
831
|
-
.filter((test) => !results.some((row) =>
|
|
1099
|
+
.filter((test) => !results.some((row) => row.repository === test.repository &&
|
|
832
1100
|
testFileMatches(row.testFilePath, test.fileName)))
|
|
833
1101
|
.map((test) => {
|
|
834
1102
|
// Say which half failed. "No row names that file" reads as a missing run
|
|
835
1103
|
// when the file is there and only the repository differs.
|
|
836
1104
|
const byName = results.filter((row) => testFileMatches(row.testFilePath, test.fileName));
|
|
837
1105
|
const evidence = byName.length
|
|
838
|
-
? `reported as ${test.fileName} for ${test.repository
|
|
1106
|
+
? `reported as ${test.fileName} for ${test.repository}, and the ${byName.length === 1 ? "row" : "rows"} naming that file ${byName.length === 1 ? "is" : "are"} for ${byName.map((row) => row.repository).join(", ")}`
|
|
839
1107
|
: `reported as ${test.fileName}, no testResults row names that file`;
|
|
840
1108
|
return {
|
|
841
1109
|
objectionId: `deliveredTestRan:${test.testId}`,
|
|
@@ -846,15 +1114,95 @@ function checkEveryDeliveredTestRan(delivered, results) {
|
|
|
846
1114
|
};
|
|
847
1115
|
});
|
|
848
1116
|
}
|
|
849
|
-
/**
|
|
850
|
-
* is
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
1117
|
+
/** A report cannot treat an explicit skip as a completed execution. Unlike an absent
|
|
1118
|
+
* result, this is a direct claim that the agent chose not to run a delivered test. */
|
|
1119
|
+
function skippedDeliveredTests(delivered, results) {
|
|
1120
|
+
return delivered.filter((test) => {
|
|
1121
|
+
const matching = results.filter((row) => row.repository === test.repository &&
|
|
1122
|
+
testFileMatches(row.testFilePath, test.fileName));
|
|
1123
|
+
return (matching.some((row) => row.status === "Skipped") &&
|
|
1124
|
+
!matching.some((row) => row.status !== "Skipped"));
|
|
1125
|
+
});
|
|
1126
|
+
}
|
|
1127
|
+
/** Whether the file can be read. The written record outlives the file, and a
|
|
1128
|
+
* path that exists but cannot be opened owes no run either — the objection
|
|
1129
|
+
* would name a file nobody can look at. */
|
|
1130
|
+
async function pathExists(file) {
|
|
1131
|
+
try {
|
|
1132
|
+
await fs.access(file, fsConstants.R_OK);
|
|
1133
|
+
return true;
|
|
1134
|
+
}
|
|
1135
|
+
catch {
|
|
1136
|
+
return false;
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
/** Whether the recorded run is a run of what this run WROTE.
|
|
1140
|
+
*
|
|
1141
|
+
* An edited file is run twice — once before the edit for a baseline, once
|
|
1142
|
+
* after — and only the second says anything about the edit, so a file left with
|
|
1143
|
+
* just its baseline has not been run in the sense this check is about.
|
|
1144
|
+
*
|
|
1145
|
+
* A generated file has no baseline of its own, but the path may still carry an
|
|
1146
|
+
* older run: REGENERATE rewrites a file that already existed and was already
|
|
1147
|
+
* run, and generating twice to one path does the same. The run has to be later
|
|
1148
|
+
* than the write, or it is a run of bytes that are gone. An unreadable or
|
|
1149
|
+
* missing timestamp on either side falls back to "a run is a run", which is the
|
|
1150
|
+
* behaviour before this and never invents an objection. */
|
|
1151
|
+
function runOfWhatWasWritten(file) {
|
|
1152
|
+
if (file.how === "updated")
|
|
1153
|
+
return file.recordedPhase === "after";
|
|
1154
|
+
if (file.recordedPhase === undefined)
|
|
1155
|
+
return false;
|
|
1156
|
+
const wrote = file.generatedAt;
|
|
1157
|
+
// The execution record keeps an ISO string, written by the execution path
|
|
1158
|
+
// rather than here, so it is parsed rather than changed in this PR.
|
|
1159
|
+
const ran = Date.parse(file.executedAt ?? "");
|
|
1160
|
+
if (wrote === undefined || Number.isNaN(ran))
|
|
1161
|
+
return true;
|
|
1162
|
+
return ran >= wrote;
|
|
1163
|
+
}
|
|
1164
|
+
/** Every test file this run wrote has an execution this server performed.
|
|
1165
|
+
*
|
|
1166
|
+
* Informational, like every check here: it surfaces the gap for a person to read
|
|
1167
|
+
* and the agent can answer it. The agent is not blocked — a hard ban on running a
|
|
1168
|
+
* test outside the tool can dead-end a run, which is why the prompt states a
|
|
1169
|
+
* preference rather than a rule.
|
|
1170
|
+
*
|
|
1171
|
+
* The two lists it joins are both the server's own, keyed by canonical absolute
|
|
1172
|
+
* path: `generatedTests`, written by the generators, and `testExecutions`,
|
|
1173
|
+
* written by skyramp_execute_test. The agent supplies neither, so unlike the
|
|
1174
|
+
* report-side checks this cannot be satisfied by what the report claims.
|
|
1175
|
+
*
|
|
1176
|
+
* What it proves is that the file was handed to the runner. It is NOT proof that
|
|
1177
|
+
* a test ran: a runner can exit 0 having executed nothing. */
|
|
1178
|
+
function checkEveryWrittenTestRan(written) {
|
|
1179
|
+
return written
|
|
1180
|
+
.filter((file) => !runOfWhatWasWritten(file))
|
|
1181
|
+
.map((file) => ({
|
|
1182
|
+
// The path is the id, because the agent quotes the id back in `answers` and a
|
|
1183
|
+
// path is what it can recognise. Everything after the first colon is that
|
|
1184
|
+
// path, colons included — a reader must not split the id on every colon.
|
|
1185
|
+
objectionId: `testRanOnThisServer:${file.file}`,
|
|
1186
|
+
verifier: "testRanOnThisServer",
|
|
1187
|
+
// Says what is recorded, never that the file went unrun: the agent is
|
|
1188
|
+
// allowed to run a test with the repository's own runner, and a run it
|
|
1189
|
+
// drove itself is not visible here.
|
|
1190
|
+
message: file.how === "generated"
|
|
1191
|
+
? "This run generated this test file, and this server recorded no run of it after it was written."
|
|
1192
|
+
: file.recordedPhase === "before"
|
|
1193
|
+
? "This run edited this test file, and the only run of it this server recorded is from before the edit."
|
|
1194
|
+
: "This run edited this test file, and this server recorded no run of it.",
|
|
1195
|
+
evidence: file.recordedPhase === "before"
|
|
1196
|
+
? `${file.file} — the only recorded skyramp_execute_test run of this file is the pre-edit baseline`
|
|
1197
|
+
: file.how === "generated" && file.recordedPhase !== undefined
|
|
1198
|
+
? `${file.file} — the last recorded skyramp_execute_test run of this path is older than the file this run wrote`
|
|
1199
|
+
: `${file.file} — no skyramp_execute_test run of this file is recorded`,
|
|
1200
|
+
suggestion: "Either run the file with `skyramp_execute_test`, or answer this objection in `answers` — say how the test was run, or why it was not run. A `testResults` row does not answer this: the run is checked against the executions this server performed, not against what the report claims.",
|
|
1201
|
+
}));
|
|
1202
|
+
}
|
|
1203
|
+
/** The report accepts every `TestType`, so an entry outside the four a plan can
|
|
1204
|
+
* express can never name a planned test and must not be asked to. */
|
|
1205
|
+
const PLANNABLE_TEST_TYPES = new Set(TESTBOT_SUPPORTED_TEST_TYPES);
|
|
858
1206
|
function runPostExecutionChecks(plan, delivered, results, issues,
|
|
859
1207
|
/** The plan-only lane delivers nothing, so there a planned test is proof
|
|
860
1208
|
* enough for a bug. Passed in, not read here, so the checks stay pure. */
|
|
@@ -864,9 +1212,12 @@ planOnly, maintained = []) {
|
|
|
864
1212
|
// unplanned: a plan cannot hold one, so the objection would name a mistake the
|
|
865
1213
|
// agent has no way to fix.
|
|
866
1214
|
.filter((test) => PLANNABLE_TEST_TYPES.has(test.testType))
|
|
867
|
-
.
|
|
868
|
-
.
|
|
869
|
-
|
|
1215
|
+
.flatMap((test) => {
|
|
1216
|
+
const plannedTestId = test.plannedTestId?.trim();
|
|
1217
|
+
return plannedTestId
|
|
1218
|
+
? [{ plannedTestId, repository: test.repository }]
|
|
1219
|
+
: [];
|
|
1220
|
+
});
|
|
870
1221
|
return [
|
|
871
1222
|
...runPostExecutionCheck("deliveredMatchesPlan", "deliveredMatchesPlan", () => checkDeliveredMatchesPlan(plan, shipped)),
|
|
872
1223
|
// The maintenance mirror: a `maintains` entry credited coverage at plan time,
|
|
@@ -881,17 +1232,37 @@ planOnly, maintained = []) {
|
|
|
881
1232
|
...runPostExecutionCheck("defectsReported", "defects:unreported", () => checkDefectsReported(plan, issues)),
|
|
882
1233
|
];
|
|
883
1234
|
}
|
|
1235
|
+
/**
|
|
1236
|
+
* Dependency edits can land in the primary checkout, a separate tests checkout,
|
|
1237
|
+
* or a related repository. Verify each configured path once, skipping absent paths.
|
|
1238
|
+
*/
|
|
1239
|
+
function collectDependencyVerificationRoots(primaryRepoRoot, testsRepoRoot, relatedRepos) {
|
|
1240
|
+
const roots = new Set([primaryRepoRoot]);
|
|
1241
|
+
if (testsRepoRoot) {
|
|
1242
|
+
roots.add(testsRepoRoot);
|
|
1243
|
+
}
|
|
1244
|
+
for (const repo of Object.values(relatedRepos ?? {})) {
|
|
1245
|
+
if (repo.repositoryPath) {
|
|
1246
|
+
roots.add(repo.repositoryPath);
|
|
1247
|
+
}
|
|
1248
|
+
}
|
|
1249
|
+
return Array.from(roots);
|
|
1250
|
+
}
|
|
884
1251
|
export function registerSubmitReportTool(server) {
|
|
885
1252
|
server.registerTool(TOOL_NAME, {
|
|
886
1253
|
annotations: {
|
|
887
1254
|
readOnlyHint: false,
|
|
888
|
-
|
|
1255
|
+
// Overwrites the report file on disk, AND edits delivered source files on an
|
|
1256
|
+
// accepted call: a comment addressed to a Skyramp gate is removed from each
|
|
1257
|
+
// file this run changed. See stripDeliveredGateMarkers.
|
|
1258
|
+
destructiveHint: true,
|
|
889
1259
|
idempotentHint: false,
|
|
890
1260
|
openWorldHint: false,
|
|
891
1261
|
},
|
|
892
1262
|
description: "Submit the final testbot report. Call this tool once, after all test analysis, generation and execution. " +
|
|
893
1263
|
"This is the ONLY way to submit the report — do NOT write the report to a file manually. " +
|
|
894
|
-
"If a check objects to what you reported, the objection names an id: fix what you can, and answer the rest through `answers` on a second call."
|
|
1264
|
+
"If a check objects to what you reported, the objection names an id: fix what you can, and answer the rest through `answers` on a second call. " +
|
|
1265
|
+
"On an ACCEPTED call this tool also edits the files this run changed: a comment addressed to a Skyramp gate — `reuse-verify: allow …` or `// kept inline: …` — is removed from each of them, because it speaks to a gate and not to the customer who owns the file. Nothing else in those files is touched. A REFUSED call leaves every source file exactly as it found it; it still records its own run state, such as the refusal count and the objections it raised.",
|
|
895
1266
|
inputSchema: {
|
|
896
1267
|
businessCaseAnalysis: z
|
|
897
1268
|
.string()
|
|
@@ -951,8 +1322,9 @@ export function registerSubmitReportTool(server) {
|
|
|
951
1322
|
stateFile: z
|
|
952
1323
|
.string()
|
|
953
1324
|
.optional()
|
|
954
|
-
.describe("Optional
|
|
955
|
-
"
|
|
1325
|
+
.describe("Optional, and a Testbot run leaves it out: the run finds its own state file. " +
|
|
1326
|
+
"A path is accepted only when it names that same file — one from another run holds " +
|
|
1327
|
+
"another run's execution results, and the call is refused. " +
|
|
956
1328
|
"The report is written beside it, and it holds the execution results, the " +
|
|
957
1329
|
"page captures and the videos this report needs."),
|
|
958
1330
|
},
|
|
@@ -967,7 +1339,9 @@ export function registerSubmitReportTool(server) {
|
|
|
967
1339
|
// event in the finally. Empty (no properties) for non-enforced runs.
|
|
968
1340
|
const languageTelemetry = {};
|
|
969
1341
|
// The run's own state file, unless the agent named one.
|
|
970
|
-
const stateFile =
|
|
1342
|
+
const { path: stateFile, refusal } = resolveOwnRunStatePath(params.stateFile);
|
|
1343
|
+
if (refusal)
|
|
1344
|
+
return toolError(refusal);
|
|
971
1345
|
if (!stateFile) {
|
|
972
1346
|
return toolError("This run has no state file. Call skyramp_analyze_changes first — it writes the state file this report goes beside.");
|
|
973
1347
|
}
|
|
@@ -1145,8 +1519,7 @@ export function registerSubmitReportTool(server) {
|
|
|
1145
1519
|
const plannedTest = plannedById.get((t.plannedTestId ?? "").trim());
|
|
1146
1520
|
if (!plannedTest)
|
|
1147
1521
|
return true;
|
|
1148
|
-
|
|
1149
|
-
return plannedType !== "" && plannedType !== t.testType;
|
|
1522
|
+
return plannedTest.scenario.testType !== t.testType;
|
|
1150
1523
|
});
|
|
1151
1524
|
if (unapproved.length > 0) {
|
|
1152
1525
|
const plannedList = plannedById.size > 0
|
|
@@ -1168,13 +1541,10 @@ export function registerSubmitReportTool(server) {
|
|
|
1168
1541
|
// Read once — the file can carry a large diffText and readRepoData would
|
|
1169
1542
|
// re-parse it twice per repo.
|
|
1170
1543
|
const fullMaintenanceState = await stateManager.readFullState();
|
|
1171
|
-
// The
|
|
1172
|
-
//
|
|
1173
|
-
|
|
1174
|
-
const
|
|
1175
|
-
process.env.GITHUB_REPOSITORY?.trim() ||
|
|
1176
|
-
undefined;
|
|
1177
|
-
const maintenanceSections = [{ data: stateData }];
|
|
1544
|
+
// The handler already refused above when the file was missing or empty,
|
|
1545
|
+
// and the run that created it names the primary on the root.
|
|
1546
|
+
const primaryRepository = fullMaintenanceState.metadata.repository;
|
|
1547
|
+
const maintenanceSections = [{ repository: primaryRepository, data: stateData }];
|
|
1178
1548
|
for (const [repo, section] of Object.entries(fullMaintenanceState?.relatedRepos ?? {})) {
|
|
1179
1549
|
// Only these two keys resolve back to the primary, already collected
|
|
1180
1550
|
// above. Any other key — a case variant, or another name over the same
|
|
@@ -1189,27 +1559,16 @@ export function registerSubmitReportTool(server) {
|
|
|
1189
1559
|
maintenanceSections.push({ repository: repo, data });
|
|
1190
1560
|
}
|
|
1191
1561
|
// Stamp every row, primary included and single-repo runs included — as the
|
|
1192
|
-
// prompt already requires of the other report sections.
|
|
1193
|
-
//
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
// the primary and the run is not on a GitHub runner either. Only sections
|
|
1197
|
-
// that actually produce rows need a name — a run with no verdicts at all
|
|
1198
|
-
// has nothing to attribute and must still be able to submit.
|
|
1199
|
-
if (!primaryRepository &&
|
|
1200
|
-
maintenanceSections.some((section) => !section.repository &&
|
|
1201
|
-
(section.data.maintenanceVerdicts?.length ?? 0) > 0)) {
|
|
1202
|
-
errorResult = toolError("Cannot name the repository for the maintenance rows: the stateFile records no primary repository " +
|
|
1203
|
-
"and GITHUB_REPOSITORY is unset. Pass `repository` (and `primaryRepository` in a multi-repo run) " +
|
|
1204
|
-
"to skyramp_analyze_changes, then re-run it.");
|
|
1205
|
-
return errorResult;
|
|
1206
|
-
}
|
|
1562
|
+
// prompt already requires of the other report sections. The primary's
|
|
1563
|
+
// section carries the name the state file's root records; a related repo's
|
|
1564
|
+
// carries its own section key.
|
|
1565
|
+
const repositoryOf = (section) => section.repository;
|
|
1207
1566
|
// Reject rather than throw: a non-array here, or a missing testFilePath
|
|
1208
1567
|
// below, escapes the handler and loses the analytics event in its `finally`.
|
|
1209
1568
|
const malformedSections = maintenanceSections
|
|
1210
1569
|
.filter(({ data }) => data.maintenanceVerdicts !== undefined &&
|
|
1211
1570
|
!Array.isArray(data.maintenanceVerdicts))
|
|
1212
|
-
.map(({ repository }) => repository
|
|
1571
|
+
.map(({ repository }) => repository);
|
|
1213
1572
|
if (malformedSections.length > 0) {
|
|
1214
1573
|
errorResult = toolError(`stateFile has a non-array maintenanceVerdicts for: ${malformedSections.join(", ")}. ` +
|
|
1215
1574
|
`Re-run skyramp_actions for that repository to rewrite the section.`);
|
|
@@ -1225,9 +1584,11 @@ export function registerSubmitReportTool(server) {
|
|
|
1225
1584
|
// verdict PER page object, so a spec-keyed map keeps only the last.
|
|
1226
1585
|
const rawMaintenance = maintenanceSections.flatMap((section) => (section.data.maintenanceVerdicts ?? [])
|
|
1227
1586
|
.filter((v) => {
|
|
1228
|
-
if (v &&
|
|
1587
|
+
if (v &&
|
|
1588
|
+
typeof v.testFilePath === "string" &&
|
|
1589
|
+
v.testFilePath.trim())
|
|
1229
1590
|
return true;
|
|
1230
|
-
badVerdicts.push(section.repository
|
|
1591
|
+
badVerdicts.push(section.repository);
|
|
1231
1592
|
return false;
|
|
1232
1593
|
})
|
|
1233
1594
|
.map((v) => ({
|
|
@@ -1237,6 +1598,7 @@ export function registerSubmitReportTool(server) {
|
|
|
1237
1598
|
testType: v.testType,
|
|
1238
1599
|
endpoint: v.endpoint,
|
|
1239
1600
|
action: v.action,
|
|
1601
|
+
reportOnly: v.reportOnly,
|
|
1240
1602
|
// A baseline refresh (SKYR-4298) leaves the spec untouched, so name the
|
|
1241
1603
|
// replaced PNGs here — the reviewer's cue to open the image diff in the PR.
|
|
1242
1604
|
description: v.rebaselineSnapshots?.length
|
|
@@ -1273,6 +1635,12 @@ export function registerSubmitReportTool(server) {
|
|
|
1273
1635
|
// draft a summary for it) — collected here and rejected below, rather than
|
|
1274
1636
|
// silently shipping a blank field.
|
|
1275
1637
|
const missingDetails = [];
|
|
1638
|
+
// An id no plan defect declares traces nothing, exactly as `defectId` on an
|
|
1639
|
+
// issue does not: without this the row could name a defect the run invented.
|
|
1640
|
+
const declaredDefectIds = new Set((stateData.plan?.defects ?? [])
|
|
1641
|
+
.map((d) => String(d?.id ?? "").trim())
|
|
1642
|
+
.filter((id) => id.length > 0));
|
|
1643
|
+
const undeclaredDefects = [];
|
|
1276
1644
|
if (dedupedMaintenance.length > 0) {
|
|
1277
1645
|
// beforeStatus/afterStatus are always stateFile-authoritative. beforeDetails/
|
|
1278
1646
|
// afterDetails come from the LLM's drafted summary (testMaintenanceDetails) —
|
|
@@ -1299,20 +1667,39 @@ export function registerSubmitReportTool(server) {
|
|
|
1299
1667
|
// must not satisfy the "drafted a summary" requirement.
|
|
1300
1668
|
const beforeDetails = detail?.beforeDetails?.trim() ?? "";
|
|
1301
1669
|
const afterDetails = detail?.afterDetails?.trim() ?? "";
|
|
1302
|
-
|
|
1670
|
+
// A recorded run owes a summary. The Unknown placeholder a reservation
|
|
1671
|
+
// leaves when the run never landed a result (SKYR-4460) is not one:
|
|
1672
|
+
// there is no output to summarise, and the row publishes Unknown.
|
|
1673
|
+
const recordedBefore = recorded?.executionBefore &&
|
|
1674
|
+
recorded.executionBefore.status !== TestExecutionStatus.Unknown;
|
|
1675
|
+
const recordedAfter = recorded?.executionAfter &&
|
|
1676
|
+
recorded.executionAfter.status !== TestExecutionStatus.Unknown;
|
|
1677
|
+
if (recordedBefore && !beforeDetails)
|
|
1303
1678
|
missingDetails.push(`${displayName} (beforeDetails)`);
|
|
1304
|
-
if (
|
|
1679
|
+
if (recordedAfter && !afterDetails)
|
|
1305
1680
|
missingDetails.push(`${displayName} (afterDetails)`);
|
|
1306
1681
|
logger.info(`${displayName}: before=${beforeStatus} after=${afterStatus}`);
|
|
1682
|
+
const defects = (detail?.defects ?? [])
|
|
1683
|
+
.map((id) => String(id ?? "").trim())
|
|
1684
|
+
.filter((id) => id.length > 0);
|
|
1685
|
+
for (const id of defects)
|
|
1686
|
+
if (!declaredDefectIds.has(id))
|
|
1687
|
+
undeclaredDefects.push(`${displayName}: ${id}`);
|
|
1307
1688
|
return {
|
|
1308
1689
|
...m,
|
|
1309
1690
|
beforeDetails,
|
|
1310
1691
|
afterDetails,
|
|
1692
|
+
defects,
|
|
1311
1693
|
beforeStatus,
|
|
1312
1694
|
afterStatus,
|
|
1313
1695
|
};
|
|
1314
1696
|
});
|
|
1315
1697
|
}
|
|
1698
|
+
if (undeclaredDefects.length > 0) {
|
|
1699
|
+
errorResult = toolError(`${undeclaredDefects.length} maintenance row(s) name a defect the plan does not declare: ${undeclaredDefects.join(", ")}. ` +
|
|
1700
|
+
"Spell each id as your registered `defects` list spells it, or leave it out.");
|
|
1701
|
+
return errorResult;
|
|
1702
|
+
}
|
|
1316
1703
|
if (missingDetails.length > 0) {
|
|
1317
1704
|
const uniqueMissing = [...new Set(missingDetails)];
|
|
1318
1705
|
errorResult = toolError(`${uniqueMissing.length} maintenance row(s) have a recorded execution but no drafted summary: ${uniqueMissing.join(", ")}. ` +
|
|
@@ -1324,6 +1711,9 @@ export function registerSubmitReportTool(server) {
|
|
|
1324
1711
|
// rows below; an unexecuted one refuses the report (inside the SKYR-3883 block,
|
|
1325
1712
|
// which already enumerates the working tree).
|
|
1326
1713
|
let retrofitViews = [];
|
|
1714
|
+
/** Absolute paths this run changed. Undefined when they cannot be read — every
|
|
1715
|
+
* reuse check falls back to acting on what it located. */
|
|
1716
|
+
let changedFilesAbs;
|
|
1327
1717
|
// SKYR-3883: in a testbot run, refuse to ship a report that claims file work
|
|
1328
1718
|
// the working tree doesn't reflect. The delivery step can only commit what the
|
|
1329
1719
|
// agent actually created/edited, so a report claiming otherwise erodes trust
|
|
@@ -1335,6 +1725,25 @@ export function registerSubmitReportTool(server) {
|
|
|
1335
1725
|
const fullState = await stateManager.readFullState();
|
|
1336
1726
|
const repoRoot = fullState?.metadata?.repositoryPath;
|
|
1337
1727
|
if (repoRoot && repoRoot !== "unknown") {
|
|
1728
|
+
const dependencyRoots = collectDependencyVerificationRoots(repoRoot, getTestsRepoDir(), fullState?.relatedRepos);
|
|
1729
|
+
for (const dependencyRoot of dependencyRoots) {
|
|
1730
|
+
try {
|
|
1731
|
+
const dependencyVerification = await verifyTestDependencyChanges(dependencyRoot);
|
|
1732
|
+
if (!dependencyVerification.ok) {
|
|
1733
|
+
errorResult = toolError("The working tree violates the safe test-only dependency policy:\n" +
|
|
1734
|
+
dependencyVerification.violations
|
|
1735
|
+
.map((violation) => ` - ${violation}`)
|
|
1736
|
+
.join("\n") +
|
|
1737
|
+
"\nMove approved packages to the required development/test scope, remove unapproved or unrelated manifest edits, run skyramp_verify_test_dependencies, and submit the report again.");
|
|
1738
|
+
return errorResult;
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
catch (err) {
|
|
1742
|
+
errorResult = toolError(`Could not verify test dependency changes in ${dependencyRoot}: ${err.message}. ` +
|
|
1743
|
+
"Testbot will not submit the report until every configured repository can be checked.");
|
|
1744
|
+
return errorResult;
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1338
1747
|
try {
|
|
1339
1748
|
// A run writes tests into up to three kinds of trees: the primary
|
|
1340
1749
|
// checkout, the tests-repo checkout (testRepoPath), and related
|
|
@@ -1363,7 +1772,11 @@ export function registerSubmitReportTool(server) {
|
|
|
1363
1772
|
changedFiles,
|
|
1364
1773
|
newTests: dedupedNewTests,
|
|
1365
1774
|
verdicts: stateData.maintenanceVerdicts ?? [],
|
|
1366
|
-
primaryRepository
|
|
1775
|
+
primaryRepository,
|
|
1776
|
+
knownRepositories: [
|
|
1777
|
+
primaryRepository,
|
|
1778
|
+
...Object.keys(fullState?.relatedRepos ?? {}),
|
|
1779
|
+
],
|
|
1367
1780
|
// SKYR-4250: captured at prompt-render time, since planOnly is a
|
|
1368
1781
|
// prompt-only argument (mirrors the SKYR-4185 language capture).
|
|
1369
1782
|
planOnly: isPlanOnlyMode(),
|
|
@@ -1392,7 +1805,7 @@ export function registerSubmitReportTool(server) {
|
|
|
1392
1805
|
`Files with actual working-tree changes:\n${changedList || " (none)"}`);
|
|
1393
1806
|
return errorResult;
|
|
1394
1807
|
}
|
|
1395
|
-
|
|
1808
|
+
changedFilesAbs = await listChangedFilesAbs([
|
|
1396
1809
|
repoRoot,
|
|
1397
1810
|
getTestsRepoDir(),
|
|
1398
1811
|
...Object.values(fullState?.relatedRepos ?? {}).map((section) => section.repositoryPath),
|
|
@@ -1417,7 +1830,7 @@ export function registerSubmitReportTool(server) {
|
|
|
1417
1830
|
// `repository` is normalized BEFORE the check so the attribution the check
|
|
1418
1831
|
// reconciles is the one the report ships, and so the stamp below survives
|
|
1419
1832
|
// into the written file — this array is what the report is built from.
|
|
1420
|
-
const issuesFound = params.issuesFound
|
|
1833
|
+
const issuesFound = params.issuesFound;
|
|
1421
1834
|
if (issuesFound.some((issue) => issue.sourceFile?.trim())) {
|
|
1422
1835
|
const citations = await findInvalidSourceCitations({
|
|
1423
1836
|
checkouts: await stateManager.listRepoCheckouts(),
|
|
@@ -1446,11 +1859,134 @@ export function registerSubmitReportTool(server) {
|
|
|
1446
1859
|
const assertionCheckouts = await stateManager
|
|
1447
1860
|
.listRepoCheckouts()
|
|
1448
1861
|
.catch(() => []);
|
|
1862
|
+
// Declines an earlier accepted submission removed from the delivered files.
|
|
1863
|
+
// They are handed to the re-derivation rather than written back into the files:
|
|
1864
|
+
// the run still knows what it declined, and the customer's copy is edited once.
|
|
1865
|
+
//
|
|
1866
|
+
// Keyed by the SPEC that owns the file the decline came from. Handing every
|
|
1867
|
+
// row every decline let one about one spec's helper clear a live finding in
|
|
1868
|
+
// another spec that defines a helper of the same name.
|
|
1869
|
+
const changedSet = changedFilesAbs ? new Set(changedFilesAbs) : undefined;
|
|
1870
|
+
const declinesBySpec = new Map();
|
|
1871
|
+
/** Files whose declines some record can still reach. One no record owns can
|
|
1872
|
+
* never be consumed by anything, so it is retired without a measurement; one
|
|
1873
|
+
* that IS owned is retired only on a measurement that found no fault. */
|
|
1874
|
+
const reachableDeclineFiles = new Set();
|
|
1875
|
+
for (const [name, record] of Object.entries(stateData.reuseOutcomes ?? {})) {
|
|
1876
|
+
const mine = [];
|
|
1877
|
+
// Every file the record owns, NOT only the ones this run changed. The filter
|
|
1878
|
+
// decides what the strip may EDIT; a decline already recorded against a file
|
|
1879
|
+
// is this run's whatever the working tree says, and dropping it makes the
|
|
1880
|
+
// gate refuse a fault the run already declined.
|
|
1881
|
+
for (const file of await deliveredFilesOf(record ?? {}, undefined)) {
|
|
1882
|
+
if ((stateData.strippedDeclines?.[file] ?? []).length > 0)
|
|
1883
|
+
reachableDeclineFiles.add(file);
|
|
1884
|
+
mine.push(...(stateData.strippedDeclines?.[file] ?? []).map(
|
|
1885
|
+
// Tagged with where it was read from, so a consumption retires THAT
|
|
1886
|
+
// file's record and not an identical one stored for another spec.
|
|
1887
|
+
(d) => ({ ...d, file })));
|
|
1888
|
+
}
|
|
1889
|
+
if (mine.length > 0)
|
|
1890
|
+
declinesBySpec.set(name, mine);
|
|
1891
|
+
}
|
|
1449
1892
|
const reuseBlocking = [];
|
|
1893
|
+
/** Carried declines a finding consumed on this call. */
|
|
1894
|
+
const consumedDeclines = [];
|
|
1895
|
+
/** Files whose stored declines a re-derivation actually measured on this call. */
|
|
1896
|
+
const evaluatedDeclineFiles = new Set();
|
|
1450
1897
|
const sanitizedNewTests = await Promise.all(dedupedNewTests.map(async ({ scenarioFile: _sf, traceFile: _tf, frontendTrace: _ft, ...rest }) => {
|
|
1451
|
-
const row = await attachReuseOutcome(
|
|
1898
|
+
const row = await attachReuseOutcome(rest, stateData.reuseOutcomes, stateData.reuseHandOffs, retrofitViews, reuseBlocking, declinesBySpec.get(path.basename(rest.fileName)) ?? [], consumedDeclines, evaluatedDeclineFiles, undefined, generatedPathFor(rest.fileName, stateData.generatedTests ?? {}));
|
|
1452
1899
|
return attachAssertionOutcome(row, stateData.assertionOutcomes ?? {}, assertionCheckouts);
|
|
1453
1900
|
}));
|
|
1901
|
+
const maintenanceRows = testMaintenance
|
|
1902
|
+
? await Promise.all(testMaintenance.map(async ({ testFilePath, pomFile, reportOnly, ...row }) => {
|
|
1903
|
+
// `pomFile` is the file skyramp_actions told the agent to edit;
|
|
1904
|
+
// `testFilePath` stays the spec, which is what ran. Name both when
|
|
1905
|
+
// they differ, or a page-object edit is reported against the spec.
|
|
1906
|
+
// Maintenance rows still carry the absolute path here, so the
|
|
1907
|
+
// assertion summary uses an EXACT canonical-path lookup — no
|
|
1908
|
+
// basename ambiguity. This is what carries the maintenance
|
|
1909
|
+
// honesty labels (nothing-to-verify vs verified) into the
|
|
1910
|
+
// report instead of leaving them as tool text the agent can
|
|
1911
|
+
// paraphrase.
|
|
1912
|
+
const record = stateData.assertionOutcomes?.[canonicalTestPath(testFilePath)];
|
|
1913
|
+
const assertions = record
|
|
1914
|
+
? await rederiveAssertionOutcome(record)
|
|
1915
|
+
: undefined;
|
|
1916
|
+
// A maintained test goes through modularize → reuse like a
|
|
1917
|
+
// generated one, so its row carries the same server-derived
|
|
1918
|
+
// reuse outcome, matched by its exact path. Wraps the
|
|
1919
|
+
// repository-attributed row, the same composition
|
|
1920
|
+
// newTestsCreated uses.
|
|
1921
|
+
return attachReuseOutcome({
|
|
1922
|
+
...row,
|
|
1923
|
+
fileName: path.basename(testFilePath),
|
|
1924
|
+
...(pomFile &&
|
|
1925
|
+
path.basename(pomFile) !== path.basename(testFilePath)
|
|
1926
|
+
? { editedFileName: path.basename(pomFile) }
|
|
1927
|
+
: {}),
|
|
1928
|
+
...(assertions ? { assertions } : {}),
|
|
1929
|
+
}, stateData.reuseOutcomes, stateData.reuseHandOffs, retrofitViews, reuseBlocking, declinesBySpec.get(path.basename(testFilePath)) ?? [], consumedDeclines, evaluatedDeclineFiles, testFilePath,
|
|
1930
|
+
// Only where the run edited the file and left a test standing.
|
|
1931
|
+
// VERIFY and IGNORE read a repository-owned test and changed nothing,
|
|
1932
|
+
// and DELETE removed it — none of the three has reuse to state, so a
|
|
1933
|
+
// sentence about what they import is noise on a complete row. A
|
|
1934
|
+
// report-only row keeps a real REGENERATE or DELETE while this run
|
|
1935
|
+
// touched nothing, so the action alone does not answer it; the same
|
|
1936
|
+
// pair of conditions `rowsLeavingCoverage` applies.
|
|
1937
|
+
MAINTENANCE_COVERAGE_ACTIONS.has(row.action) &&
|
|
1938
|
+
reportOnly !== true
|
|
1939
|
+
? testFilePath
|
|
1940
|
+
: undefined);
|
|
1941
|
+
}))
|
|
1942
|
+
: undefined;
|
|
1943
|
+
// Retirement happens HERE: after BOTH row kinds have re-derived, and still
|
|
1944
|
+
// before any refusal can return. Running it between them left a decline owned
|
|
1945
|
+
// only by a maintained spec unmeasured at this point — retired on that silence
|
|
1946
|
+
// before the fix below, and then kept forever once silence stopped retiring. A decline whose fault
|
|
1947
|
+
// the agent repaired has nothing left to authorise, and a submission that is
|
|
1948
|
+
// then refused for an UNRELATED fault used to leave it in place — so while
|
|
1949
|
+
// repairing that one the agent could reintroduce the first and the next
|
|
1950
|
+
// submission would authorise it in silence. Only the physical strip has to wait
|
|
1951
|
+
// for the accepted path; the record does not.
|
|
1952
|
+
//
|
|
1953
|
+
// Reconciled whenever any decline is stored, not when a count says it changed:
|
|
1954
|
+
// `consumedDeclines` accumulates per report ROW, so two rows consuming one
|
|
1955
|
+
// decline made the totals match while a second, stale decline survived.
|
|
1956
|
+
if (Object.keys(stateData.strippedDeclines ?? {}).length > 0) {
|
|
1957
|
+
const budget = [...consumedDeclines];
|
|
1958
|
+
const kept = Object.fromEntries(Object.entries(stateData.strippedDeclines ?? {}).map(([file, stored]) => [
|
|
1959
|
+
file,
|
|
1960
|
+
// Retire on an ANSWER, not on a silence. A file a record still owns,
|
|
1961
|
+
// whose re-derivation did not measure it — no row for that spec on this
|
|
1962
|
+
// call, a record with no spec identity, a verifier that threw —
|
|
1963
|
+
// consumed nothing for want of a reading, not for want of a fault, and
|
|
1964
|
+
// deleting it there drops permission while the fault is still in the
|
|
1965
|
+
// file. A file no record owns is retired without a measurement: nothing
|
|
1966
|
+
// can consume it, so no reading would ever come.
|
|
1967
|
+
reachableDeclineFiles.has(file) &&
|
|
1968
|
+
!evaluatedDeclineFiles.has(file)
|
|
1969
|
+
? stored
|
|
1970
|
+
: stored.filter((d) => {
|
|
1971
|
+
const i = budget.findIndex((c) => c.file === file &&
|
|
1972
|
+
c.kind === d.kind &&
|
|
1973
|
+
c.helper === d.helper &&
|
|
1974
|
+
c.keptInline === d.keptInline);
|
|
1975
|
+
if (i === -1)
|
|
1976
|
+
return false;
|
|
1977
|
+
budget.splice(i, 1);
|
|
1978
|
+
return true;
|
|
1979
|
+
}),
|
|
1980
|
+
]));
|
|
1981
|
+
try {
|
|
1982
|
+
const latest = (await stateManager.readData()) ?? stateData;
|
|
1983
|
+
await stateManager.appendData({ ...latest, strippedDeclines: kept });
|
|
1984
|
+
stateData.strippedDeclines = kept;
|
|
1985
|
+
}
|
|
1986
|
+
catch (err) {
|
|
1987
|
+
logger.warning("Could not retire the declines this call did not consume — one may authorise a later fault", { error: String(err) });
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1454
1990
|
// A blocking reuse verdict on the delivered files refuses the report. The
|
|
1455
1991
|
// live verify pass is a one-time checkpoint on a file that keeps changing —
|
|
1456
1992
|
// the execution fix loop and a post-verification rewrite both edit after it —
|
|
@@ -1469,6 +2005,10 @@ export function registerSubmitReportTool(server) {
|
|
|
1469
2005
|
// off than one with a disclosed fault (see REUSE_SUBMIT_MAX_REFUSALS). The
|
|
1470
2006
|
// count is persisted per spec path; a count that cannot be persisted cannot
|
|
1471
2007
|
// bound anything, so a failed write accepts rather than refuses forever.
|
|
2008
|
+
// Built BEFORE the blocking-verdict refusal below, not inside the report
|
|
2009
|
+
// object: a maintained test goes through the same reuse chain as a generated
|
|
2010
|
+
// one, so its delivered files can fail the same check, and a verdict collected
|
|
2011
|
+
// after the refusal would never refuse anything.
|
|
1472
2012
|
// `reuseOutcomes` is keyed by basename, so two rows sharing a file name find
|
|
1473
2013
|
// one record and push one verdict twice — deduplicated by path here, as
|
|
1474
2014
|
// pendingReuseVerification skips that case on its side. Sorted by file so the
|
|
@@ -1484,7 +2024,10 @@ export function registerSubmitReportTool(server) {
|
|
|
1484
2024
|
for (const v of owed)
|
|
1485
2025
|
refusals[v.file] = (refusals[v.file] ?? 0) + 1;
|
|
1486
2026
|
try {
|
|
1487
|
-
await stateManager.
|
|
2027
|
+
await stateManager.appendData({
|
|
2028
|
+
...stateData,
|
|
2029
|
+
reuseRefusals: refusals,
|
|
2030
|
+
});
|
|
1488
2031
|
}
|
|
1489
2032
|
catch (err) {
|
|
1490
2033
|
persisted = false;
|
|
@@ -1532,6 +2075,100 @@ export function registerSubmitReportTool(server) {
|
|
|
1532
2075
|
test.expectedToFail = true;
|
|
1533
2076
|
}
|
|
1534
2077
|
}
|
|
2078
|
+
// Every file this run wrote, from the server's own records — one canonical
|
|
2079
|
+
// absolute path each, which is also the key an execution is recorded under.
|
|
2080
|
+
// Generated files come from the generators; edited files from the maintenance
|
|
2081
|
+
// triage, narrowed by `rowsLeavingCoverage` to UPDATE and REGENERATE. The rest
|
|
2082
|
+
// cannot be run: DELETE removed the file, VERIFY and IGNORE never edited one,
|
|
2083
|
+
// and a `reportOnly` row only advised a change this run did not make.
|
|
2084
|
+
//
|
|
2085
|
+
// A verdict names the SPEC, never `pomFile`: a POM-backed row edits the page
|
|
2086
|
+
// object, but the execution is recorded against the spec.
|
|
2087
|
+
// A file a DELETE verdict names is gone by report time, so no run is owed for
|
|
2088
|
+
// it. The verdicts keep every recommendation, not the winning one, so one file
|
|
2089
|
+
// can carry both a DELETE and an UPDATE — and the DELETE is what was applied.
|
|
2090
|
+
const deletedPaths = new Set(maintenanceSections.flatMap((section) => (section.data.maintenanceVerdicts ?? [])
|
|
2091
|
+
.filter((verdict) => verdict?.action === DriftAction.Delete &&
|
|
2092
|
+
verdict?.reportOnly !== true)
|
|
2093
|
+
.map((verdict) => canonicalTestPath(verdict.testFilePath))));
|
|
2094
|
+
const filesWritten = new Map();
|
|
2095
|
+
// When each generated path was last written, so a run recorded before that
|
|
2096
|
+
// write is not credited for the bytes that replaced it.
|
|
2097
|
+
const generatedAt = new Map();
|
|
2098
|
+
for (const section of maintenanceSections) {
|
|
2099
|
+
for (const [generated, record] of Object.entries(section.data.generatedTests ?? {}))
|
|
2100
|
+
if (!deletedPaths.has(generated)) {
|
|
2101
|
+
filesWritten.set(generated, "generated");
|
|
2102
|
+
if (typeof record?.generatedAt === "number")
|
|
2103
|
+
generatedAt.set(generated, record.generatedAt);
|
|
2104
|
+
}
|
|
2105
|
+
for (const verdict of rowsLeavingCoverage(section.data.maintenanceVerdicts)) {
|
|
2106
|
+
// A rename UPDATE moves the spec before it is executed, so the run is
|
|
2107
|
+
// recorded under the new name while `testFilePath` keeps the old one.
|
|
2108
|
+
const raw = verdict._suggestedNewFile?.trim() || verdict.testFilePath.trim();
|
|
2109
|
+
if (!raw)
|
|
2110
|
+
continue;
|
|
2111
|
+
// Canonical because that is how an execution is keyed: a checkout under a
|
|
2112
|
+
// symlinked temp or home directory spells one file two ways.
|
|
2113
|
+
const key = canonicalTestPath(raw);
|
|
2114
|
+
if (!filesWritten.has(key) && !deletedPaths.has(key))
|
|
2115
|
+
filesWritten.set(key, "updated");
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
// The LATEST run of each file, because a re-run replaces the record. An edited
|
|
2119
|
+
// file needs a run of the edit, so its pre-edit baseline does not answer for
|
|
2120
|
+
// it — `phase` is what tells the two apart. A generated file has no baseline,
|
|
2121
|
+
// so any run of it is a run of what was written.
|
|
2122
|
+
const executions = new Map(maintenanceSections.flatMap((section) => Object.entries(section.data.testExecutions ?? {}).map(([file, record]) => [
|
|
2123
|
+
file,
|
|
2124
|
+
{
|
|
2125
|
+
phase: String(record?.phase ?? ""),
|
|
2126
|
+
executedAt: String(record?.executedAt ?? ""),
|
|
2127
|
+
},
|
|
2128
|
+
])));
|
|
2129
|
+
// A record is never removed once written, so a file the agent generated and
|
|
2130
|
+
// then deleted would object forever — and name a path that is not there. What
|
|
2131
|
+
// is gone owes no run. Unreadable counts as gone: a false objection about a
|
|
2132
|
+
// file nobody can open is worse than the silence.
|
|
2133
|
+
const written = [];
|
|
2134
|
+
for (const [file, how] of filesWritten) {
|
|
2135
|
+
if (!(await pathExists(file)))
|
|
2136
|
+
continue;
|
|
2137
|
+
const run = executions.get(file);
|
|
2138
|
+
written.push({
|
|
2139
|
+
file,
|
|
2140
|
+
how,
|
|
2141
|
+
recordedPhase: run?.phase,
|
|
2142
|
+
executedAt: run?.executedAt,
|
|
2143
|
+
generatedAt: generatedAt.get(file),
|
|
2144
|
+
});
|
|
2145
|
+
}
|
|
2146
|
+
// The counts a reader compares at a glance, from the same two sets the check
|
|
2147
|
+
// below joins — so a shortfall here is exactly the files it names.
|
|
2148
|
+
const executionCoverage = {
|
|
2149
|
+
generated: written.filter((f) => f.how === "generated").length,
|
|
2150
|
+
updated: written.filter((f) => f.how === "updated").length,
|
|
2151
|
+
executed: written.filter(runOfWhatWasWritten).length,
|
|
2152
|
+
};
|
|
2153
|
+
// A skipped test is not an execution. Refuse before writing a report so the
|
|
2154
|
+
// agent must execute every delivered plan test instead of declaring a time
|
|
2155
|
+
// constraint in a Skipped row and ending the run.
|
|
2156
|
+
const registeredPlanTestIds = new Set(Array.isArray(stateData.plan?.plannedTests)
|
|
2157
|
+
? stateData.plan.plannedTests
|
|
2158
|
+
.map((test) => test?.plannedTestId?.trim())
|
|
2159
|
+
.filter((id) => !!id)
|
|
2160
|
+
: []);
|
|
2161
|
+
const skippedTests = isPlanOnlyMode()
|
|
2162
|
+
? []
|
|
2163
|
+
: skippedDeliveredTests(dedupedNewTests.filter((test) => registeredPlanTestIds.has((test.plannedTestId ?? "").trim())), params.testResults);
|
|
2164
|
+
if (skippedTests.length > 0) {
|
|
2165
|
+
errorResult = toolError(`Cannot submit the report: ${skippedTests.length} registered plan test${skippedTests.length === 1 ? "" : "s"} ${skippedTests
|
|
2166
|
+
.map((test) => `${test.plannedTestId?.trim() || test.testId}`)
|
|
2167
|
+
.join(", ")} ${skippedTests.length === 1 ? "has" : "have"} no non-skipped execution result. ` +
|
|
2168
|
+
"Do not skip because you believe you are low on context or time, or because you speculate that another attempt will not help. " +
|
|
2169
|
+
"Run each remaining test with skyramp_execute_test, include its Pass or Fail result in testResults, then call skyramp_submit_report again.");
|
|
2170
|
+
return errorResult;
|
|
2171
|
+
}
|
|
1535
2172
|
// Report-time checks on what the customer will read. They need no plan, so a
|
|
1536
2173
|
// run without one still gets them.
|
|
1537
2174
|
const reportChecks = [
|
|
@@ -1541,6 +2178,15 @@ export function registerSubmitReportTool(server) {
|
|
|
1541
2178
|
...(isPlanOnlyMode()
|
|
1542
2179
|
? []
|
|
1543
2180
|
: runPostExecutionCheck("deliveredTestRan", "deliveredTestRan", () => checkEveryDeliveredTestRan(dedupedNewTests, params.testResults))),
|
|
2181
|
+
// Every lane, not just testbot: both records are written whenever the
|
|
2182
|
+
// tools run, and a file written and never run is the same defect
|
|
2183
|
+
// wherever it happens. Silent when the run recorded nothing — an older
|
|
2184
|
+
// state file has no `generatedTests`, and the check must not read that
|
|
2185
|
+
// as "nothing was run". Skipped in the plan-only lane, which has no
|
|
2186
|
+
// application to run anything against.
|
|
2187
|
+
...(isPlanOnlyMode()
|
|
2188
|
+
? []
|
|
2189
|
+
: runPostExecutionCheck("testRanOnThisServer", "testRanOnThisServer", () => checkEveryWrittenTestRan(written))),
|
|
1544
2190
|
];
|
|
1545
2191
|
// Run against an EMPTY plan when none was submitted, never skipped: the gate
|
|
1546
2192
|
// above covers contract and integration only. An empty plan raises exactly
|
|
@@ -1548,7 +2194,14 @@ export function registerSubmitReportTool(server) {
|
|
|
1548
2194
|
let objections;
|
|
1549
2195
|
let changeTable;
|
|
1550
2196
|
{
|
|
1551
|
-
const postExecution = runPostExecutionChecks(stateData.plan ??
|
|
2197
|
+
const postExecution = runPostExecutionChecks(stateData.plan ??
|
|
2198
|
+
{
|
|
2199
|
+
plannedTests: [],
|
|
2200
|
+
registrationNumber: 0,
|
|
2201
|
+
answers: [],
|
|
2202
|
+
openObjections: [],
|
|
2203
|
+
answeredObjections: [],
|
|
2204
|
+
}, dedupedNewTests, params.testResults, params.issuesFound ?? [], isPlanOnlyMode(),
|
|
1552
2205
|
// The triage's own verdicts, not this tool's input, so the join is against
|
|
1553
2206
|
// what the run is recorded as having edited — and only the rows that left a
|
|
1554
2207
|
// test standing and were actually applied, so a no-op assessment, a deletion
|
|
@@ -1610,7 +2263,10 @@ export function registerSubmitReportTool(server) {
|
|
|
1610
2263
|
// objection and answers it in the same call: fixing it stops the check
|
|
1611
2264
|
// raising it, so matching against this call alone called the answer unknown
|
|
1612
2265
|
// and objected to the agent for doing both (run 33946409570, sb19).
|
|
1613
|
-
const answerable = [
|
|
2266
|
+
const answerable = [
|
|
2267
|
+
...raised,
|
|
2268
|
+
...previouslyRaised.map((entry) => entry.objection),
|
|
2269
|
+
];
|
|
1614
2270
|
const reportStageObjections = [
|
|
1615
2271
|
...raised,
|
|
1616
2272
|
...unknownAnswerObjections(answerable, acknowledged, {
|
|
@@ -1623,24 +2279,31 @@ export function registerSubmitReportTool(server) {
|
|
|
1623
2279
|
// their silence is not evidence: only a call carrying results can resolve an
|
|
1624
2280
|
// earlier objection.
|
|
1625
2281
|
const judgedThisCall = (params.testResults ?? []).length > 0;
|
|
2282
|
+
// These verifiers read the server's own records, not `testResults`, so
|
|
2283
|
+
// they reach a verdict on every call. An empty resubmission leaves them
|
|
2284
|
+
// judged, and an objection they no longer raise is resolved — without
|
|
2285
|
+
// this it would vanish from the report and sit in `raisedObjections`
|
|
2286
|
+
// for the rest of the run with nothing able to clear it.
|
|
2287
|
+
const stateBackedVerifiers = new Set(["testRanOnThisServer"]);
|
|
2288
|
+
const judged = (objection) => judgedThisCall || stateBackedVerifiers.has(objection.verifier);
|
|
1626
2289
|
// Resolved = raised earlier, absent from a call that could have raised it.
|
|
1627
2290
|
// One that comes back drops out: publishing it as both says two things.
|
|
1628
2291
|
const resolvedObjections = [
|
|
1629
2292
|
...previouslyResolved,
|
|
1630
|
-
...
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
: []),
|
|
2293
|
+
...previouslyRaised
|
|
2294
|
+
.filter((entry) => judged(entry.objection) &&
|
|
2295
|
+
!raisedNow.has(answerKey(entry.objection)))
|
|
2296
|
+
.map((entry) => ({
|
|
2297
|
+
objection: entry.objection,
|
|
2298
|
+
resolvedBy: `submission ${submissionNumber}`,
|
|
2299
|
+
})),
|
|
1638
2300
|
].filter((entry, index, all) => !raisedNow.has(answerKey(entry.objection)) &&
|
|
1639
2301
|
all.findIndex((other) => answerKey(other.objection) === answerKey(entry.objection)) === index);
|
|
1640
2302
|
const answeredReportObjections = [];
|
|
1641
2303
|
const openReportObjections = [];
|
|
1642
2304
|
for (const objection of reportStageObjections) {
|
|
1643
|
-
const answer = answerFor(objection, acknowledged) ??
|
|
2305
|
+
const answer = answerFor(objection, acknowledged) ??
|
|
2306
|
+
carriedByKey.get(answerKey(objection))?.answer;
|
|
1644
2307
|
if (answer)
|
|
1645
2308
|
answeredReportObjections.push({ objection, answer });
|
|
1646
2309
|
else
|
|
@@ -1715,14 +2378,19 @@ export function registerSubmitReportTool(server) {
|
|
|
1715
2378
|
// corroborate a claim that an existing test covers the change.
|
|
1716
2379
|
const editedPaths = [];
|
|
1717
2380
|
for (const row of rowsLeavingCoverage(stateData.maintenanceVerdicts)) {
|
|
1718
|
-
for (const full of [
|
|
2381
|
+
for (const full of [
|
|
2382
|
+
row?.testFilePath,
|
|
2383
|
+
row?.pomFile,
|
|
2384
|
+
]) {
|
|
1719
2385
|
const path = typeof full === "string" ? full.trim() : "";
|
|
1720
2386
|
if (path)
|
|
1721
2387
|
editedPaths.push(path);
|
|
1722
2388
|
}
|
|
1723
2389
|
}
|
|
1724
2390
|
const maintainedByChange = new Map();
|
|
1725
|
-
for (const entry of Array.isArray(stateData.plan?.maintains)
|
|
2391
|
+
for (const entry of Array.isArray(stateData.plan?.maintains)
|
|
2392
|
+
? stateData.plan.maintains
|
|
2393
|
+
: []) {
|
|
1726
2394
|
const file = String(entry?.file ?? "").trim();
|
|
1727
2395
|
if (!file || !Array.isArray(entry?.changes))
|
|
1728
2396
|
continue;
|
|
@@ -1742,7 +2410,9 @@ export function registerSubmitReportTool(server) {
|
|
|
1742
2410
|
for (const objection of objections) {
|
|
1743
2411
|
if (!objection.objectionId.startsWith(CHANGE_OBJECTION_PREFIX))
|
|
1744
2412
|
continue;
|
|
1745
|
-
const answer = typeof objection.answer === "string"
|
|
2413
|
+
const answer = typeof objection.answer === "string"
|
|
2414
|
+
? objection.answer.trim()
|
|
2415
|
+
: "";
|
|
1746
2416
|
if (answer)
|
|
1747
2417
|
answerByChange.set(objection.objectionId.slice(CHANGE_OBJECTION_PREFIX.length), answer);
|
|
1748
2418
|
}
|
|
@@ -1750,7 +2420,8 @@ export function registerSubmitReportTool(server) {
|
|
|
1750
2420
|
const id = String(change?.id ?? "").trim();
|
|
1751
2421
|
const testedBy = dedupedNewTests
|
|
1752
2422
|
.map((test) => (test.plannedTestId ?? "").trim())
|
|
1753
|
-
.filter((plannedTestId) => plannedTestId &&
|
|
2423
|
+
.filter((plannedTestId) => plannedTestId &&
|
|
2424
|
+
(changesByCandidate.get(plannedTestId) ?? []).includes(id));
|
|
1754
2425
|
const answer = answerByChange.get(id);
|
|
1755
2426
|
const maintainedBy = maintainedByChange.get(id) ?? [];
|
|
1756
2427
|
return {
|
|
@@ -1771,7 +2442,7 @@ export function registerSubmitReportTool(server) {
|
|
|
1771
2442
|
// The report is the deliverable, so a failed state write must not cost
|
|
1772
2443
|
// it; the answer is simply repeated on the next call.
|
|
1773
2444
|
try {
|
|
1774
|
-
await stateManager.
|
|
2445
|
+
await stateManager.appendData({
|
|
1775
2446
|
...stateData,
|
|
1776
2447
|
reportObjections: {
|
|
1777
2448
|
answeredObjections: [
|
|
@@ -1785,9 +2456,8 @@ export function registerSubmitReportTool(server) {
|
|
|
1785
2456
|
objection,
|
|
1786
2457
|
submission: firstRaisedAt.get(answerKey(objection)) ?? submissionNumber,
|
|
1787
2458
|
})),
|
|
1788
|
-
...(
|
|
1789
|
-
|
|
1790
|
-
: previouslyRaised.filter((entry) => !raisedNow.has(answerKey(entry.objection)))),
|
|
2459
|
+
...previouslyRaised.filter((entry) => !judged(entry.objection) &&
|
|
2460
|
+
!raisedNow.has(answerKey(entry.objection))),
|
|
1791
2461
|
],
|
|
1792
2462
|
resolvedObjections,
|
|
1793
2463
|
submissionCount: submissionNumber,
|
|
@@ -1801,44 +2471,20 @@ export function registerSubmitReportTool(server) {
|
|
|
1801
2471
|
const report = {
|
|
1802
2472
|
businessCaseAnalysis: params.businessCaseAnalysis,
|
|
1803
2473
|
newTestsCreated: sanitizedNewTests,
|
|
1804
|
-
additionalRecommendations: dedupedRecommendations
|
|
2474
|
+
additionalRecommendations: dedupedRecommendations,
|
|
1805
2475
|
// Report wire format keeps main's original `fileName` (basename) — testFilePath is
|
|
1806
2476
|
// an internal-only field, needed for matching but never meant to reach the report.
|
|
1807
|
-
testMaintenance:
|
|
1808
|
-
? await Promise.all(testMaintenance.map(async ({ testFilePath, pomFile, ...row }) => {
|
|
1809
|
-
// `pomFile` is the file skyramp_actions told the agent to edit;
|
|
1810
|
-
// `testFilePath` stays the spec, which is what ran. Name both when
|
|
1811
|
-
// they differ, or a page-object edit is reported against the spec.
|
|
1812
|
-
// Maintenance rows still carry the absolute path here, so the
|
|
1813
|
-
// assertion summary uses an EXACT canonical-path lookup — no
|
|
1814
|
-
// basename ambiguity. This is what carries the maintenance
|
|
1815
|
-
// honesty labels (nothing-to-verify vs verified) into the
|
|
1816
|
-
// report instead of leaving them as tool text the agent can
|
|
1817
|
-
// paraphrase.
|
|
1818
|
-
const record = stateData.assertionOutcomes?.[canonicalTestPath(testFilePath)];
|
|
1819
|
-
const assertions = record
|
|
1820
|
-
? await rederiveAssertionOutcome(record)
|
|
1821
|
-
: undefined;
|
|
1822
|
-
return normalizeRepository({
|
|
1823
|
-
...row,
|
|
1824
|
-
fileName: path.basename(testFilePath),
|
|
1825
|
-
...(pomFile &&
|
|
1826
|
-
path.basename(pomFile) !== path.basename(testFilePath)
|
|
1827
|
-
? { editedFileName: path.basename(pomFile) }
|
|
1828
|
-
: {}),
|
|
1829
|
-
...(assertions ? { assertions } : {}),
|
|
1830
|
-
});
|
|
1831
|
-
}))
|
|
1832
|
-
: undefined,
|
|
2477
|
+
testMaintenance: maintenanceRows,
|
|
1833
2478
|
// videoPath is filled from the run's execution records; testFilePath is the
|
|
1834
2479
|
// match-only key and is stripped from the wire format, the same line drawn for
|
|
1835
2480
|
// testMaintenance's own testFilePath above (downstream scoring scripts traverse
|
|
1836
2481
|
// these objects and don't expect it).
|
|
1837
2482
|
testResults: params.testResults.map((row) => {
|
|
1838
|
-
const { testFilePath: _tfp, ...wire } = attachVideoPath(
|
|
2483
|
+
const { testFilePath: _tfp, ...wire } = attachVideoPath(row, stateData.executionVideos);
|
|
1839
2484
|
return wire;
|
|
1840
2485
|
}),
|
|
1841
2486
|
issuesFound,
|
|
2487
|
+
executionCoverage,
|
|
1842
2488
|
nextSteps: params.nextSteps ?? [],
|
|
1843
2489
|
...(changeTable ? { changes: changeTable } : {}),
|
|
1844
2490
|
...(objections ? { objections } : {}),
|
|
@@ -1861,11 +2507,64 @@ export function registerSubmitReportTool(server) {
|
|
|
1861
2507
|
outputFile: reportPath,
|
|
1862
2508
|
elapsedMs: elapsed,
|
|
1863
2509
|
});
|
|
2510
|
+
// The report is on disk, so this call is past every refusal in the handler
|
|
2511
|
+
// — including the one a failed write above would have raised. See
|
|
2512
|
+
// stripDeliveredGateMarkers for why the accepted path is the only one that
|
|
2513
|
+
// may remove a decline.
|
|
2514
|
+
const stripped = await stripDeliveredGateMarkers(stateData.reuseOutcomes, retrofitViews, changedSet);
|
|
2515
|
+
// Only ADDS here. Retirement already happened above, before any refusal
|
|
2516
|
+
// could return, so this call has nothing left to draw down.
|
|
2517
|
+
if (Object.keys(stripped).length > 0) {
|
|
2518
|
+
// Recorded so a later call in this run can read them back — the removal is
|
|
2519
|
+
// for the customer, not for our own gates. A record that cannot be
|
|
2520
|
+
// persisted would make that call refuse a declined fault, so the failure
|
|
2521
|
+
// is logged where the next refusal can be explained by it.
|
|
2522
|
+
try {
|
|
2523
|
+
// Re-read rather than spread the `stateData` this call opened with. The
|
|
2524
|
+
// objections block above has already persisted its own record, and
|
|
2525
|
+
// carryForwardRootFields carries `reportObjections` only when the
|
|
2526
|
+
// incoming value is undefined — so writing the call-start copy back would
|
|
2527
|
+
// revert the submission count and re-raise answers this call resolved.
|
|
2528
|
+
const latest = (await stateManager.readData()) ?? stateData;
|
|
2529
|
+
await stateManager.appendData({
|
|
2530
|
+
...latest,
|
|
2531
|
+
// Merged per file, never replaced: a later submission that strips a
|
|
2532
|
+
// second marker from one file must not forget the first, which is
|
|
2533
|
+
// still a decline this run made.
|
|
2534
|
+
//
|
|
2535
|
+
// A decline this call did NOT consume is retired. The fault it covered
|
|
2536
|
+
// is gone, and permission that covers no fault authorises the next one
|
|
2537
|
+
// instead — which nobody declined. A marker left in a file retires the
|
|
2538
|
+
// same way, through `stale-allow`; this is that rule for the records.
|
|
2539
|
+
strippedDeclines: Object.fromEntries([
|
|
2540
|
+
...new Set([
|
|
2541
|
+
...Object.keys(latest.strippedDeclines ?? {}),
|
|
2542
|
+
...Object.keys(stripped),
|
|
2543
|
+
]),
|
|
2544
|
+
].map((file) => [
|
|
2545
|
+
file,
|
|
2546
|
+
[
|
|
2547
|
+
...(latest.strippedDeclines?.[file] ?? []),
|
|
2548
|
+
...(stripped[file] ?? []),
|
|
2549
|
+
].map((d) => {
|
|
2550
|
+
// The `file` tag is a runtime aid for retirement, not part of
|
|
2551
|
+
// the record: the record is already keyed by file.
|
|
2552
|
+
const { file: _tag, ...rest } = d;
|
|
2553
|
+
return rest;
|
|
2554
|
+
}),
|
|
2555
|
+
])),
|
|
2556
|
+
});
|
|
2557
|
+
}
|
|
2558
|
+
catch (err) {
|
|
2559
|
+
logger.warning("Could not record the declines removed from the delivered files — a further submission may refuse one of them", { error: String(err) });
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
1864
2562
|
// Addressed to the AGENT in the second person, each naming a next move: the
|
|
1865
2563
|
// report is already written when this returns and the tool can be called
|
|
1866
2564
|
// again, so the instruction is actionable. An answered objection is
|
|
1867
2565
|
// published but not listed back, which would read as a second demand.
|
|
1868
|
-
const open = objections?.filter((objection) => objection.stage === "post-execution" &&
|
|
2566
|
+
const open = objections?.filter((objection) => objection.stage === "post-execution" &&
|
|
2567
|
+
objection.answer === undefined) ?? [];
|
|
1869
2568
|
return {
|
|
1870
2569
|
content: [
|
|
1871
2570
|
{
|