@skyramp/mcp 0.4.2 → 0.4.3-rc.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (135) hide show
  1. package/build/execution/wrapperConfig.d.ts +4 -0
  2. package/build/execution/wrapperConfig.js +14 -2
  3. package/build/prompts/code-reuse.d.ts +5 -1
  4. package/build/prompts/code-reuse.js +16 -10
  5. package/build/prompts/modularization/ui-test-modularization.js +9 -2
  6. package/build/prompts/pom-aware-code-reuse.d.ts +4 -1
  7. package/build/prompts/pom-aware-code-reuse.js +38 -6
  8. package/build/prompts/reuse-hand-off.d.ts +4 -0
  9. package/build/prompts/reuse-hand-off.js +10 -5
  10. package/build/prompts/shared-helper-policy.d.ts +5 -0
  11. package/build/prompts/shared-helper-policy.js +5 -0
  12. package/build/prompts/test-maintenance/driftAnalysisShared.js +2 -0
  13. package/build/prompts/test-maintenance/uiDriftAnalysisSections.js +2 -1
  14. package/build/prompts/testbot/testbot-prompts.js +33 -16
  15. package/build/recommendation/answers.d.ts +5 -3
  16. package/build/recommendation/answers.js +5 -2
  17. package/build/recommendation/fakeVerifyContext.d.ts +3 -0
  18. package/build/recommendation/fakeVerifyContext.js +16 -0
  19. package/build/recommendation/runVerifiers.js +2 -0
  20. package/build/recommendation/types.d.ts +12 -0
  21. package/build/recommendation/verifierContracts.d.ts +32 -2
  22. package/build/recommendation/verifierContracts.js +45 -4
  23. package/build/recommendation/verifiers/changedLinesClaimed.d.ts +17 -0
  24. package/build/recommendation/verifiers/changedLinesClaimed.js +88 -0
  25. package/build/recommendation/verifiers/coverage.js +143 -4
  26. package/build/recommendation/verifiers/expectedValueSourced.js +8 -0
  27. package/build/recommendation/verifiers/requirementSourced.js +141 -11
  28. package/build/services/TestGenerationService.js +4 -0
  29. package/build/tools/code-refactor/caller-gate.d.ts +105 -0
  30. package/build/tools/code-refactor/caller-gate.js +174 -0
  31. package/build/tools/code-refactor/changed-helper-report.d.ts +62 -0
  32. package/build/tools/code-refactor/changed-helper-report.js +114 -0
  33. package/build/tools/code-refactor/codeReuseTool.js +131 -62
  34. package/build/tools/code-refactor/enhanceAssertionsTool.js +2 -2
  35. package/build/tools/code-refactor/helper-callers.d.ts +65 -0
  36. package/build/tools/code-refactor/helper-callers.js +85 -0
  37. package/build/tools/code-refactor/modularizationTool.js +10 -0
  38. package/build/tools/code-refactor/pom-pass-state.d.ts +51 -0
  39. package/build/tools/code-refactor/pom-pass-state.js +115 -0
  40. package/build/tools/code-refactor/removed-modules.d.ts +33 -0
  41. package/build/tools/code-refactor/removed-modules.js +61 -0
  42. package/build/tools/code-refactor/retrofit-state.d.ts +3 -2
  43. package/build/tools/code-refactor/retrofit-state.js +11 -12
  44. package/build/tools/code-refactor/reuse-outcome.d.ts +33 -1
  45. package/build/tools/code-refactor/reuse-record-store.d.ts +57 -0
  46. package/build/tools/code-refactor/reuse-record-store.js +140 -0
  47. package/build/tools/code-refactor/reuse-state.d.ts +34 -9
  48. package/build/tools/code-refactor/reuse-state.js +140 -135
  49. package/build/tools/code-refactor/utils-verify-gates.js +42 -18
  50. package/build/tools/code-refactor/verify-gates.d.ts +4 -0
  51. package/build/tools/code-refactor/verify-gates.js +8 -1
  52. package/build/tools/generate-tests/generateContractRestTool.js +7 -7
  53. package/build/tools/generate-tests/generateIntegrationRestTool.js +8 -8
  54. package/build/tools/generate-tests/generateUIRestTool.js +3 -3
  55. package/build/tools/submitReportTool.js +121 -30
  56. package/build/tools/test-management/actionsTool.js +7 -0
  57. package/build/tools/test-management/registerTestPlanTool.d.ts +1 -0
  58. package/build/tools/test-management/registerTestPlanTool.js +71 -23
  59. package/build/types/RepositoryAnalysis.d.ts +20 -20
  60. package/build/types/ReuseOutcome.d.ts +123 -0
  61. package/build/types/TestExecution.d.ts +11 -0
  62. package/build/types/TestExecution.js +19 -0
  63. package/build/types/index.d.ts +1 -1
  64. package/build/utils/branchDiff.d.ts +1 -1
  65. package/build/utils/branchDiff.js +1 -1
  66. package/build/utils/changedRuns.d.ts +13 -0
  67. package/build/utils/changedRuns.js +56 -0
  68. package/build/utils/featureFlags.d.ts +8 -10
  69. package/build/utils/featureFlags.js +23 -21
  70. package/build/utils/normalizeSkyrampImports.d.ts +4 -1
  71. package/build/utils/normalizeSkyrampImports.js +24 -17
  72. package/build/utils/pom-scope/ownership.d.ts +19 -0
  73. package/build/utils/pom-scope/ownership.js +31 -0
  74. package/build/utils/pom-scope/pom-files.d.ts +8 -0
  75. package/build/utils/pom-scope/pom-files.js +8 -0
  76. package/build/utils/pom-scope/scoring.js +13 -1
  77. package/build/utils/pom-scope/strip.d.ts +10 -0
  78. package/build/utils/pom-scope/strip.js +9 -0
  79. package/build/utils/pom-verify/verify.js +13 -2
  80. package/build/utils/reuseRouting.d.ts +30 -2
  81. package/build/utils/reuseRouting.js +36 -15
  82. package/build/utils/testDependencyPolicy.js +4 -16
  83. package/build/utils/utils-verify/action-sites.d.ts +2 -2
  84. package/build/utils/utils-verify/allow.d.ts +1 -1
  85. package/build/utils/utils-verify/allow.js +15 -0
  86. package/build/utils/utils-verify/body-reach.d.ts +2 -2
  87. package/build/utils/utils-verify/body-reach.js +4 -1
  88. package/build/utils/utils-verify/call-sites.d.ts +16 -6
  89. package/build/utils/utils-verify/call-sites.js +12 -6
  90. package/build/utils/utils-verify/changed-helpers.d.ts +38 -0
  91. package/build/utils/utils-verify/changed-helpers.js +91 -0
  92. package/build/utils/utils-verify/changed-lines.d.ts +48 -0
  93. package/build/utils/utils-verify/changed-lines.js +106 -0
  94. package/build/utils/utils-verify/head.d.ts +73 -0
  95. package/build/utils/utils-verify/head.js +156 -0
  96. package/build/utils/utils-verify/importers.d.ts +59 -10
  97. package/build/utils/utils-verify/importers.js +121 -31
  98. package/build/utils/utils-verify/in-house.js +26 -31
  99. package/build/utils/utils-verify/index.d.ts +3 -0
  100. package/build/utils/utils-verify/index.js +3 -0
  101. package/build/utils/utils-verify/language-spec.js +10 -2
  102. package/build/utils/utils-verify/locate.d.ts +3 -0
  103. package/build/utils/utils-verify/locate.js +6 -6
  104. package/build/utils/utils-verify/parse.d.ts +13 -1
  105. package/build/utils/utils-verify/parse.js +122 -49
  106. package/build/utils/utils-verify/retrofit-equivalence.d.ts +0 -5
  107. package/build/utils/utils-verify/retrofit-equivalence.js +2 -34
  108. package/build/utils/utils-verify/stage.js +4 -16
  109. package/build/utils/utils-verify/typecheck.d.ts +25 -4
  110. package/build/utils/utils-verify/typecheck.js +95 -13
  111. package/build/utils/utils-verify/verify.d.ts +33 -8
  112. package/build/utils/utils-verify/verify.js +180 -18
  113. package/build/utils/versions.d.ts +3 -3
  114. package/build/utils/versions.js +1 -1
  115. package/node_modules/playwright/lib/dom-analyzer/blueprint.js +1 -0
  116. package/node_modules/playwright/lib/dom-analyzer/dynamicId.js +1 -0
  117. package/node_modules/playwright/lib/dom-analyzer/dynamicId.test.js +6 -0
  118. package/node_modules/playwright/lib/mcp/skyramp/exportTool.js +4 -2
  119. package/node_modules/playwright/lib/mcp/skyramp/snapshotLocators.js +246 -0
  120. package/node_modules/playwright/lib/mcp/skyramp/traceRecordingBackend.js +378 -237
  121. package/node_modules/playwright/lib/mcp/test/skyRampExport.js +21 -0
  122. package/node_modules/playwright/node_modules/playwright-core/lib/generated/pollingRecorderSource.js +1 -1
  123. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{codeMirrorModule-LNgEKtdV.js → codeMirrorModule-Bou8kKzE.js} +1 -1
  124. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/assets/{defaultSettingsView-Bwr1eMKC.js → defaultSettingsView-DTp2-8Si.js} +3 -3
  125. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/index.html +2 -2
  126. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{index.BAkLd5DX.js → index.uiPTFncY.js} +1 -1
  127. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/{uiMode.BPopbasy.js → uiMode.GLqsx5cI.js} +1 -1
  128. package/node_modules/playwright/node_modules/playwright-core/lib/vite/traceViewer/uiMode.html +2 -2
  129. package/node_modules/playwright/node_modules/playwright-core/package.json +1 -1
  130. package/node_modules/playwright/node_modules/playwright-core/src/generated/pollingRecorderSource.ts +1 -1
  131. package/node_modules/playwright/package.json +1 -1
  132. package/package.json +3 -3
  133. package/plugin/prompts/code-reuse/hand-off.md +13 -1
  134. package/plugin/prompts/plan-tests.md +2 -2
  135. package/plugin/skills/enhance-assertions/reference/shared-rules.md +1 -0
@@ -251,15 +251,15 @@ export declare const endpointInteractionSchema: z.ZodObject<{
251
251
  headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
252
252
  queryParams: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
253
253
  }, "strip", z.ZodTypeAny, {
254
+ queryParams?: Record<string, any> | undefined;
254
255
  body?: Record<string, any> | undefined;
255
256
  contentType?: string | undefined;
256
257
  headers?: Record<string, string> | undefined;
257
- queryParams?: Record<string, any> | undefined;
258
258
  }, {
259
+ queryParams?: Record<string, any> | undefined;
259
260
  body?: Record<string, any> | undefined;
260
261
  contentType?: string | undefined;
261
262
  headers?: Record<string, string> | undefined;
262
- queryParams?: Record<string, any> | undefined;
263
263
  }>;
264
264
  response: z.ZodObject<{
265
265
  statusCode: z.ZodNumber;
@@ -314,10 +314,10 @@ export declare const endpointInteractionSchema: z.ZodObject<{
314
314
  type: "error" | "success" | "edge-case";
315
315
  description: string;
316
316
  request: {
317
+ queryParams?: Record<string, any> | undefined;
317
318
  body?: Record<string, any> | undefined;
318
319
  contentType?: string | undefined;
319
320
  headers?: Record<string, string> | undefined;
320
- queryParams?: Record<string, any> | undefined;
321
321
  };
322
322
  response: {
323
323
  description: string;
@@ -336,10 +336,10 @@ export declare const endpointInteractionSchema: z.ZodObject<{
336
336
  type: "error" | "success" | "edge-case";
337
337
  description: string;
338
338
  request: {
339
+ queryParams?: Record<string, any> | undefined;
339
340
  body?: Record<string, any> | undefined;
340
341
  contentType?: string | undefined;
341
342
  headers?: Record<string, string> | undefined;
342
- queryParams?: Record<string, any> | undefined;
343
343
  };
344
344
  response: {
345
345
  description: string;
@@ -390,15 +390,15 @@ export declare const endpointMethodSchema: z.ZodObject<{
390
390
  headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
391
391
  queryParams: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
392
392
  }, "strip", z.ZodTypeAny, {
393
+ queryParams?: Record<string, any> | undefined;
393
394
  body?: Record<string, any> | undefined;
394
395
  contentType?: string | undefined;
395
396
  headers?: Record<string, string> | undefined;
396
- queryParams?: Record<string, any> | undefined;
397
397
  }, {
398
+ queryParams?: Record<string, any> | undefined;
398
399
  body?: Record<string, any> | undefined;
399
400
  contentType?: string | undefined;
400
401
  headers?: Record<string, string> | undefined;
401
- queryParams?: Record<string, any> | undefined;
402
402
  }>;
403
403
  response: z.ZodObject<{
404
404
  statusCode: z.ZodNumber;
@@ -453,10 +453,10 @@ export declare const endpointMethodSchema: z.ZodObject<{
453
453
  type: "error" | "success" | "edge-case";
454
454
  description: string;
455
455
  request: {
456
+ queryParams?: Record<string, any> | undefined;
456
457
  body?: Record<string, any> | undefined;
457
458
  contentType?: string | undefined;
458
459
  headers?: Record<string, string> | undefined;
459
- queryParams?: Record<string, any> | undefined;
460
460
  };
461
461
  response: {
462
462
  description: string;
@@ -475,10 +475,10 @@ export declare const endpointMethodSchema: z.ZodObject<{
475
475
  type: "error" | "success" | "edge-case";
476
476
  description: string;
477
477
  request: {
478
+ queryParams?: Record<string, any> | undefined;
478
479
  body?: Record<string, any> | undefined;
479
480
  contentType?: string | undefined;
480
481
  headers?: Record<string, string> | undefined;
481
- queryParams?: Record<string, any> | undefined;
482
482
  };
483
483
  response: {
484
484
  description: string;
@@ -512,10 +512,10 @@ export declare const endpointMethodSchema: z.ZodObject<{
512
512
  type: "error" | "success" | "edge-case";
513
513
  description: string;
514
514
  request: {
515
+ queryParams?: Record<string, any> | undefined;
515
516
  body?: Record<string, any> | undefined;
516
517
  contentType?: string | undefined;
517
518
  headers?: Record<string, string> | undefined;
518
- queryParams?: Record<string, any> | undefined;
519
519
  };
520
520
  response: {
521
521
  description: string;
@@ -551,10 +551,10 @@ export declare const endpointMethodSchema: z.ZodObject<{
551
551
  type: "error" | "success" | "edge-case";
552
552
  description: string;
553
553
  request: {
554
+ queryParams?: Record<string, any> | undefined;
554
555
  body?: Record<string, any> | undefined;
555
556
  contentType?: string | undefined;
556
557
  headers?: Record<string, string> | undefined;
557
- queryParams?: Record<string, any> | undefined;
558
558
  };
559
559
  response: {
560
560
  description: string;
@@ -632,15 +632,15 @@ export declare const enrichedEndpointSchema: z.ZodObject<{
632
632
  headers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
633
633
  queryParams: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodAny>>;
634
634
  }, "strip", z.ZodTypeAny, {
635
+ queryParams?: Record<string, any> | undefined;
635
636
  body?: Record<string, any> | undefined;
636
637
  contentType?: string | undefined;
637
638
  headers?: Record<string, string> | undefined;
638
- queryParams?: Record<string, any> | undefined;
639
639
  }, {
640
+ queryParams?: Record<string, any> | undefined;
640
641
  body?: Record<string, any> | undefined;
641
642
  contentType?: string | undefined;
642
643
  headers?: Record<string, string> | undefined;
643
- queryParams?: Record<string, any> | undefined;
644
644
  }>;
645
645
  response: z.ZodObject<{
646
646
  statusCode: z.ZodNumber;
@@ -695,10 +695,10 @@ export declare const enrichedEndpointSchema: z.ZodObject<{
695
695
  type: "error" | "success" | "edge-case";
696
696
  description: string;
697
697
  request: {
698
+ queryParams?: Record<string, any> | undefined;
698
699
  body?: Record<string, any> | undefined;
699
700
  contentType?: string | undefined;
700
701
  headers?: Record<string, string> | undefined;
701
- queryParams?: Record<string, any> | undefined;
702
702
  };
703
703
  response: {
704
704
  description: string;
@@ -717,10 +717,10 @@ export declare const enrichedEndpointSchema: z.ZodObject<{
717
717
  type: "error" | "success" | "edge-case";
718
718
  description: string;
719
719
  request: {
720
+ queryParams?: Record<string, any> | undefined;
720
721
  body?: Record<string, any> | undefined;
721
722
  contentType?: string | undefined;
722
723
  headers?: Record<string, string> | undefined;
723
- queryParams?: Record<string, any> | undefined;
724
724
  };
725
725
  response: {
726
726
  description: string;
@@ -754,10 +754,10 @@ export declare const enrichedEndpointSchema: z.ZodObject<{
754
754
  type: "error" | "success" | "edge-case";
755
755
  description: string;
756
756
  request: {
757
+ queryParams?: Record<string, any> | undefined;
757
758
  body?: Record<string, any> | undefined;
758
759
  contentType?: string | undefined;
759
760
  headers?: Record<string, string> | undefined;
760
- queryParams?: Record<string, any> | undefined;
761
761
  };
762
762
  response: {
763
763
  description: string;
@@ -793,10 +793,10 @@ export declare const enrichedEndpointSchema: z.ZodObject<{
793
793
  type: "error" | "success" | "edge-case";
794
794
  description: string;
795
795
  request: {
796
+ queryParams?: Record<string, any> | undefined;
796
797
  body?: Record<string, any> | undefined;
797
798
  contentType?: string | undefined;
798
799
  headers?: Record<string, string> | undefined;
799
- queryParams?: Record<string, any> | undefined;
800
800
  };
801
801
  response: {
802
802
  description: string;
@@ -819,7 +819,6 @@ export declare const enrichedEndpointSchema: z.ZodObject<{
819
819
  }>, "many">;
820
820
  }, "strip", z.ZodTypeAny, {
821
821
  path: string;
822
- resourceGroup: string;
823
822
  pathParams: {
824
823
  type: string;
825
824
  name: string;
@@ -827,6 +826,7 @@ export declare const enrichedEndpointSchema: z.ZodObject<{
827
826
  description?: string | undefined;
828
827
  constraints?: string | undefined;
829
828
  }[];
829
+ resourceGroup: string;
830
830
  methods: {
831
831
  method: string;
832
832
  description: string;
@@ -843,10 +843,10 @@ export declare const enrichedEndpointSchema: z.ZodObject<{
843
843
  type: "error" | "success" | "edge-case";
844
844
  description: string;
845
845
  request: {
846
+ queryParams?: Record<string, any> | undefined;
846
847
  body?: Record<string, any> | undefined;
847
848
  contentType?: string | undefined;
848
849
  headers?: Record<string, string> | undefined;
849
- queryParams?: Record<string, any> | undefined;
850
850
  };
851
851
  response: {
852
852
  description: string;
@@ -869,7 +869,6 @@ export declare const enrichedEndpointSchema: z.ZodObject<{
869
869
  }[];
870
870
  }, {
871
871
  path: string;
872
- resourceGroup: string;
873
872
  pathParams: {
874
873
  type: string;
875
874
  name: string;
@@ -877,6 +876,7 @@ export declare const enrichedEndpointSchema: z.ZodObject<{
877
876
  description?: string | undefined;
878
877
  constraints?: string | undefined;
879
878
  }[];
879
+ resourceGroup: string;
880
880
  methods: {
881
881
  method: string;
882
882
  description: string;
@@ -893,10 +893,10 @@ export declare const enrichedEndpointSchema: z.ZodObject<{
893
893
  type: "error" | "success" | "edge-case";
894
894
  description: string;
895
895
  request: {
896
+ queryParams?: Record<string, any> | undefined;
896
897
  body?: Record<string, any> | undefined;
897
898
  contentType?: string | undefined;
898
899
  headers?: Record<string, string> | undefined;
899
- queryParams?: Record<string, any> | undefined;
900
900
  };
901
901
  response: {
902
902
  description: string;
@@ -131,6 +131,101 @@ export interface RetrofitOutcome {
131
131
  file: string;
132
132
  execution?: TestExecutionStatus;
133
133
  }
134
+ /** What a run did to a helper that already existed in the repository. A helper the
135
+ * module gained is not here: this run created it, and it has no incumbent callers
136
+ * whose behaviour it could change. */
137
+ export type HelperChangeKind =
138
+ /** The parameter list differs — the change a caller's own diff can be read
139
+ * against, and the one an added argument or a re-defaulted argument lands in
140
+ * (a helper re-defaulted to a rejection status is the recorded case). */
141
+ "signature"
142
+ /** The parameter list is byte-identical and the body is not. The dangerous case
143
+ * for a browser helper: a selector change alters what every caller does and
144
+ * appears in no calling test's diff. */
145
+ | "body"
146
+ /** The incumbent defined it and the module no longer does. Every caller's import
147
+ * now names something that is not there. */
148
+ | "removed";
149
+ /** A helper this run changed in a module that already existed in the repository,
150
+ * with the tests that call it.
151
+ *
152
+ * A shared helper's change reaches every caller at once — that is the feature's
153
+ * value and its danger. A caller the run did not touch is not delivered, so nothing
154
+ * executed it, and a break in one reached the customer's own continuous integration
155
+ * instead of this report. A browser helper is the worse case: its selector can
156
+ * change with no signature change at all, and the change appears in no calling
157
+ * test's diff. */
158
+ export interface ChangedHelperEntry {
159
+ /** REPOSITORY-RELATIVE path (posix) of the module that defines it — per helper,
160
+ * as {@link OrphanedHelper} names its: a delivery can carry several modules, and
161
+ * the same helper name in two of them is two entries a reader must tell apart. */
162
+ utilsFile: string;
163
+ helper: string;
164
+ change: HelperChangeKind;
165
+ /** The Skyramp-generated tests that call it, by BASENAME, each with the execution
166
+ * recorded against the module as it stands.
167
+ *
168
+ * SCOPED TO THE MODULE'S OWN DIRECTORY, which is as far as the caller walk reads.
169
+ * A generated test that imports the module from another directory is not here, so
170
+ * this is a lower bound: read it as "the callers found beside the module", never
171
+ * as "every test that calls it". The gate acts only on what is listed, so a caller
172
+ * it cannot see costs a report line and an unrun test — never a false block, which
173
+ * is the direction this feature chooses everywhere.
174
+ *
175
+ * ABSENT means even that much could not be established — never "no test calls
176
+ * it". An empty array is the positive statement that the walk ran and found
177
+ * none beside the module. A caller with no `execution` and no `deliveredByThisRun`
178
+ * was not run: the run could not execute it (a native suite, or a test the prescan
179
+ * never inventoried), which is disclosed rather than blocked on. */
180
+ callers?: ChangedHelperCaller[];
181
+ }
182
+ export interface ChangedHelperCaller {
183
+ /** BASENAME of the calling test. */
184
+ file: string;
185
+ execution?: TestExecutionStatus;
186
+ /** This run delivered or edited the caller, so its own row in this report carries
187
+ * its execution and its own checks judge it. Read an absent `execution` as "not
188
+ * run" only when this is absent too. */
189
+ deliveredByThisRun?: true;
190
+ }
191
+ /** A helper in a shared module that NO Skyramp-generated test imports.
192
+ *
193
+ * A run can delete a test, and a helper that test was the last importer of stays in
194
+ * the module. The customer then maintains code nothing calls, and nothing in the
195
+ * pull request says so — the agent was told, and the reviewer was not.
196
+ *
197
+ * REPORTED, never removed, and never gated. Removing a helper that is still called
198
+ * is far worse than leaving a dead one, and the caller list this is read from is a
199
+ * lower bound scoped to the module's directory. A reader decides; the run does not. */
200
+ export interface OrphanedHelper {
201
+ /** REPOSITORY-RELATIVE path (posix) of the module that defines it. Per helper, not
202
+ * per row: a delivery can carry several modules, and a reader needs the file to
203
+ * open. */
204
+ utilsFile: string;
205
+ helper: string;
206
+ }
207
+ /** A shared module this run CREATED and then REMOVED before delivery, with the
208
+ * reason the run state holds for it.
209
+ *
210
+ * A run builds a module, rewires tests onto it, and — when its verify pass keeps
211
+ * failing — deletes it and restores the inline code. Nothing then locates the
212
+ * module: the delivered tests import nothing, and every row reads as "nothing to
213
+ * reuse", which is the silence this entry exists to break. An incumbent module the
214
+ * run deleted is disclosed helper by helper under {@link ChangedHelperEntry}
215
+ * (`removed`); this is the module HEAD never held, which that comparison cannot
216
+ * see. Attached to every row in the module's own directory — the tests the module
217
+ * was built beside. */
218
+ export interface RemovedModuleEntry {
219
+ /** REPOSITORY-RELATIVE path (posix) the module had. */
220
+ utilsFile: string;
221
+ /** In a reader's words: that the run created and removed it, and what the last
222
+ * verify pass that had it said. */
223
+ reason: string;
224
+ /** The failures of the last verify pass that had the module, when it failed —
225
+ * the same shape as {@link HelperReuseOutcome.verificationFailures}. Absent when
226
+ * that pass passed. */
227
+ verificationFailures?: ReuseVerificationFailure[];
228
+ }
134
229
  /** Shared-helper reuse for an API test, re-derived from the delivered files at report
135
230
  * time — nothing here is read back from what the agent or the verify pass recorded.
136
231
  * Absent altogether when no utils file exists for the test (the same omission rule
@@ -159,6 +254,16 @@ export interface HelperReuseOutcome {
159
254
  * wraps — reuse that was available and not taken. Omitted at zero. Informational,
160
255
  * like {@link ReuseOutcome.missedReuse}. */
161
256
  siblingInlineCallSites?: number;
257
+ /** Helpers in `utilsFile` that no Skyramp-generated test imports — see
258
+ * {@link OrphanedHelper}. Omitted when there are none, and omitted (with
259
+ * `orphanScanIncomplete` set) when the walk could not establish the importers at
260
+ * all. Informational: nothing gates on it. */
261
+ orphanedHelpers?: OrphanedHelper[];
262
+ /** Present — and only ever `true` — when the importer walk could not read every
263
+ * file beside the module, so no helper is reported as orphaned. Absent means the
264
+ * walk ran, so an absent `orphanedHelpers` then means none were found rather than
265
+ * that nothing was looked for. */
266
+ orphanScanIncomplete?: true;
162
267
  /** Whether the delivered utils file holds the shared-helper invariants: one helper
163
268
  * per method+path, status-code-only assertions, method+resource names — with
164
269
  * documented declines (`reuse-verify: allow …`) counted as holding. */
@@ -296,6 +401,24 @@ export interface ReuseOutcome {
296
401
  * not the module, and is most telling when no module exists at all — the case
297
402
  * where `helpers` is rightly absent. Omitted at zero. Informational. */
298
403
  unextractedDuplicateSites?: number;
404
+ /** Helpers that already existed in the repository and that this run CHANGED, each
405
+ * with the tests that call it. Omitted when the run changed no incumbent helper —
406
+ * which is every first run, where the module is created rather than edited. A
407
+ * helper an earlier run created and this one changed appears here as changed,
408
+ * never as created.
409
+ *
410
+ * Top level, not on the helpers row, for the same reason as
411
+ * {@link unextractedDuplicateSites}: it describes what happened to a module, and
412
+ * it is most telling when NO module is there to describe — a run that deleted or
413
+ * renamed the shared module removes every helper at once and leaves `helpers`
414
+ * rightly absent, which is exactly when a reader most needs to be told. See
415
+ * {@link ChangedHelperEntry}. */
416
+ changedHelpers?: ChangedHelperEntry[];
417
+ /** Shared modules this run created beside this test and removed again before
418
+ * delivery — see {@link RemovedModuleEntry}. Top level like `changedHelpers`,
419
+ * and for the same reason: there is no `helpers` row left to hang it on, which
420
+ * is exactly when a reader most needs to be told. Omitted when none. */
421
+ removedModules?: RemovedModuleEntry[];
299
422
  verification?: ReuseVerificationOutcome;
300
423
  /** Why `verification` is `failed` on the page-object path, on the same terms as
301
424
  * {@link HelperReuseOutcome.verificationFailures}: present only when the
@@ -14,6 +14,17 @@ export declare enum TestExecutionStatus {
14
14
  /** Execution was expected (UPDATE/REGENERATE) but no result was recorded. */
15
15
  Unknown = "Unknown"
16
16
  }
17
+ /** Whether a status is a RESULT: the test ran and pass, fail or error is what it
18
+ * did. `Skipped` is a decision not to execute and `Unknown` is an execution that was
19
+ * expected and recorded nothing — neither may satisfy a demand for a run, or a run
20
+ * that never touched the file would clear the debt for it. */
21
+ export declare function isResult(status: TestExecutionStatus): boolean;
22
+ /** The status of `record` when it is a result that ran the bytes written at
23
+ * `sinceMs` or later, and `undefined` otherwise. THE freshness rule, for every gate
24
+ * that asks whether a file has been run as it now stands: an execution older than
25
+ * the last write to what it exercised ran bytes that no longer exist. Which write —
26
+ * the file's own, or a module's under it — is the caller's to say. */
27
+ export declare function freshExecution(record: Pick<TestExecutionData, "status" | "executedAt"> | undefined, sinceMs: number): TestExecutionStatus | undefined;
17
28
  export interface TestExecutionData {
18
29
  status: TestExecutionStatus;
19
30
  executedAt: string;
@@ -14,3 +14,22 @@ export var TestExecutionStatus;
14
14
  /** Execution was expected (UPDATE/REGENERATE) but no result was recorded. */
15
15
  TestExecutionStatus["Unknown"] = "Unknown";
16
16
  })(TestExecutionStatus || (TestExecutionStatus = {}));
17
+ /** Whether a status is a RESULT: the test ran and pass, fail or error is what it
18
+ * did. `Skipped` is a decision not to execute and `Unknown` is an execution that was
19
+ * expected and recorded nothing — neither may satisfy a demand for a run, or a run
20
+ * that never touched the file would clear the debt for it. */
21
+ export function isResult(status) {
22
+ return (status === TestExecutionStatus.Pass ||
23
+ status === TestExecutionStatus.Fail ||
24
+ status === TestExecutionStatus.Error);
25
+ }
26
+ /** The status of `record` when it is a result that ran the bytes written at
27
+ * `sinceMs` or later, and `undefined` otherwise. THE freshness rule, for every gate
28
+ * that asks whether a file has been run as it now stands: an execution older than
29
+ * the last write to what it exercised ran bytes that no longer exist. Which write —
30
+ * the file's own, or a module's under it — is the caller's to say. */
31
+ export function freshExecution(record, sinceMs) {
32
+ if (!record || !isResult(record.status))
33
+ return undefined;
34
+ return Date.parse(record.executedAt) >= sinceMs ? record.status : undefined;
35
+ }
@@ -4,5 +4,5 @@ export { TestType, HttpMethod } from "./TestTypes.js";
4
4
  export type { TestbotReport } from "./TestbotReport.js";
5
5
  export { IssueFoundCategory } from "./TestbotReport.js";
6
6
  export { ReuseDeclinedBy, ReuseVerificationOutcome, HelperVerificationOutcome, } from "./ReuseOutcome.js";
7
- export type { ReuseOutcome, ReuseSkippedEntry, ReuseMissedEntry, HelperReuseOutcome, RetrofitOutcome, } from "./ReuseOutcome.js";
7
+ export type { ReuseOutcome, ReuseSkippedEntry, ReuseMissedEntry, HelperReuseOutcome, RetrofitOutcome, ChangedHelperEntry, ChangedHelperCaller, OrphanedHelper, } from "./ReuseOutcome.js";
8
8
  export type { RelatedRepository, TestbotPromptOptions, } from "./TestbotPromptOptions.js";
@@ -72,5 +72,5 @@ export declare function changedFileNames(diff: BranchDiffData): ChangedFileName[
72
72
  * compares two commits through the three-dot form, which is the CI shape. */
73
73
  export declare function computeBranchDiff(repositoryPath: string, providedBaseBranch?: string, includeUncommitted?: boolean,
74
74
  /** Names only: no diff text is computed, and `diffContent`/`diffStat` come back
75
- * empty. The plan checks read which files changed, never what changed in them. */
75
+ * empty. */
76
76
  namesOnly?: boolean): Promise<BranchDiffData>;
@@ -349,7 +349,7 @@ export function changedFileNames(diff) {
349
349
  * compares two commits through the three-dot form, which is the CI shape. */
350
350
  export async function computeBranchDiff(repositoryPath, providedBaseBranch, includeUncommitted = false,
351
351
  /** Names only: no diff text is computed, and `diffContent`/`diffStat` come back
352
- * empty. The plan checks read which files changed, never what changed in them. */
352
+ * empty. */
353
353
  namesOnly = false) {
354
354
  const git = simpleGit(repositoryPath);
355
355
  const isRepo = await git.checkIsRepo();
@@ -0,0 +1,13 @@
1
+ /** One run of consecutive changed lines in a file: lines removed, added, or both. */
2
+ export interface ChangedRun {
3
+ file: string;
4
+ /** First and last line of the run in the new file. A run that only removes
5
+ * lines has none of its own, so both name the line that now follows it. */
6
+ start: number;
7
+ end: number;
8
+ /** The run's first changed line as the diff shows it, for the evidence. */
9
+ firstLine: string;
10
+ }
11
+ /** Every run of changed lines, file by file, in the new file's numbering. A run
12
+ * whose changed lines are all blank changes nothing a test can observe. */
13
+ export declare function collectChangedRuns(rawDiff: string): ChangedRun[];
@@ -0,0 +1,56 @@
1
+ import { diffBlocksByPath } from "./branchDiff.js";
2
+ /** Every run of changed lines, file by file, in the new file's numbering. A run
3
+ * whose changed lines are all blank changes nothing a test can observe. */
4
+ export function collectChangedRuns(rawDiff) {
5
+ const runs = [];
6
+ for (const [file, block] of diffBlocksByPath(rawDiff)) {
7
+ let inHunk = false;
8
+ let newLine = 0;
9
+ let open;
10
+ const close = () => {
11
+ if (open && open.lines.some((text) => text.slice(1).trim() !== "")) {
12
+ runs.push({
13
+ file,
14
+ start: open.start,
15
+ end: open.added > 0 ? open.start + open.added - 1 : open.start,
16
+ firstLine: open.lines.find((text) => text.slice(1).trim() !== "") ?? "",
17
+ });
18
+ }
19
+ open = undefined;
20
+ };
21
+ for (const text of block.split("\n")) {
22
+ // A path printed twice holds its header twice, and the second one is no hunk.
23
+ if (text.startsWith("diff ")) {
24
+ close();
25
+ inHunk = false;
26
+ continue;
27
+ }
28
+ const hunk = /^@@ -\d+(?:,\d+)? \+(\d+)/.exec(text);
29
+ if (hunk) {
30
+ close();
31
+ inHunk = true;
32
+ newLine = Number(hunk[1]);
33
+ continue;
34
+ }
35
+ // Only the file header spells `---`/`+++`; inside a hunk they are content.
36
+ if (!inHunk)
37
+ continue;
38
+ if (text.startsWith("-") || text.startsWith("+")) {
39
+ open ??= { start: newLine, added: 0, lines: [] };
40
+ open.lines.push(text);
41
+ if (text.startsWith("+")) {
42
+ open.added++;
43
+ newLine++;
44
+ }
45
+ }
46
+ else if (!text.startsWith("\\")) {
47
+ // `` belongs to the line before it.
48
+ close();
49
+ if (text.startsWith(" "))
50
+ newLine++;
51
+ }
52
+ }
53
+ close();
54
+ }
55
+ return runs;
56
+ }
@@ -4,8 +4,9 @@
4
4
  * Each helper in this module reads a `SKYRAMP_FEATURE_*` env var and returns
5
5
  * a boolean. These helpers treat a flag as ON only when the env var is
6
6
  * exactly `"1"`; anything else (unset, empty, "0", "true", "false", etc.) is
7
- * treated as OFF. The utils-reuse flag is additionally ON for the orgs in
8
- * `UTILS_REUSE_DEFAULT_ORGS` regardless of the env vars.
7
+ * treated as OFF. The POM-reuse and utils-reuse flags are additionally ON for
8
+ * the orgs in `POM_REUSE_DEFAULT_ORGS` and `UTILS_REUSE_DEFAULT_ORGS`
9
+ * regardless of the env vars.
9
10
  */
10
11
  /**
11
12
  * Gates BOTH consumer-side contract tests AND the SDK's "default mode"
@@ -54,15 +55,12 @@ export declare function isLocalDevEnabled(): boolean;
54
55
  export declare function isOneClickEnabled(): boolean;
55
56
  /**
56
57
  * Gates the POM-aware path of `skyramp_reuse_code`
57
- * (SKYRAMP_FEATURE_POM_REUSE=1).
58
+ * (SKYRAMP_FEATURE_POM_REUSE=1, or a `POM_REUSE_DEFAULT_ORGS` org).
58
59
  *
59
- * Deliberately NOT defaulted on for `UTILS_REUSE_DEFAULT_ORGS`: for UI tests
60
- * the POM path supersedes the modularize-first flow (see
61
- * `isModularizeFirstTarget` in utils/reuseRouting.ts), so an org-level POM
62
- * default would stop those orgs' UI tests from ever seeding a SkyrampUtils
63
- * file — the very thing the utils org default exists to provide (SKYR-4290).
64
- * And an explicit-"0"-override escape hatch cannot work either, because
65
- * testbot normalizes an unset flag to a literal "0" before forwarding.
60
+ * With both flags on, a UI test takes the POM pass first and the
61
+ * modularize-first flow over what it left inline (see `isPomFirstTarget` in
62
+ * utils/reuseRouting.ts), so a `POM_REUSE_DEFAULT_ORGS` org that also has the
63
+ * utils default still seeds a SkyrampUtils file for its UI tests.
66
64
  *
67
65
  * When OFF (default), TypeScript/JavaScript + Playwright targets are treated
68
66
  * like every other target: `skyramp_reuse_code` returns the SkyrampUtils
@@ -4,8 +4,9 @@
4
4
  * Each helper in this module reads a `SKYRAMP_FEATURE_*` env var and returns
5
5
  * a boolean. These helpers treat a flag as ON only when the env var is
6
6
  * exactly `"1"`; anything else (unset, empty, "0", "true", "false", etc.) is
7
- * treated as OFF. The utils-reuse flag is additionally ON for the orgs in
8
- * `UTILS_REUSE_DEFAULT_ORGS` regardless of the env vars.
7
+ * treated as OFF. The POM-reuse and utils-reuse flags are additionally ON for
8
+ * the orgs in `POM_REUSE_DEFAULT_ORGS` and `UTILS_REUSE_DEFAULT_ORGS`
9
+ * regardless of the env vars.
9
10
  */
10
11
  /**
11
12
  * Gates BOTH consumer-side contract tests AND the SDK's "default mode"
@@ -62,15 +63,12 @@ export function isOneClickEnabled() {
62
63
  }
63
64
  /**
64
65
  * Gates the POM-aware path of `skyramp_reuse_code`
65
- * (SKYRAMP_FEATURE_POM_REUSE=1).
66
+ * (SKYRAMP_FEATURE_POM_REUSE=1, or a `POM_REUSE_DEFAULT_ORGS` org).
66
67
  *
67
- * Deliberately NOT defaulted on for `UTILS_REUSE_DEFAULT_ORGS`: for UI tests
68
- * the POM path supersedes the modularize-first flow (see
69
- * `isModularizeFirstTarget` in utils/reuseRouting.ts), so an org-level POM
70
- * default would stop those orgs' UI tests from ever seeding a SkyrampUtils
71
- * file — the very thing the utils org default exists to provide (SKYR-4290).
72
- * And an explicit-"0"-override escape hatch cannot work either, because
73
- * testbot normalizes an unset flag to a literal "0" before forwarding.
68
+ * With both flags on, a UI test takes the POM pass first and the
69
+ * modularize-first flow over what it left inline (see `isPomFirstTarget` in
70
+ * utils/reuseRouting.ts), so a `POM_REUSE_DEFAULT_ORGS` org that also has the
71
+ * utils default still seeds a SkyrampUtils file for its UI tests.
74
72
  *
75
73
  * When OFF (default), TypeScript/JavaScript + Playwright targets are treated
76
74
  * like every other target: `skyramp_reuse_code` returns the SkyrampUtils
@@ -83,7 +81,8 @@ export function isOneClickEnabled() {
83
81
  * the verification gates apply.
84
82
  */
85
83
  export function isPomReuseEnabled() {
86
- return process.env.SKYRAMP_FEATURE_POM_REUSE === "1";
84
+ return (process.env.SKYRAMP_FEATURE_POM_REUSE === "1" ||
85
+ isDefaultOrg(POM_REUSE_DEFAULT_ORGS));
87
86
  }
88
87
  /**
89
88
  * Gates SkyrampUtils code reuse for integration tests on the testbot path, and
@@ -107,27 +106,30 @@ export function isPomReuseEnabled() {
107
106
  * yields the single reuse pass it did before the feature existed.
108
107
  */
109
108
  export function isUtilsReuseEnabled() {
110
- return (process.env.SKYRAMP_FEATURE_UTILS_REUSE === "1" || isUtilsReuseDefaultOrg());
109
+ return (process.env.SKYRAMP_FEATURE_UTILS_REUSE === "1" ||
110
+ isDefaultOrg(UTILS_REUSE_DEFAULT_ORGS));
111
111
  }
112
112
  /**
113
- * GitHub orgs where SkyrampUtils code reuse is on by default, without
114
- * requiring the SKYRAMP_FEATURE_UTILS_REUSE env var. Compared
115
- * case-insensitively (GitHub owner handles are case-insensitive, but
116
- * GITHUB_REPOSITORY_OWNER preserves display casing). Because this ORs with
117
- * the env check, these orgs cannot turn the feature off by setting the env
118
- * var to "0". POM reuse has no org default — see `isPomReuseEnabled`.
113
+ * GitHub orgs where a reuse feature is on by default, without requiring its
114
+ * SKYRAMP_FEATURE_* env var. Compared case-insensitively (GitHub owner handles
115
+ * are case-insensitive, but GITHUB_REPOSITORY_OWNER preserves display casing).
116
+ * Because these OR with the env check, these orgs cannot turn the feature off
117
+ * by setting the env var to "0" — and testbot normalizes an unset flag to a
118
+ * literal "0" anyway.
119
119
  *
120
- * "Skyramp-HandsOn" is the Wipro / IntelliAssure pilot org (SKYR-4286).
120
+ * "Skyramp-HandsOn" is the Wipro / IntelliAssure pilot org (SKYR-4286 for
121
+ * utils reuse, SKYR-4514 for POM reuse).
121
122
  */
122
123
  const UTILS_REUSE_DEFAULT_ORGS = ["skyramp-handson"];
123
- function isUtilsReuseDefaultOrg() {
124
+ const POM_REUSE_DEFAULT_ORGS = ["skyramp-handson"];
125
+ function isDefaultOrg(orgs) {
124
126
  // GITHUB_REPOSITORY_OWNER is set on GitHub Actions runners; fall back to
125
127
  // the owner half of GITHUB_REPOSITORY ("owner/repo") for environments that
126
128
  // only set the latter.
127
129
  const owner = process.env.GITHUB_REPOSITORY_OWNER ??
128
130
  process.env.GITHUB_REPOSITORY?.split("/")[0] ??
129
131
  "";
130
- return UTILS_REUSE_DEFAULT_ORGS.includes(owner.toLowerCase());
132
+ return orgs.includes(owner.toLowerCase());
131
133
  }
132
134
  /**
133
135
  * Returns true when the calling agent has the Skyramp Agent Skills plugin
@@ -1,7 +1,10 @@
1
1
  /**
2
2
  * Ensure `expect` is imported from `@skyramp/skyramp` rather than `@playwright/test`.
3
3
  * - Strips `expect` from any `@playwright/test` import (keeps other identifiers).
4
- * - If the file uses `expect(...)`, ensures an `@skyramp/skyramp` import carries `expect`.
4
+ * - If the file uses `expect(...)`, ensures its `@skyramp/skyramp` import carries `expect`.
5
+ * A file with no `@skyramp/skyramp` import is not a Skyramp test (a repository's
6
+ * own spec), so it is left alone, as is a file that takes `expect` from another
7
+ * module.
5
8
  * Returns the original string if no rewrite was needed.
6
9
  */
7
10
  export declare function normalizeSkyrampExpectImport(source: string): string;