@skyramp/mcp 0.4.0 → 0.4.1-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 (83) hide show
  1. package/build/prompts/enhance-assertions/contractProviderAssertionsPrompt.js +2 -1
  2. package/build/prompts/enhance-assertions/integrationAssertionsPrompt.js +2 -1
  3. package/build/prompts/enhance-assertions/sharedAssertionRules.d.ts +1 -1
  4. package/build/prompts/enhance-assertions/sharedAssertionRules.js +57 -22
  5. package/build/prompts/enhance-assertions/uiAssertionsPrompt.js +17 -9
  6. package/build/prompts/test-recommendation/diffExecutionPlan.js +0 -2
  7. package/build/prompts/test-recommendation/test-recommendation-prompt.js +11 -6
  8. package/build/prompts/testbot/testbot-prompts.js +19 -12
  9. package/build/recommendation/answers.d.ts +11 -7
  10. package/build/recommendation/answers.js +14 -10
  11. package/build/recommendation/pullRequestText.d.ts +18 -0
  12. package/build/recommendation/pullRequestText.js +31 -0
  13. package/build/recommendation/registerPlan.d.ts +9 -1
  14. package/build/recommendation/registerPlan.js +8 -1
  15. package/build/recommendation/runVerifiers.js +6 -0
  16. package/build/recommendation/types.d.ts +58 -0
  17. package/build/recommendation/verifierContracts.d.ts +108 -13
  18. package/build/recommendation/verifierContracts.js +147 -29
  19. package/build/recommendation/verifiers/coverage.d.ts +10 -0
  20. package/build/recommendation/verifiers/coverage.js +144 -22
  21. package/build/recommendation/verifiers/defects.d.ts +9 -0
  22. package/build/recommendation/verifiers/defects.js +117 -0
  23. package/build/recommendation/verifiers/deliveredMatchesPlan.d.ts +22 -0
  24. package/build/recommendation/verifiers/deliveredMatchesPlan.js +43 -0
  25. package/build/recommendation/verifiers/existingCoverage.js +53 -0
  26. package/build/recommendation/verifiers/expectedValueSourced.d.ts +14 -0
  27. package/build/recommendation/verifiers/expectedValueSourced.js +246 -0
  28. package/build/recommendation/verifiers/issueTraceability.d.ts +52 -0
  29. package/build/recommendation/verifiers/issueTraceability.js +197 -0
  30. package/build/recommendation/verifiers/requirementSourced.d.ts +2 -0
  31. package/build/recommendation/verifiers/requirementSourced.js +168 -0
  32. package/build/services/TestGenerationService.js +3 -1
  33. package/build/tools/code-refactor/codeReuseTool.js +1 -1
  34. package/build/tools/code-refactor/reuse-outcome.d.ts +1 -1
  35. package/build/tools/code-refactor/reuse-state.d.ts +85 -7
  36. package/build/tools/code-refactor/reuse-state.js +239 -34
  37. package/build/tools/code-refactor/utils-verify-gates.d.ts +5 -0
  38. package/build/tools/code-refactor/utils-verify-gates.js +103 -11
  39. package/build/tools/generate-tests/generateBatchScenarioRestTool.js +2 -1
  40. package/build/tools/submitReportTool.js +276 -39
  41. package/build/tools/test-management/actionsTool.js +5 -0
  42. package/build/tools/test-management/analyzeChangesTool.d.ts +53 -0
  43. package/build/tools/test-management/analyzeChangesTool.js +55 -2
  44. package/build/tools/test-management/registerTestPlanTool.d.ts +32 -17
  45. package/build/tools/test-management/registerTestPlanTool.js +105 -16
  46. package/build/types/ReuseOutcome.d.ts +73 -7
  47. package/build/types/TestAnalysis.d.ts +6 -0
  48. package/build/types/TestbotReport.d.ts +22 -4
  49. package/build/utils/AnalysisStateManager.d.ts +7 -1
  50. package/build/utils/AnalysisStateManager.js +5 -1
  51. package/build/utils/assertion-verify/api-shared-lints.js +118 -11
  52. package/build/utils/assertion-verify/format.js +2 -2
  53. package/build/utils/assertion-verify/helper-imports.d.ts +7 -0
  54. package/build/utils/assertion-verify/helper-imports.js +119 -27
  55. package/build/utils/assertion-verify/lint-types.d.ts +31 -2
  56. package/build/utils/assertion-verify/lint-types.js +66 -0
  57. package/build/utils/assertion-verify/metrics.d.ts +13 -0
  58. package/build/utils/assertion-verify/metrics.js +16 -0
  59. package/build/utils/assertion-verify/verify.d.ts +11 -6
  60. package/build/utils/assertion-verify/verify.js +56 -15
  61. package/build/utils/canonicalJson.d.ts +11 -0
  62. package/build/utils/canonicalJson.js +17 -0
  63. package/build/utils/utils-verify/action-key.d.ts +27 -0
  64. package/build/utils/utils-verify/action-key.js +292 -0
  65. package/build/utils/utils-verify/allow.d.ts +8 -1
  66. package/build/utils/utils-verify/allow.js +14 -1
  67. package/build/utils/utils-verify/call-sites.d.ts +76 -8
  68. package/build/utils/utils-verify/call-sites.js +256 -70
  69. package/build/utils/utils-verify/language-spec.d.ts +3 -2
  70. package/build/utils/utils-verify/parse.d.ts +22 -3
  71. package/build/utils/utils-verify/parse.js +123 -52
  72. package/build/utils/utils-verify/verify.d.ts +33 -3
  73. package/build/utils/utils-verify/verify.js +126 -12
  74. package/build/utils/workspaceAuth.d.ts +59 -19
  75. package/build/utils/workspaceAuth.js +228 -31
  76. package/package.json +1 -1
  77. package/plugin/prompts/generate-tests/execution-plan.md +3 -3
  78. package/plugin/prompts/generate-tests/generation.md +1 -0
  79. package/plugin/prompts/plan-tests.md +41 -15
  80. package/plugin/prompts/testbot-task1.md +3 -9
  81. package/build/prompts/testbot/planDeclarations.d.ts +0 -6
  82. package/build/prompts/testbot/planDeclarations.js +0 -9
  83. package/plugin/prompts/declaring-a-plan.md +0 -20
@@ -0,0 +1,31 @@
1
+ /** The pull request's own title and description, as the run rendered its prompt with.
2
+ *
3
+ * IN PROCESS, never the state file: the prompt and the plan tool are registered on
4
+ * the same server, so the text is already here, and writing it would put the pull
5
+ * request's prose on disk once per registration for a reader in the same process.
6
+ *
7
+ * ONE SLOT, last write wins. A run renders the prompt once, and a second render is
8
+ * a second run whose text replaces the first. */
9
+ let recorded;
10
+ const asText = (value) => (typeof value === "string" ? value : "");
11
+ /** Called by every entry point that renders the testbot prompt, with the values it
12
+ * renders. */
13
+ export function recordPullRequestText(title, description) {
14
+ recorded = { title: asText(title), description: asText(description) };
15
+ }
16
+ /** UNDEFINED when this process never rendered the prompt — a caller that drives the
17
+ * tools directly, or a test. A check that reads the pull request stays silent then;
18
+ * it cannot tell an empty pull request from one it never saw. */
19
+ export function pullRequestText() {
20
+ return recorded;
21
+ }
22
+ export function clearPullRequestText() {
23
+ recorded = undefined;
24
+ }
25
+ /** The pull request as one searchable string. Whitespace runs fold to one space, so
26
+ * a quote wrapped across lines still matches the sentence it was taken from. */
27
+ export const searchable = (pr) => `${pr.title}\n${pr.description}`.replace(/\s+/g, " ").trim().toLowerCase();
28
+ export const appearsIn = (haystack, needle) => {
29
+ const folded = needle.replace(/\s+/g, " ").trim().toLowerCase();
30
+ return folded.length > 0 && haystack.includes(folded);
31
+ };
@@ -1,11 +1,19 @@
1
- import { Objection, ObjectionAnswer, PlanChange, PlanInput, PlannedTest, VerifyContext } from "./types.js";
1
+ import { Objection, ObjectionAnswer, PlanChange, PlanDefect, PlanInput, PlanMaintenance, PlannedTest, VerifyContext } from "./types.js";
2
2
  /** The plan as stored for the run. Every registration produces one; the newest
3
3
  * replaces the last. */
4
4
  export interface Plan {
5
5
  /** The changes the agent said the diff makes. Published in the report
6
6
  * beside the tests delivered against each one. */
7
7
  changes: PlanChange[];
8
+ /** The defects the code review found. skyramp_submit_report reads them to hold
9
+ * the report to the review: every one is an issue, and every issue names the
10
+ * test that proves it. */
11
+ defects: PlanDefect[];
8
12
  plannedTests: PlannedTest[];
13
+ /** Existing tests this run edits instead of planning new ones. Stored because
14
+ * `coverage` counts the changes they cite, so a reader of the plan has to be
15
+ * able to see which coverage came from maintenance rather than a new test. */
16
+ maintains?: PlanMaintenance[];
9
17
  /** Objections the agent answered, with the answers. */
10
18
  answeredObjections: Array<{
11
19
  objection: Objection;
@@ -53,12 +53,13 @@ export function registerPlan(registration, ctx, previous) {
53
53
  const acknowledged = Array.isArray(registration.answers) ? registration.answers : [];
54
54
  const plannedTests = Array.isArray(registration.plannedTests) ? registration.plannedTests : [];
55
55
  const changes = Array.isArray(registration.changes) ? registration.changes : [];
56
+ const defects = Array.isArray(registration.defects) ? registration.defects : [];
56
57
  const registrationNumber = storedRegistrationNumber(registration);
57
58
  // The verifiers see the same normalised fields the STORED plan gets, not the raw
58
59
  // registration: handing them the raw one let a malformed top-level field crash
59
60
  // every check at once, giving eight `:crashed` objections and no real ones.
60
61
  // Crash containment is for a malformed field INSIDE a planned test.
61
- const verified = runPlanTimeVerifiers({ ...registration, plannedTests, changes, answers: acknowledged }, ctx);
62
+ const verified = runPlanTimeVerifiers({ ...registration, plannedTests, changes, defects, answers: acknowledged }, ctx);
62
63
  const objections = [
63
64
  ...verified,
64
65
  ...unknownAnswerObjections(verified, acknowledged, {
@@ -107,7 +108,13 @@ export function registerPlan(registration, ctx, previous) {
107
108
  : {}),
108
109
  plan: {
109
110
  changes,
111
+ defects,
110
112
  plannedTests,
113
+ // Only when declared: the field is optional, and writing `[]` on every plan
114
+ // would change the stored shape of every run that maintains nothing.
115
+ ...(Array.isArray(registration.maintains) && registration.maintains.length > 0
116
+ ? { maintains: registration.maintains }
117
+ : {}),
111
118
  answeredObjections,
112
119
  unverifiedCloses,
113
120
  openObjections,
@@ -6,7 +6,10 @@ import { expectedOutcomeAtPlanTime } from "./verifiers/expectedOutcome.js";
6
6
  import { uiElementGrounded } from "./verifiers/uiElementGrounded.js";
7
7
  import { screenRoute } from "./verifiers/screenRoute.js";
8
8
  import { coverage } from "./verifiers/coverage.js";
9
+ import { expectedValueSourced } from "./verifiers/expectedValueSourced.js";
9
10
  import { removedElementGuarded } from "./verifiers/removedElementGuarded.js";
11
+ import { requirementSourced } from "./verifiers/requirementSourced.js";
12
+ import { defects } from "./verifiers/defects.js";
10
13
  /** Verifiers that need only the plan and the run's facts. The order is the order
11
14
  * objections are reported in; it carries no priority. */
12
15
  export const PLAN_TIME_VERIFIERS = [
@@ -18,7 +21,10 @@ export const PLAN_TIME_VERIFIERS = [
18
21
  uiElementGrounded,
19
22
  screenRoute,
20
23
  coverage,
24
+ expectedValueSourced,
21
25
  removedElementGuarded,
26
+ requirementSourced,
27
+ defects,
22
28
  ];
23
29
  /** How a crashed verifier reads back. The id is the verifier's name plus a fixed
24
30
  * word, so it stays stable across registrations and can be answered. */
@@ -23,6 +23,9 @@ export interface PlanChange {
23
23
  id: string;
24
24
  text: string;
25
25
  source: string;
26
+ /** The requirement in the pull request's own words, when `source` names the pull
27
+ * request. Checked against the text the run rendered its prompt with. */
28
+ quote?: string;
26
29
  /** Where a user or caller meets this change. Every change states it: an optional
27
30
  * field the agent skips gives the check nothing to read. */
28
31
  surfaces: Array<"api" | "page">;
@@ -34,8 +37,32 @@ export interface PlanChange {
34
37
  value?: string | number | boolean | null;
35
38
  absent?: true;
36
39
  expect: "accept" | "reject";
40
+ /** What the response must carry when this case is sent. Declared here so the
41
+ * test asserts a value the plan decided, not the value the running app
42
+ * happened to return — the one source that always agrees with a defect. */
43
+ expectedValue?: string | number | boolean | null;
44
+ /** Where the agent read `expectedValue` OR `derived` — the check reads it for
45
+ * either one: `pr-description`, `spec:<path>`, `convention:<file:line>`, or
46
+ * `code`. */
47
+ expectedFrom?: string;
48
+ /** The rule the value follows from, in the source's words, when the source
49
+ * states a rule and not a value. Stands in for `expectedValue`: the test
50
+ * computes the number from what it sent, so nothing pins one the source
51
+ * never wrote. */
52
+ derived?: string;
37
53
  }>;
38
54
  }
55
+ /** One defect the code review found, as the agent read it. Nothing on the server
56
+ * parses the description or opens the file: the check reads the id, and a test
57
+ * that cites it and expects to fail is what proves it. */
58
+ export interface PlanDefect {
59
+ id: string;
60
+ file: string;
61
+ /** Advisory. Line numbers drift, so nothing checks it. */
62
+ line?: number;
63
+ description: string;
64
+ severity: "critical" | "high" | "medium" | "low";
65
+ }
39
66
  /** What a planned test states so a verifier has a checkable change. Kept in step with
40
67
  * `declarationFieldsSchema`, which the plan tool asserts against this type. */
41
68
  export interface PlannedTestDetails {
@@ -74,6 +101,9 @@ export interface PlannedTestDetails {
74
101
  };
75
102
  /** The ids of the declared changes this test proves. */
76
103
  changes?: string[];
104
+ /** The ids of the declared defects this test proves. A test proves a defect
105
+ * only when it expects to fail; the defects check reads both. */
106
+ defects?: string[];
77
107
  /** The state the mutated record is in before the mutation. Presence only. */
78
108
  startState?: string;
79
109
  /** UI planned tests only. `items` entries are COPIES of elements a capture of
@@ -100,10 +130,31 @@ export interface ObjectionAnswer {
100
130
  * An objection about an untested change closes only with one of these. */
101
131
  blocker?: string;
102
132
  }
133
+ /** An existing test this run maintains instead of planning a new one, and the
134
+ * declared changes that maintenance covers. A removal already covered by a spec
135
+ * is maintained, not duplicated, so the plan needs a way to say "this change is
136
+ * tested by a file I am editing" — without it, `coverage:change:` is unanswerable
137
+ * for a correct maintenance decision, and it closes only with a `blocker`. */
138
+ export interface PlanMaintenance {
139
+ /** Repository-relative path of the existing test. Only its presence in the
140
+ * checkout is checked, the same rule `declarations.existingTests` follows. */
141
+ file: string;
142
+ /** The edit this run makes to it, in one sentence. */
143
+ differsBy: string;
144
+ /** The ids of the declared changes this maintenance covers. */
145
+ changes: string[];
146
+ }
103
147
  export interface PlanInput {
104
148
  /** The changes the diff makes. Each planned test cites the ones it tests. */
105
149
  changes: PlanChange[];
150
+ /** The defects the code review found. Each planned test cites the ones it
151
+ * proves. Empty is a statement — the review found none — and draws an
152
+ * objection the agent answers. */
153
+ defects: PlanDefect[];
106
154
  plannedTests: PlannedTest[];
155
+ /** Existing tests this run edits rather than replacing with new ones. Coverage
156
+ * counts the changes these cite, so a maintenance-only plan is a complete plan. */
157
+ maintains?: PlanMaintenance[];
107
158
  answers: ObjectionAnswer[];
108
159
  /** The tool owns this and overwrites whatever the caller sends. */
109
160
  registrationNumber: number;
@@ -141,6 +192,13 @@ export interface VerifyContext {
141
192
  /** Identifiers a rename replaced, empty when the diff renames nothing. Only the
142
193
  * server pairs the two sides of a diff. */
143
194
  retiredUiElements: RemovedUiElement[];
195
+ /** The pull request's title and description, as the run rendered its prompt with.
196
+ * The plan tool fills it from the process the prompt was rendered in. Both fields
197
+ * blank MEANS the run had no title and no description. */
198
+ pullRequest: {
199
+ title: string;
200
+ description: string;
201
+ };
144
202
  /** Whether a cited path names a file in any of the run's repositories. Injected
145
203
  * to keep the verifiers filesystem-free. */
146
204
  citedFileExists(relativePath: string): boolean;
@@ -17,8 +17,6 @@ export interface ContractObjection {
17
17
  export interface VerifierContract {
18
18
  /** The verifier's own `Verifier.name`. */
19
19
  readonly id: string;
20
- /** What the registered plan has to carry for the check to have anything to read. */
21
- readonly declarationFields: readonly string[];
22
20
  /** One entry per message the verifier can emit, keyed by the case. A key naming a
23
21
  * segment of the `objectionId` uses that segment's spelling. The keys are widened
24
22
  * here so every contract satisfies the interface, while each contract is `as
@@ -31,7 +29,6 @@ export interface VerifierContract {
31
29
  }
32
30
  export declare const CHANGED_FILE_CONTRACT: {
33
31
  readonly id: "changedFile";
34
- readonly declarationFields: readonly ["declarations.changedFile", "declarations.screenEvidence.file"];
35
32
  readonly objections: {
36
33
  readonly notStated: {
37
34
  readonly message: "This test does not state which changed file it targets.";
@@ -54,7 +51,6 @@ export declare const CHANGED_FILE_CONTRACT: {
54
51
  };
55
52
  export declare const SCREEN_ROUTE_CONTRACT: {
56
53
  readonly id: "screenRoute";
57
- readonly declarationFields: readonly ["declarations.elements.pageUrl", "declarations.screenEvidence.file", "declarations.changedFile"];
58
54
  readonly objections: {
59
55
  readonly mismatch: {
60
56
  readonly message: "The page this UI test opens is not a page that renders the changed file it is about.";
@@ -69,7 +65,6 @@ export declare const SCREEN_ROUTE_CONTRACT: {
69
65
  };
70
66
  export declare const ENDPOINT_GROUNDED_CONTRACT: {
71
67
  readonly id: "endpointGrounded";
72
- readonly declarationFields: readonly ["steps[]", "testType", "declarations.routes"];
73
68
  readonly objections: {
74
69
  readonly uncited: {
75
70
  readonly message: "A call this test makes cites no file that declares it.";
@@ -88,7 +83,6 @@ export declare const ENDPOINT_GROUNDED_CONTRACT: {
88
83
  };
89
84
  export declare const STATED_DIFFERENCE_CONTRACT: {
90
85
  readonly id: "statedDifference";
91
- readonly declarationFields: readonly ["steps[]", "testType", "scenarioName", "declarations.differsFrom"];
92
86
  readonly objections: {
93
87
  readonly unexplainedPair: {
94
88
  readonly message: "Another planned test in this plan tests the same endpoint and neither says how they differ.";
@@ -107,18 +101,24 @@ export declare const STATED_DIFFERENCE_CONTRACT: {
107
101
  };
108
102
  export declare const EXISTING_COVERAGE_CONTRACT: {
109
103
  readonly id: "existingCoverage";
110
- readonly declarationFields: readonly ["declarations.existingTests"];
111
104
  readonly objections: {
112
105
  readonly citedTestMissing: {
113
106
  readonly message: "This planned test names an existing test file that is not in the checkout.";
114
107
  readonly suggestion: "Name the file repository-relative, spelled as the analysis lists it. Drop the entry if it names a test you did not read — naming none is an answer too.";
115
108
  };
109
+ readonly maintainedTestMissing: {
110
+ readonly message: "`maintains` names an existing test file that is not in the checkout.";
111
+ readonly suggestion: "Name the file repository-relative, spelled as the analysis lists it. A maintenance entry is what makes its changes count as covered, so it has to name a file this run can edit.";
112
+ };
113
+ readonly maintainedUnknownChange: {
114
+ readonly message: "A `maintains` entry cites a change id that this plan does not declare.";
115
+ readonly suggestion: "Spell the change's `id` exactly as your own `changes` list gives it — the match is exact apart from surrounding space. Declare the change if it is missing.";
116
+ };
116
117
  };
117
- readonly suggestion: "Which existing tests did you read on this change, and how does this planned test differ from them?";
118
+ readonly suggestion: "Which existing tests did you read on this change, how does this planned test differ from them, and which changes does the maintenance you are doing cover?";
118
119
  };
119
120
  export declare const EXPECTED_OUTCOME_CONTRACT: {
120
121
  readonly id: "expectedOutcome";
121
- readonly declarationFields: readonly ["category", "declarations.expected.outcome", "declarations.expected.why"];
122
122
  readonly objections: {
123
123
  readonly passInShouldFailCategory: {
124
124
  readonly message: "This planned test is categorised \"{category}\" but expects to pass on the app as it stands.";
@@ -133,7 +133,6 @@ export declare const EXPECTED_OUTCOME_CONTRACT: {
133
133
  };
134
134
  export declare const UI_ELEMENT_GROUNDED_CONTRACT: {
135
135
  readonly id: "uiElementGrounded";
136
- readonly declarationFields: readonly ["testType", "steps[]", "declarations.elements.items", "declarations.elements.pageUrl", "declarations.asserts", "scenarioName", "description", "declarations.expected.why"];
137
136
  readonly objections: {
138
137
  readonly nogrounding: {
139
138
  readonly message: "This UI test names no `elements.items`, so nothing says the elements it targets are on the page.";
@@ -156,11 +155,10 @@ export declare const UI_ELEMENT_GROUNDED_CONTRACT: {
156
155
  };
157
156
  export declare const COVERAGE_CONTRACT: {
158
157
  readonly id: "coverage";
159
- readonly declarationFields: readonly ["changes", "declarations.changes", "declarations.changedFile", "declarations.routes", "declarations.screenEvidence.file", "declarations.startState", "changes[].cases", "changes[].surfaces"];
160
158
  readonly objections: {
161
159
  readonly change: {
162
160
  readonly message: "This declared change has no test in the plan.";
163
- readonly suggestion: "Plan a test that exercises it. If this run cannot write one, answer it and set `blocker` to what stopped the run — a service that is not running, a paired branch that no longer exists, the one credential the run holds. An answer with no blocker leaves this open: a change nothing prevented a test from reaching takes the test. Coverage per file is the basic minimum; coverage per change is what the plan is judged by, so a file covered by a shape test still leaves the change it makes untested.";
161
+ readonly suggestion: "Plan a test that exercises it. If an existing test already covers it and this run edits that test rather than writing a new one, declare it in `maintains` with the file, what your edit changes, and this change's id — maintenance is coverage, and a duplicate spec beside the one you edited is the mistake. If this run cannot write one, answer it and set `blocker` to what stopped the run — a service that is not running, a paired branch that no longer exists, the one credential the run holds. An answer with no blocker leaves this open: a change nothing prevented a test from reaching takes the test. Coverage per file is the basic minimum; coverage per change is what the plan is judged by, so a file covered by a shape test still leaves the change it makes untested.";
164
162
  };
165
163
  readonly noChanges: {
166
164
  readonly message: "The plan declares no changes, so nothing says what the pull request must make different.";
@@ -195,7 +193,6 @@ export declare const COVERAGE_CONTRACT: {
195
193
  };
196
194
  export declare const REMOVED_ELEMENT_GUARDED_CONTRACT: {
197
195
  readonly id: "removedElementGuarded";
198
- readonly declarationFields: readonly ["declarations.elements.items", "declarations.asserts"];
199
196
  readonly objections: {
200
197
  readonly missing: {
201
198
  readonly message: "The diff removes `{element}` and no planned test says it is gone.";
@@ -208,6 +205,104 @@ export declare const REMOVED_ELEMENT_GUARDED_CONTRACT: {
208
205
  };
209
206
  readonly suggestion: "Does every element this diff removes have a planned test that asserts it is gone, and does no test claim a renamed identifier is gone?";
210
207
  };
208
+ export declare const EXPECTED_VALUE_SOURCED_CONTRACT: {
209
+ readonly id: "expectedValueSourced";
210
+ readonly objections: {
211
+ readonly sourcedFromCode: {
212
+ readonly message: "This case expects a value it read from the code as it stands, so a defect in that code becomes what the test demands.";
213
+ readonly suggestion: "Read the value from the pull request title or description, from a requirements file the description names, or from a convention the application already follows elsewhere, and name that source. If none of them states it, leave `expectedValue` out and assert the shape instead.";
214
+ };
215
+ readonly missingSource: {
216
+ readonly message: "This case states an expected value and does not say where the value came from.";
217
+ readonly suggestion: "Set `expectedFrom` to `pr-title`, `pr-description`, `spec:<path>`, `convention:<file:line>` or `code`.";
218
+ };
219
+ readonly unreadableSource: {
220
+ readonly message: "This case names a file as the source of its expected value, and that file is not in the checkout.";
221
+ readonly suggestion: "Give the path as the repository spells it, from the repository root. If the file is not there, the value has no source: read it from the pull request title or description instead, or leave `expectedValue` out and assert the shape.";
222
+ };
223
+ readonly changedConvention: {
224
+ readonly message: "This case reads its expected value from a line this pull request writes, so the same change decides both the behaviour and what counts as correct.";
225
+ readonly suggestion: "Cite a file the pull request leaves alone, or read the value from the description or a requirements file. If the cited file is changed but has nothing to do with this case, say so as the answer.";
226
+ };
227
+ readonly quoteMissing: {
228
+ readonly message: "This case reads its value from the pull request, and its change does not quote the sentence that states the rule.";
229
+ readonly suggestion: "Put the sentence from the title or description on the change as `quote`, as written there. If that sentence states the rule and not the number, put the rule in `derived` and leave `expectedValue` out.";
230
+ };
231
+ readonly valueNotQuoted: {
232
+ readonly message: "This case reads its value from the pull request, and the pull request does not state this value.";
233
+ readonly suggestion: "Quote the sentence that states it, or, if the pull request states a rule, put the rule in `derived` and leave `expectedValue` out; the test computes the value from what it sent.";
234
+ };
235
+ readonly failWithoutValue: {
236
+ readonly message: "This test expects to fail, and no case on the changes it cites states the value it must assert.";
237
+ readonly suggestion: "Put the value the code does not return today on the case this test sends, as `expectedValue`, and name where you read it. If the source states a rule rather than a number, put the rule in `derived` instead. If no source states either, answer with what the test asserts instead and where that comes from.";
238
+ };
239
+ };
240
+ readonly suggestion: "Where does the correct value for this case come from?";
241
+ };
242
+ export declare const REQUIREMENT_SOURCED_CONTRACT: {
243
+ readonly id: "requirementSourced";
244
+ readonly objections: {
245
+ readonly unsourced: {
246
+ readonly message: "Change `{change}` does not name a source the plan accepts: `pr-title`, `pr-description`, `spec:<path>` or `diff`.";
247
+ readonly suggestion: "Set `source` on this change to `pr-title` or `pr-description`, to `spec:<path>` for the requirements file that states it, or to `diff`.";
248
+ };
249
+ readonly unreadable: {
250
+ readonly message: "Change `{change}` cites `{source}`, and the checkout holds no file at that path.";
251
+ readonly suggestion: "Cite the requirements file by the path the checkout holds, or name the source you actually read this change in.";
252
+ };
253
+ readonly fromDiff: {
254
+ readonly message: "This test reports a requirement conflict, and none of the changes it cites was read from the pull request or a requirements file.";
255
+ readonly suggestion: "Cite a change you read in the pull request or in a requirements file, or report this test as something other than a requirement conflict.";
256
+ };
257
+ readonly notNamed: {
258
+ readonly message: "Change `{change}` cites `{source}`, and the pull request names no such file.";
259
+ readonly suggestion: "Cite a file the title or description names. If neither names it, you read the change in the repository, so set `source` to `diff`.";
260
+ };
261
+ readonly emptyPullRequest: {
262
+ readonly message: "Change `{change}` says it was read from the pull request, and the pull request has no title or description.";
263
+ readonly suggestion: "Set `source` to `spec:<path>` for the requirements file that states this change, or to `diff` if only the code states it.";
264
+ };
265
+ readonly notQuoted: {
266
+ readonly message: "This test reports a requirement conflict, and change `{change}` quotes words the pull request does not carry.";
267
+ readonly suggestion: "Quote the requirement as the title or description spells it, word for word. If you cannot find those words there, the change came from somewhere else: say where in `source`.";
268
+ };
269
+ readonly unquoted: {
270
+ readonly message: "This test reports a requirement conflict, and change `{change}` does not quote the requirement.";
271
+ readonly suggestion: "Set `quote` on that change to the sentence in the title or description that states the requirement.";
272
+ };
273
+ readonly noChange: {
274
+ readonly message: "This test reports a requirement conflict and cites no change, so nothing says where the requirement came from.";
275
+ readonly suggestion: "Name in `changes` the change that states the requirement, or report this test as something other than a requirement conflict.";
276
+ };
277
+ };
278
+ readonly suggestion: "Does every change name where you read it, and does every requirement-conflict test cite a change that came from the pull request or a requirements file?";
279
+ };
280
+ export declare const DEFECTS_CONTRACT: {
281
+ readonly id: "defects";
282
+ readonly objections: {
283
+ readonly none: {
284
+ readonly message: "The plan declares no defects, so nothing says the code review happened before the plan.";
285
+ readonly suggestion: "Review the code that serves each change — the handler, the functions it calls to read or write data, the component and what it calls — and list each defect in `defects`. If the review found none, answer in one line what you read.";
286
+ };
287
+ readonly untested: {
288
+ readonly message: "This declared defect has no test in the plan.";
289
+ readonly suggestion: "Add a planned test that cites the defect in `defects` and sets `expected.outcome` to `fail`. A defect in a file this pull request touches gets the test whether or not this pull request introduced it. If something stopped this run from writing the test, answer it and set `blocker` to what stopped it.";
290
+ };
291
+ readonly expectedPass: {
292
+ readonly message: "Every test that cites this defect expects to pass, and a test that passes today proves nothing about a defect.";
293
+ readonly suggestion: "Assert what the fixed code returns, set `expected.outcome` to `fail` with `why`, or answer in one line why a passing test is the proof.";
294
+ };
295
+ readonly unknownId: {
296
+ readonly message: "A planned test cites a defect id that this plan does not declare.";
297
+ readonly suggestion: "Spell the defect's `id` exactly as your own `defects` list gives it — the match is exact apart from surrounding space. Declare the defect if it is missing.";
298
+ };
299
+ readonly conflictWithoutDefect: {
300
+ readonly message: "This test reports a requirement conflict and cites no defect.";
301
+ readonly suggestion: "List the conflict in `defects`: the file and line that contradict the requirement, and a description that quotes the requirement. Cite that id from this test.";
302
+ };
303
+ };
304
+ readonly suggestion: "Which planned test proves each defect the review found, and for one nothing proves, why does it need no test?";
305
+ };
211
306
  /** Fills a contract sentence's `{placeholder}` spans from the run's or the
212
307
  * planned test's own values. A placeholder with no value is left as written rather
213
308
  * than blanked, so a missed key shows up as `{knob}` instead of reading as a gap