@loucompanion/forge-bridge 0.1.3-dev.bd0c67bbf352 → 0.1.3-dev.f2b8822ce953

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/cli/cli.help.d.ts +2 -0
  2. package/dist/cli/cli.help.js +171 -0
  3. package/dist/cli/cli.service.js +5 -183
  4. package/dist/session/phase-completion.artifacts.d.ts +10 -0
  5. package/dist/session/phase-completion.artifacts.js +599 -0
  6. package/dist/session/phase-completion.d.ts +32 -44
  7. package/dist/session/phase-completion.helpers.d.ts +112 -0
  8. package/dist/session/phase-completion.helpers.js +660 -0
  9. package/dist/session/phase-completion.js +28 -3501
  10. package/dist/session/phase-completion.prompts.d.ts +14 -0
  11. package/dist/session/phase-completion.prompts.js +220 -0
  12. package/dist/session/phase-completion.runtime.d.ts +36 -0
  13. package/dist/session/phase-completion.runtime.js +609 -0
  14. package/dist/session/phase-completion.schemas.d.ts +3 -0
  15. package/dist/session/phase-completion.schemas.js +685 -0
  16. package/dist/session/phase-completion.validation.d.ts +34 -0
  17. package/dist/session/phase-completion.validation.js +724 -0
  18. package/dist/session/provider-cli/provider-cli.artifacts.d.ts +93 -0
  19. package/dist/session/provider-cli/provider-cli.artifacts.js +754 -0
  20. package/dist/session/provider-cli/provider-cli.deterministic-payloads.d.ts +12 -0
  21. package/dist/session/provider-cli/provider-cli.deterministic-payloads.js +279 -0
  22. package/dist/session/provider-cli/provider-cli.deterministic.d.ts +12 -0
  23. package/dist/session/provider-cli/provider-cli.deterministic.js +318 -0
  24. package/dist/session/provider-cli/provider-cli.process.d.ts +31 -0
  25. package/dist/session/provider-cli/provider-cli.process.js +265 -0
  26. package/dist/session/provider-cli/provider-cli.prompts.d.ts +32 -0
  27. package/dist/session/provider-cli/provider-cli.prompts.js +387 -0
  28. package/dist/session/provider-cli/provider-cli.qa-sidecars.d.ts +11 -0
  29. package/dist/session/provider-cli/provider-cli.qa-sidecars.js +65 -0
  30. package/dist/session/provider-cli/provider-cli.runtime.d.ts +9 -0
  31. package/dist/session/provider-cli/provider-cli.runtime.js +40 -0
  32. package/dist/session/provider-cli/provider-cli.service.d.ts +6 -53
  33. package/dist/session/provider-cli/provider-cli.service.js +11 -2529
  34. package/dist/session/provider-cli/provider-output-state.d.ts +20 -0
  35. package/dist/session/provider-cli/provider-output-state.js +445 -0
  36. package/dist/shared/process/windows-launch.d.ts +8 -0
  37. package/dist/shared/process/windows-launch.js +38 -0
  38. package/dist/version.d.ts +1 -1
  39. package/dist/version.js +1 -1
  40. package/package.json +1 -1
@@ -0,0 +1,685 @@
1
+ import { decomposeGateFileNames, isDebugCompletionPhase, phaseFiles, qaBlockerReasons, qaGateFileNames, qaScopeAuthorities, qaScopeConfidence, qaScopeEvidence, qaScopeSources, qaScopeSurfaces, resolvedFindingStatusValues } from "./phase-completion.js";
2
+ export function toolDescription(phase, qaGate = null, decomposeGate = null) {
3
+ if (qaGate) {
4
+ return qaGate === "qa_verdict"
5
+ ? "Complete the final Forge QA verdict by writing the canonical QA bundle with pass, needs_rework, or blocked evidence."
6
+ : `Complete the ${qaGate.replace(/_/g, " ")} QA gate by writing ${qaGateFileNames[qaGate]} before continuing to the next QA gate.`;
7
+ }
8
+ if (decomposeGate) {
9
+ return decomposeGate === "decompose_proposal"
10
+ ? "Complete the Forge Decompose proposal by writing decompose.proposal.json with candidate child items and optional structured QA scopes."
11
+ : `Complete the ${decomposeGate.replace(/_/g, " ")} Decompose gate by writing ${decomposeGateFileNames[decomposeGate]} before continuing.`;
12
+ }
13
+ if (isDebugCompletionPhase(phase)) {
14
+ return `Complete the ${phase.replace(/_/g, " ")} Debug gate by writing ${phaseFiles[phase].jsonFileName} with structured bug-fix evidence before continuing.`;
15
+ }
16
+ if (phase === "implementation") {
17
+ return "Complete Forge implementation by writing implementation.completeness.json with scoped changed-file, verification, acceptance-criteria, gap, and next-phase evidence.";
18
+ }
19
+ if (phase === "self_review") {
20
+ return "Complete Forge self-review by writing self-review.report.json with diff review, findings, security, edge-case, design, deterministic-comparison, fixes, and remaining-risk evidence.";
21
+ }
22
+ if (phase === "qa") {
23
+ return "Complete Forge QA by writing qa.plan.json, qa.evidence.json, qa.findings.json, qa.verdict.json, and qa.report.md with structured pass, needs-rework, or blocked evidence.";
24
+ }
25
+ if (phase === "architect") {
26
+ return "Complete Forge Architect planning by writing implementation.plan.json, qa.plan.json, architect.verdict.json, and architect.report.md with a pending-approval plan or explicit revision/blocker reasons.";
27
+ }
28
+ return "Complete Forge validation by writing validation.verdict.json with command, build/test/lint/design, drift, acceptance-criteria, and ready-for-dev-test evidence.";
29
+ }
30
+ export function toolInputSchema(phase, qaGate = null, decomposeGate = null) {
31
+ const baseProperties = {
32
+ status: {
33
+ type: "string",
34
+ enum: ["pass", "needs_rework", "fail", "failed"],
35
+ description: "Use pass only when the phase evidence satisfies Forge's gate. Use needs_rework/fail/failed with explicit blockers or findings otherwise."
36
+ },
37
+ summary: { type: "string", description: "Concise phase summary." },
38
+ report_markdown: { type: "string", description: "Optional human-readable report. Forge will generate a fallback report if omitted." },
39
+ blockers: {
40
+ type: "array",
41
+ description: "Required for non-pass status unless findings, failures, known_gaps, remaining_risks, or commands provide explicit evidence.",
42
+ items: evidenceObjectSchema(["reason"], {
43
+ reason: { type: "string" },
44
+ owner: { type: "string" },
45
+ next_step: { type: "string" }
46
+ })
47
+ }
48
+ };
49
+ if (qaGate && qaGate !== "qa_verdict") {
50
+ return qaGateToolInputSchema(qaGate);
51
+ }
52
+ if (decomposeGate) {
53
+ return decomposeGateToolInputSchema(decomposeGate, baseProperties);
54
+ }
55
+ if (isDebugCompletionPhase(phase)) {
56
+ return debugToolInputSchema(phase, baseProperties);
57
+ }
58
+ if (phase === "implementation") {
59
+ return objectToolSchema({
60
+ ...baseProperties,
61
+ plan: {
62
+ type: "array",
63
+ description: "Required for pass. Ordered implementation steps with status.",
64
+ items: evidenceObjectSchema(["step", "status"], {
65
+ step: { type: "string" },
66
+ status: { type: "string" },
67
+ evidence: { type: "string" }
68
+ })
69
+ },
70
+ changed_files: {
71
+ type: "array",
72
+ description: "Required for pass. Must be non-empty unless known_gaps explains why.",
73
+ items: evidenceObjectSchema(["path", "summary"], {
74
+ path: { type: "string" },
75
+ summary: { type: "string" }
76
+ })
77
+ },
78
+ tests_added_or_updated: {
79
+ type: "array",
80
+ description: "Required for pass. Use an object explaining why no tests were needed for documentation/config-only work if empty would be ambiguous.",
81
+ items: evidenceObjectSchema(["path", "summary"], {
82
+ path: { type: "string" },
83
+ summary: { type: "string" }
84
+ })
85
+ },
86
+ docs_added_or_updated: {
87
+ type: "array",
88
+ description: "Required for pass. Use an object explaining why no docs changed when appropriate.",
89
+ items: evidenceObjectSchema(["path", "summary"], {
90
+ path: { type: "string" },
91
+ summary: { type: "string" }
92
+ })
93
+ },
94
+ verification: {
95
+ type: "array",
96
+ description: "Implementation verification evidence. Objects should include command, exit_code, status, and summary when command-based.",
97
+ items: commandEvidenceSchema()
98
+ },
99
+ acceptance_criteria: acceptanceCriteriaSchema(),
100
+ known_gaps: {
101
+ type: "array",
102
+ description: "Required for pass, even when empty. If changed_files/tests/docs arrays are empty, explain why here.",
103
+ items: evidenceObjectSchema(["summary"], {
104
+ summary: { type: "string" },
105
+ impact: { type: "string" },
106
+ follow_up: { type: "string" }
107
+ })
108
+ },
109
+ next_phase_context: {
110
+ description: "Required for pass. Context the self-review phase should carry forward."
111
+ },
112
+ resolved_findings: {
113
+ type: "array",
114
+ description: "Required for QA rework. Each entry must use status resolved, disputed, or not_reproduced.",
115
+ items: resolvedFindingSchema()
116
+ }
117
+ }, ["status", "summary"], "For status pass, include plan, changed_files, tests_added_or_updated, docs_added_or_updated, acceptance_criteria, known_gaps, and next_phase_context. Acceptance criteria entries must use status: satisfied plus evidence.");
118
+ }
119
+ if (phase === "self_review") {
120
+ return objectToolSchema({
121
+ ...baseProperties,
122
+ reviewed_diff: { type: "boolean", description: "Required for pass. True after reviewing the implementation diff." },
123
+ findings: {
124
+ type: "array",
125
+ description: "Required for pass, even when empty. Include finding objects for issues discovered during review.",
126
+ items: evidenceObjectSchema(["severity", "summary", "status"], {
127
+ severity: { type: "string" },
128
+ summary: { type: "string" },
129
+ status: { type: "string" },
130
+ file: { type: "string" }
131
+ })
132
+ },
133
+ security_review: reviewSectionSchema(),
134
+ edge_case_review: reviewSectionSchema(),
135
+ design_review: {
136
+ ...reviewSectionSchema(),
137
+ properties: {
138
+ ...reviewSectionProperties(),
139
+ screenshots: { type: "array", items: evidenceObjectSchema(["path"], { path: { type: "string" }, summary: { type: "string" } }) }
140
+ }
141
+ },
142
+ deterministic_comparison: {
143
+ type: "object",
144
+ description: "Required for pass. Evidence that the reviewed diff matches the intended deterministic change.",
145
+ additionalProperties: true,
146
+ properties: {
147
+ status: { type: "string" },
148
+ command_or_tool: { type: "string" },
149
+ notes: { type: "string" }
150
+ }
151
+ },
152
+ fixes_applied: {
153
+ type: "array",
154
+ description: "Required for pass, even when empty.",
155
+ items: evidenceObjectSchema(["summary"], { summary: { type: "string" }, path: { type: "string" } })
156
+ },
157
+ remaining_risks: {
158
+ type: "array",
159
+ description: "Required for pass, even when empty.",
160
+ items: evidenceObjectSchema(["summary"], { summary: { type: "string" }, mitigation: { type: "string" } })
161
+ }
162
+ }, ["status", "summary"], "For status pass, include reviewed_diff, findings, security_review, edge_case_review, design_review, deterministic_comparison, fixes_applied, and remaining_risks.");
163
+ }
164
+ if (phase === "qa") {
165
+ return objectToolSchema({
166
+ ...baseProperties,
167
+ status: {
168
+ type: "string",
169
+ enum: ["pass", "needs_rework", "blocked"],
170
+ description: "Use pass only when QA evidence satisfies acceptance criteria, needs_rework for actionable in-scope defects, or blocked for an inconclusive QA blocker."
171
+ },
172
+ checklist: {
173
+ type: "array",
174
+ description: "QA plan/checklist. Forge writes this to qa.plan.json; a default checklist is generated if omitted.",
175
+ items: { type: "string" }
176
+ },
177
+ tested_acceptance_criteria: {
178
+ type: "array",
179
+ description: "Required and non-empty for pass/needs_rework. Criteria actually tested by QA.",
180
+ items: { type: "string" }
181
+ },
182
+ commands: {
183
+ type: "array",
184
+ description: "Command evidence written to qa.evidence.json.",
185
+ items: commandEvidenceSchema()
186
+ },
187
+ browser_checks: {
188
+ type: "array",
189
+ description: "Browser/manual UI checks with URL or route, action, observed result, screenshot/trace references, console/network notes, and status.",
190
+ items: evidenceObjectSchema(["summary", "status"], {
191
+ summary: { type: "string" },
192
+ url: { type: "string" },
193
+ route: { type: "string" },
194
+ status: { type: "string" },
195
+ screenshot: { type: "string" },
196
+ trace: { type: "string" }
197
+ })
198
+ },
199
+ sidecars: {
200
+ type: "array",
201
+ description: "Optional QA sidecar files already written under .forge/work-items/<id>/artifacts/qa/ with path/content_hash/bytes metadata.",
202
+ items: evidenceObjectSchema(["path", "content_hash", "bytes"], {
203
+ path: { type: "string" },
204
+ content_hash: { type: "string" },
205
+ bytes: { type: "number" }
206
+ })
207
+ },
208
+ notes: { type: "string", description: "Additional QA evidence notes." },
209
+ evidence: {
210
+ type: "object",
211
+ description: "Optional wrapper containing commands, browser_checks, and notes when a client prefers nested evidence."
212
+ },
213
+ findings: {
214
+ type: "array",
215
+ description: "QA findings. needs_rework requires at least one actionable in_scope finding.",
216
+ items: evidenceObjectSchema(["id", "severity", "scope", "title"], {
217
+ id: { type: "string" },
218
+ severity: { type: "string", enum: ["critical", "major", "minor"] },
219
+ scope: { type: "string", enum: ["in_scope", "out_of_scope", "unclear"] },
220
+ title: { type: "string" },
221
+ acceptance_criteria: { type: "array", items: { type: "string" } },
222
+ repro_steps: { type: "array", items: { type: "string" } },
223
+ expected: { type: "string" },
224
+ actual: { type: "string" },
225
+ evidence: { type: "object" },
226
+ likely_area: { type: "array", items: { type: "string" } },
227
+ required_fix: { type: "string" },
228
+ required_regression: { type: "string" }
229
+ })
230
+ },
231
+ candidate_bugs: {
232
+ type: "array",
233
+ description: "Out-of-scope/follow-up bug candidates. They do not block the current item unless attached as in-scope findings.",
234
+ items: evidenceObjectSchema(["id", "title", "suggested_action"], {
235
+ id: { type: "string" },
236
+ title: { type: "string" },
237
+ severity: { type: "string" },
238
+ scope_reason: { type: "string" },
239
+ repro_steps: { type: "array", items: { type: "string" } },
240
+ evidence: { type: "object" },
241
+ suggested_action: { type: "string", enum: ["create_bug", "create_follow_up", "accept_risk", "attach_to_current_item"] }
242
+ })
243
+ },
244
+ policy_warnings: {
245
+ type: "array",
246
+ description: "Non-blocking Forge policy or scope warnings surfaced with the QA verdict.",
247
+ items: evidenceObjectSchema(["code", "message"], {
248
+ code: { type: "string" },
249
+ message: { type: "string" },
250
+ severity: { type: "string" },
251
+ evidence: { type: "object" }
252
+ })
253
+ },
254
+ blocker_reason: {
255
+ type: "string",
256
+ enum: [...qaBlockerReasons],
257
+ description: "Required when status is blocked."
258
+ }
259
+ }, ["status", "summary"], "For pass, include tested_acceptance_criteria plus concrete command/browser/note evidence. For needs_rework, include at least one actionable in_scope finding. For blocked, include blocker_reason.");
260
+ }
261
+ if (phase === "architect") {
262
+ return objectToolSchema({
263
+ status: {
264
+ type: "string",
265
+ enum: ["pass", "needs_revision", "blocked"],
266
+ description: "Use pass only for a complete pending-approval Architect plan. Use needs_revision for fixable story/design/repo gaps, or blocked for missing required context or decisions."
267
+ },
268
+ summary: { type: "string", description: "Concise Architect plan verdict summary." },
269
+ implementation_plan: {
270
+ type: "object",
271
+ description: "Required for pass. Top-level fields inside this object must include schema_version, plan_id, phases, tasks, dependencies, and phase_gates.",
272
+ additionalProperties: true,
273
+ properties: {
274
+ schema_version: { type: "number", enum: [1] },
275
+ plan_id: { type: "string" },
276
+ phases: {
277
+ type: "array",
278
+ description: "Required top-level implementation_plan.phases array.",
279
+ items: { type: "object", additionalProperties: true }
280
+ },
281
+ tasks: {
282
+ type: "array",
283
+ description: "Required Develop child task list. Each task must include id, a non-empty tests array, and should include verification_scope for task-level QA evidence. Do not put parent-story QA/sign-off, child-count/materialization inventory, parent plan artifact checks, or final acceptance proof here; those belong in qa_plan.final_qa.",
284
+ items: {
285
+ type: "object",
286
+ additionalProperties: true,
287
+ properties: {
288
+ id: { type: "string" },
289
+ title: { type: "string" },
290
+ verification_scope: qaScopeSchema("Task-level QA/verification scope for the materialized child task."),
291
+ tests: {
292
+ type: "array",
293
+ items: { type: "object", additionalProperties: true }
294
+ }
295
+ }
296
+ }
297
+ },
298
+ dependencies: {
299
+ type: "array",
300
+ description: "Required dependency edge array. Use [] when no task dependencies exist. Each edge should use before and after task ids; from/to are accepted only as aliases and normalized before writing artifacts.",
301
+ items: {
302
+ type: "object",
303
+ additionalProperties: true,
304
+ properties: {
305
+ id: { type: "string" },
306
+ before: { type: "string", description: "Task id that must complete first." },
307
+ after: { type: "string", description: "Task id that depends on before." },
308
+ from: { type: "string", description: "Alias for before." },
309
+ to: { type: "string", description: "Alias for after." },
310
+ type: { type: "string" },
311
+ rationale: { type: "string" }
312
+ }
313
+ }
314
+ },
315
+ phase_gates: {
316
+ type: "array",
317
+ description: "Required non-empty phase gate array.",
318
+ items: { type: "object", additionalProperties: true }
319
+ }
320
+ }
321
+ },
322
+ qa_plan: {
323
+ type: "object",
324
+ description: "Required for pass. Must include schema_version: 1 and non-empty final_qa coverage.",
325
+ additionalProperties: true,
326
+ properties: {
327
+ schema_version: { type: "number", enum: [1] },
328
+ final_qa: {
329
+ type: "array",
330
+ description: "Required non-empty final QA coverage array.",
331
+ items: { type: "object", additionalProperties: true }
332
+ }
333
+ }
334
+ },
335
+ report_markdown: { type: "string", description: "Human-readable Architect report. Use report as an alias only when the client cannot send report_markdown." },
336
+ report: { type: "string", description: "Alias for report_markdown." },
337
+ blockers: baseProperties.blockers,
338
+ revision_questions: {
339
+ type: "array",
340
+ description: "Required for needs_revision when blockers are not used.",
341
+ items: { type: "string" }
342
+ },
343
+ revision_reasons: {
344
+ type: "array",
345
+ description: "Concrete reasons that prevent pass status.",
346
+ items: { type: "string" }
347
+ }
348
+ }, ["status", "summary"], "For status pass, include implementation_plan and qa_plan at the top level of the MCP tool arguments. implementation_plan.phases must be a top-level array inside implementation_plan, not nested under another object.");
349
+ }
350
+ return objectToolSchema({
351
+ ...baseProperties,
352
+ commands: {
353
+ type: "array",
354
+ description: "Required for pass. Include at least one deterministic validation command with exit_code: 0 and status: pass.",
355
+ items: commandEvidenceSchema()
356
+ },
357
+ acceptance_criteria: acceptanceCriteriaSchema(),
358
+ build_status: { type: "string", description: "Required for pass and must be pass." },
359
+ test_status: { type: "string", description: "Required for pass and must be pass." },
360
+ lint_status: { type: "string", description: "Required for pass. pass/fail/needs_rework/not_applicable." },
361
+ design_status: { type: "string", description: "Required for pass. pass/fail/needs_rework/not_applicable." },
362
+ drift_checks: {
363
+ type: "array",
364
+ description: "Required for pass. Deterministic checks for unintended drift.",
365
+ items: evidenceObjectSchema(["command_or_tool", "status", "summary"], {
366
+ command_or_tool: { type: "string" },
367
+ status: { type: "string" },
368
+ summary: { type: "string" }
369
+ })
370
+ },
371
+ ready_for_dev_test: { type: "boolean", description: "Required for pass and must be true." }
372
+ }, ["status", "summary"], "For status pass, commands must include at least one passing deterministic command, acceptance criteria entries must use status: satisfied plus evidence, and ready_for_dev_test must be true.");
373
+ }
374
+ function decomposeGateToolInputSchema(gate, baseProperties) {
375
+ if (gate !== "decompose_proposal") {
376
+ return objectToolSchema({
377
+ ...baseProperties,
378
+ findings: {
379
+ type: "array",
380
+ description: "Optional issues, constraints, or risks discovered in this Decompose gate.",
381
+ items: { type: "object", additionalProperties: true }
382
+ }
383
+ }, ["status", "summary"], `Record the ${gate.replace(/_/g, " ")} Decompose gate before continuing.`);
384
+ }
385
+ const candidateSchema = {
386
+ type: "object",
387
+ additionalProperties: true,
388
+ properties: {
389
+ origin: { type: "string" },
390
+ key: { type: "string" },
391
+ title: { type: "string" },
392
+ summary: { type: "string" },
393
+ description: { type: "string" },
394
+ acceptanceCriteria: { type: "string" },
395
+ planningPacket: { type: "string" },
396
+ sortOrder: { type: "number" },
397
+ scheduleIndex: { type: "number", description: "Feature-to-Story total schedule order for dependency-safe execution." },
398
+ schedule_index: { type: "number", description: "Snake_case alias for scheduleIndex." },
399
+ waveIndex: { type: "number", description: "Feature-to-Story parallel wave number." },
400
+ wave_index: { type: "number", description: "Snake_case alias for waveIndex." },
401
+ featureScheduleIndex: { type: "number", description: "Optional ordering within the Feature." },
402
+ feature_schedule_index: { type: "number", description: "Snake_case alias for featureScheduleIndex." },
403
+ expectedOutput: { type: "string", description: "Durable output this Story should produce for dependents." },
404
+ expected_output: { type: "string", description: "Snake_case alias for expectedOutput." },
405
+ handoffSummary: { type: "string", description: "How dependent Stories should consume this Story output." },
406
+ handoff_summary: { type: "string", description: "Snake_case alias for handoffSummary." },
407
+ dependencyReason: { type: "string", description: "Why this Story depends on its dependsOn predecessors." },
408
+ dependency_reason: { type: "string", description: "Snake_case alias for dependencyReason." },
409
+ points: { type: "number" },
410
+ dependsOn: { type: "array", items: { type: "string" } },
411
+ estimatedQaScope: qaScopeSchema("Work Order-to-Feature planning QA estimate. This is not dispatch-authoritative QA scope."),
412
+ estimated_qa_scope: qaScopeSchema("Work Order-to-Feature planning QA estimate. Snake_case alias for estimatedQaScope."),
413
+ qaScope: qaScopeSchema("Feature-to-Story QA scope that becomes Story-level QA scope after proposal approval."),
414
+ qa_scope: qaScopeSchema("Feature-to-Story QA scope. Snake_case alias for qaScope.")
415
+ }
416
+ };
417
+ const proposalSchema = {
418
+ type: "object",
419
+ additionalProperties: true,
420
+ description: "Final Decompose proposal JSON. Must include candidates, and may include blockedReason.",
421
+ properties: {
422
+ summary: { type: "string" },
423
+ blockedReason: { type: "string" },
424
+ blocked_reason: { type: "string" },
425
+ candidates: {
426
+ type: "array",
427
+ items: candidateSchema
428
+ }
429
+ }
430
+ };
431
+ return objectToolSchema({
432
+ ...baseProperties,
433
+ status: {
434
+ type: "string",
435
+ enum: ["pass", "needs_rework", "blocked"],
436
+ description: "Use pass only when the Decompose proposal is ready for user sign-off."
437
+ },
438
+ proposal_json: proposalSchema,
439
+ decomposition_json: {
440
+ ...proposalSchema,
441
+ description: "Alias for proposal_json when a client cannot send proposal_json."
442
+ }
443
+ }, ["status", "summary"], "For the final Decompose proposal, include proposal_json with a candidates array. Work Order-to-Feature candidates may include estimatedQaScope/estimated_qa_scope; Feature-to-Story candidates may include qaScope/qa_scope.");
444
+ }
445
+ function debugToolInputSchema(phase, baseProperties) {
446
+ if (phase === "debug_repro") {
447
+ return objectToolSchema({
448
+ ...baseProperties,
449
+ repro_steps: {
450
+ type: "array",
451
+ description: "Required for pass. Minimal reproduction or characterization steps.",
452
+ items: { type: "string" }
453
+ },
454
+ observed: { type: "string", description: "Observed failure behavior." },
455
+ expected: { type: "string", description: "Expected behavior." },
456
+ evidence: {
457
+ type: "array",
458
+ description: "Required for pass. Concrete logs, commands, screenshots, file refs, or characterization notes.",
459
+ items: { type: "object", additionalProperties: true }
460
+ }
461
+ }, ["status", "summary"], "For status pass, include repro_steps, observed, expected, and evidence.");
462
+ }
463
+ if (phase === "debug_root_cause") {
464
+ return objectToolSchema({
465
+ ...baseProperties,
466
+ root_cause: { type: "string", description: "Likely root cause tied to code or config evidence." },
467
+ evidence: {
468
+ type: "array",
469
+ description: "Required for pass. Concrete code/log/test evidence supporting the root cause.",
470
+ items: { type: "object", additionalProperties: true }
471
+ },
472
+ eliminated_alternatives: {
473
+ type: "array",
474
+ description: "Required for pass. Plausible alternatives checked or ruled out.",
475
+ items: { type: "object", additionalProperties: true }
476
+ },
477
+ risk_area: { type: "string", description: "Subsystem or behavior most at risk from the fix." }
478
+ }, ["status", "summary"], "For status pass, include root_cause, evidence, eliminated_alternatives, and risk_area.");
479
+ }
480
+ if (phase === "debug_fix") {
481
+ return objectToolSchema({
482
+ ...baseProperties,
483
+ changed_files: {
484
+ type: "array",
485
+ description: "Required for pass. Files changed by the bug fix.",
486
+ items: evidenceObjectSchema(["path", "summary"], { path: { type: "string" }, summary: { type: "string" } })
487
+ },
488
+ tests_added_or_updated: {
489
+ type: "array",
490
+ description: "Required for pass. Regression tests or checks added/updated, or justify omission in remaining_risks.",
491
+ items: evidenceObjectSchema(["path", "summary"], { path: { type: "string" }, summary: { type: "string" } })
492
+ },
493
+ remaining_risks: {
494
+ type: "array",
495
+ description: "Required for pass, even when empty. Include why no regression coverage changed when tests_added_or_updated is empty.",
496
+ items: evidenceObjectSchema(["summary"], { summary: { type: "string" }, mitigation: { type: "string" } })
497
+ },
498
+ resolved_findings: {
499
+ type: "array",
500
+ description: "Required for QA rework. Each entry must use status resolved, disputed, or not_reproduced.",
501
+ items: resolvedFindingSchema()
502
+ }
503
+ }, ["status", "summary"], "For status pass, include changed_files, tests_added_or_updated, and remaining_risks.");
504
+ }
505
+ return objectToolSchema({
506
+ ...baseProperties,
507
+ commands: {
508
+ type: "array",
509
+ description: "Required for pass. Targeted regression/repro-negative commands with exit_code: 0 and status: pass.",
510
+ items: commandEvidenceSchema()
511
+ },
512
+ required_regressions: {
513
+ type: "array",
514
+ description: "Required for pass. Repro-negative and regression coverage checklist.",
515
+ items: { type: "string" }
516
+ },
517
+ build_status: { type: "string", description: "Required for pass. pass or not_applicable." },
518
+ test_status: { type: "string", description: "Required for pass. pass or not_applicable." },
519
+ repro_negative: { type: "boolean", description: "Required for pass and must be true after the original repro no longer fails." },
520
+ qa_ready: { type: "boolean", description: "Required for pass and must be true." }
521
+ }, ["status", "summary"], "For status pass, commands must include at least one passing deterministic debug regression check, repro_negative must be true, and qa_ready must be true.");
522
+ }
523
+ function qaGateToolInputSchema(gate) {
524
+ const baseProperties = {
525
+ status: {
526
+ type: "string",
527
+ enum: ["pass", "skipped", "blocked"],
528
+ description: "Use pass when this QA gate produced evidence, skipped when not applicable, or blocked when a prerequisite prevents this gate."
529
+ },
530
+ summary: { type: "string", description: "Concise QA gate summary." },
531
+ reason: { type: "string", description: "Required for skipped or blocked gates." },
532
+ blocker_reason: {
533
+ type: "string",
534
+ enum: [...qaBlockerReasons],
535
+ description: "Required when status is blocked."
536
+ }
537
+ };
538
+ if (gate === "qa_plan") {
539
+ return objectToolSchema({
540
+ ...baseProperties,
541
+ checklist: {
542
+ type: "array",
543
+ description: "Required for pass. QA checklist entries planned for this work item.",
544
+ items: { type: "string" }
545
+ },
546
+ acceptance_criteria: {
547
+ type: "array",
548
+ description: "Acceptance criteria selected for QA.",
549
+ items: { type: "string" }
550
+ }
551
+ }, ["status", "summary"], "Record the QA plan gate. Pass requires a non-empty checklist.");
552
+ }
553
+ if (gate === "qa_findings") {
554
+ return objectToolSchema({
555
+ ...baseProperties,
556
+ findings: {
557
+ type: "array",
558
+ description: "Normalized in-scope and out-of-scope QA findings.",
559
+ items: { type: "object", additionalProperties: true }
560
+ },
561
+ candidate_bugs: {
562
+ type: "array",
563
+ description: "Out-of-scope or follow-up candidates.",
564
+ items: { type: "object", additionalProperties: true }
565
+ }
566
+ }, ["status", "summary"], "Record normalized QA findings. Pass or skipped requires findings and candidate_bugs arrays, even when empty.");
567
+ }
568
+ return objectToolSchema({
569
+ ...baseProperties,
570
+ commands: {
571
+ type: "array",
572
+ description: "Command evidence for this gate when applicable.",
573
+ items: commandEvidenceSchema()
574
+ },
575
+ browser_checks: {
576
+ type: "array",
577
+ description: "Browser/manual evidence for this gate when applicable.",
578
+ items: { type: "object", additionalProperties: true }
579
+ },
580
+ notes: { type: "string", description: "Evidence notes for this gate." }
581
+ }, ["status", "summary"], "Record this QA evidence gate. Use skipped with a reason when the gate is not applicable.");
582
+ }
583
+ function objectToolSchema(properties, required, description) {
584
+ return {
585
+ type: "object",
586
+ additionalProperties: true,
587
+ description,
588
+ properties,
589
+ required: [...required]
590
+ };
591
+ }
592
+ function evidenceObjectSchema(required, properties) {
593
+ return {
594
+ type: "object",
595
+ additionalProperties: true,
596
+ properties,
597
+ required: [...required]
598
+ };
599
+ }
600
+ function qaScopeSchema(description) {
601
+ return {
602
+ type: "object",
603
+ additionalProperties: false,
604
+ description,
605
+ properties: {
606
+ schema_version: { type: "number", enum: [1] },
607
+ surface: { type: "array", items: { type: "string", enum: [...qaScopeSurfaces] } },
608
+ required_evidence: { type: "array", items: { type: "string", enum: [...qaScopeEvidence] } },
609
+ not_applicable: { type: "array", items: { type: "string", enum: [...qaScopeEvidence] } },
610
+ browser_required: { type: "boolean" },
611
+ api_required: { type: "boolean" },
612
+ source: { type: "string", enum: [...qaScopeSources] },
613
+ authority: { type: "string", enum: [...qaScopeAuthorities] },
614
+ confidence: { type: "string", enum: [...qaScopeConfidence] },
615
+ confirmed_by_user: { type: "boolean" },
616
+ locked: { type: "boolean" },
617
+ derived_from: {
618
+ type: "object",
619
+ additionalProperties: false,
620
+ properties: {
621
+ work_item_id: { type: "string" },
622
+ mode: { type: "string" }
623
+ }
624
+ },
625
+ rationale: { type: "string" }
626
+ },
627
+ required: ["schema_version", "required_evidence", "not_applicable"]
628
+ };
629
+ }
630
+ function commandEvidenceSchema() {
631
+ return evidenceObjectSchema(["command", "exit_code", "status", "summary"], {
632
+ command: { type: "string" },
633
+ exit_code: { type: "number" },
634
+ status: { type: "string" },
635
+ summary: { type: "string" }
636
+ });
637
+ }
638
+ function resolvedFindingSchema() {
639
+ return evidenceObjectSchema([
640
+ "qa_id",
641
+ "status",
642
+ "summary",
643
+ "evidence",
644
+ "changed_files",
645
+ "tests_added_or_updated",
646
+ "regression_evidence"
647
+ ], {
648
+ qa_id: { type: "string" },
649
+ status: {
650
+ type: "string",
651
+ enum: [...resolvedFindingStatusValues],
652
+ description: "Use resolved when fixed, disputed when the QA finding is incorrect/out-of-scope, or not_reproduced when retesting cannot reproduce it."
653
+ },
654
+ summary: { type: "string" },
655
+ evidence: { description: "Concrete evidence for the resolution." },
656
+ changed_files: { type: "array", description: "Required and non-empty when status is resolved." },
657
+ tests_added_or_updated: { type: "array", description: "Required and non-empty when status is resolved." },
658
+ regression_evidence: { description: "Concrete regression evidence for this QA finding." }
659
+ });
660
+ }
661
+ function acceptanceCriteriaSchema() {
662
+ return {
663
+ type: "array",
664
+ description: "Required for pass. Every entry must be an object with status: satisfied and non-empty evidence.",
665
+ items: evidenceObjectSchema(["criterion", "evidence", "status"], {
666
+ criterion: { type: "string" },
667
+ evidence: { type: "string" },
668
+ status: { type: "string", enum: ["satisfied"] }
669
+ })
670
+ };
671
+ }
672
+ function reviewSectionSchema() {
673
+ return {
674
+ type: "object",
675
+ description: "Required for pass. Use status pass/fail/needs_rework/not_applicable plus notes.",
676
+ additionalProperties: true,
677
+ properties: reviewSectionProperties()
678
+ };
679
+ }
680
+ function reviewSectionProperties() {
681
+ return {
682
+ status: { type: "string" },
683
+ notes: { type: "string" }
684
+ };
685
+ }