@boyingliu01/xp-gate 0.32.1 → 0.32.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 (47) hide show
  1. package/adapters/powershell.sh +33 -7
  2. package/gate-3.sh +37 -2
  3. package/hooks/gate-3.sh +37 -2
  4. package/lib/mattpocock-license-verdict.json +1 -1
  5. package/mock-policy/AGENTS.md +3 -3
  6. package/mutation/AGENTS.md +3 -3
  7. package/package.json +1 -1
  8. package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
  9. package/plugins/claude-code/skills/delphi-review/AGENTS.md +3 -3
  10. package/plugins/claude-code/skills/sprint-flow/AGENTS.md +3 -3
  11. package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +3 -3
  12. package/plugins/dsh/README.md +51 -2
  13. package/plugins/dsh/client/client.js +760 -0
  14. package/plugins/dsh/lib/client.js +760 -0
  15. package/plugins/dsh/lib/delphi-settings.d.ts +52 -0
  16. package/plugins/dsh/lib/delphi-settings.d.ts.map +1 -0
  17. package/plugins/dsh/lib/delphi-settings.js +76 -0
  18. package/plugins/dsh/lib/hooks.d.ts +14 -1
  19. package/plugins/dsh/lib/hooks.d.ts.map +1 -1
  20. package/plugins/dsh/lib/hooks.js +44 -5
  21. package/plugins/dsh/lib/index.d.ts +63 -0
  22. package/plugins/dsh/lib/index.d.ts.map +1 -1
  23. package/plugins/dsh/lib/index.js +126 -21
  24. package/plugins/dsh/package.json +13 -3
  25. package/plugins/dsh/scripts/copy-client.cjs +32 -0
  26. package/plugins/dsh/skills/delphi-review/AGENTS.md +3 -3
  27. package/plugins/dsh/skills/sprint-flow/AGENTS.md +3 -3
  28. package/plugins/dsh/skills/test-specification-alignment/AGENTS.md +3 -3
  29. package/plugins/dsh/src/client.test.ts +231 -0
  30. package/plugins/dsh/src/delphi-settings.test.ts +101 -0
  31. package/plugins/dsh/src/delphi-settings.ts +111 -0
  32. package/plugins/dsh/src/hooks.test.ts +48 -0
  33. package/plugins/dsh/src/hooks.ts +45 -5
  34. package/plugins/dsh/src/index.test.ts +3 -2
  35. package/plugins/dsh/src/index.ts +147 -25
  36. package/plugins/opencode/package.json +1 -1
  37. package/plugins/opencode/skills/delphi-review/AGENTS.md +3 -3
  38. package/plugins/opencode/skills/sprint-flow/AGENTS.md +3 -3
  39. package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +3 -3
  40. package/plugins/qoder/plugin.json +1 -1
  41. package/plugins/qoder/skills/delphi-review/AGENTS.md +3 -3
  42. package/plugins/qoder/skills/sprint-flow/AGENTS.md +3 -3
  43. package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +3 -3
  44. package/principles/AGENTS.md +3 -3
  45. package/skills/delphi-review/AGENTS.md +3 -3
  46. package/skills/sprint-flow/AGENTS.md +3 -3
  47. package/skills/test-specification-alignment/AGENTS.md +3 -3
@@ -11,7 +11,17 @@ import type { ShellDialect } from "./command.js"
11
11
  import { GATE_WHITELIST, buildCommand, detectDialect, resolveTarget } from "./command.js"
12
12
  import { runXpGate } from "./gate-runner.js"
13
13
  import type { ResolvedDelphiConfig } from "./delphi-config.js"
14
- import { DelphiSetupError, listCredentialReferences, loadDelphiConfig, toDelphiOptions } from "./delphi-config.js"
14
+ import {
15
+ DelphiSetupError,
16
+ expandEnvDetailed,
17
+ listCredentialReferences,
18
+ loadDelphiConfig,
19
+ toDelphiOptions,
20
+ } from "./delphi-config.js"
21
+ import type { DelphiOptions } from "./delphi.js"
22
+ import { DelphiConfigError } from "./delphi.js"
23
+ import type { DelphiSettingsLayer } from "./delphi-settings.js"
24
+ import { seatsFromSettings } from "./delphi-settings.js"
15
25
  import type { ReviewOutcome } from "./delphi-run.js"
16
26
  import { formatOutcome, runReview } from "./delphi-run.js"
17
27
  import { buildEvidence } from "./evidence.js"
@@ -200,46 +210,120 @@ interface ReviewRequest {
200
210
  readonly resolveApiKey: ApiKeyResolver
201
211
  readonly signal: AbortSignal | undefined
202
212
  readonly writeEvidence: boolean
213
+ /** Seats from the settings page; empty means "read .delphi-config.json". */
214
+ readonly settings: DelphiSettingsLayer | undefined
215
+ }
216
+
217
+ /**
218
+ * Build the review options from whichever source is configured.
219
+ *
220
+ * The settings page wins when it holds seats: it is the more recent edit, and a
221
+ * user who just saved three models expects the next run to use them. When it holds
222
+ * none, `.delphi-config.json` applies exactly as before — which is what keeps every
223
+ * existing project working without opening the page.
224
+ *
225
+ * Both paths end in the same `validateSeats`, so neither can produce a panel the
226
+ * skill would reject.
227
+ */
228
+ async function reviewOptions(
229
+ request: ReviewRequest,
230
+ resolveApiKey: ApiKeyResolver,
231
+ ): Promise<{ options: DelphiOptions; provider: string; described: string; cfg?: ResolvedDelphiConfig }> {
232
+ const fromSettings = seatsFromSettings(request.settings)
233
+ if (fromSettings) {
234
+ // The seat list is already validated; only the key still has to be resolved,
235
+ // and only the credential store (not this process's environment) knows it.
236
+ const key = await resolveExpressionKey(fromSettings.apiKeyExpression, resolveApiKey)
237
+ return {
238
+ options: {
239
+ baseUrl: fromSettings.baseUrl,
240
+ apiKey: key,
241
+ seats: fromSettings.seats,
242
+ thresholdPercent: fromSettings.thresholdPercent,
243
+ maxRounds: request.maxRounds ?? fromSettings.maxRounds,
244
+ },
245
+ provider: "settings",
246
+ described: fromSettings.seats.map((s) => s.model).join(", "),
247
+ }
248
+ }
249
+
250
+ // Resolve credentials BEFORE loading the config: the loader is synchronous and
251
+ // throws as soon as a referenced variable has no value. Reading the reference
252
+ // names first turns "the DSH process never inherited WHALECLOUD_API_KEY" from a
253
+ // hard failure into a lookup in the credential store.
254
+ const apiKeyOverrides: Record<string, string> = {}
255
+ for (const name of listCredentialReferences(request.cwd)) {
256
+ const value = await resolveApiKey(name)
257
+ if (value !== undefined && value.length > 0) apiKeyOverrides[name] = value
258
+ }
259
+ const cfg = loadDelphiConfig(request.cwd, process.env, undefined, apiKeyOverrides)
260
+ const options = toDelphiOptions(cfg)
261
+ return {
262
+ options: { ...options, maxRounds: request.maxRounds ?? cfg.maxRounds },
263
+ provider: cfg.provider,
264
+ described: cfg.seats.map((s) => s.model).join(", "),
265
+ cfg,
266
+ }
267
+ }
268
+
269
+ /**
270
+ * Resolve a settings-supplied `api_key` expression to a usable token.
271
+ *
272
+ * Accepts the same two placeholder spellings the JSON loader accepts. A literal
273
+ * value is rejected rather than used: a bearer token typed into a settings field is
274
+ * a credential written to a profile file in plain text.
275
+ */
276
+ async function resolveExpressionKey(expression: string, resolveApiKey: ApiKeyResolver): Promise<string> {
277
+ const placeholder = findPlaceholderName(expression)
278
+ if (placeholder === undefined) {
279
+ if (expression.trim() !== "") {
280
+ throw new DelphiSetupError(
281
+ "The Delphi settings hold a literal api key. Use a credential reference instead, " +
282
+ 'e.g. "${WHALECLOUD_API_KEY}", so the token never lands in the profile file.',
283
+ )
284
+ }
285
+ throw new DelphiSetupError('The Delphi settings have no api key. Set it to "${YOUR_KEY_VAR}".')
286
+ }
287
+ const fromStore = await resolveApiKey(placeholder)
288
+ if (fromStore !== undefined && fromStore.length > 0) return fromStore
289
+ const fromEnv = expandEnvDetailed(expression, process.env)
290
+ if (fromEnv.value.trim() !== "") return fromEnv.value
291
+ throw new DelphiSetupError(
292
+ `API key variable ${placeholder} has no value. Set it, or store it in DSH's credential store.`,
293
+ )
294
+ }
295
+
296
+ /** The first `${VAR}` / `{env:VAR}` name in a config value, or undefined. */
297
+ function findPlaceholderName(value: string): string | undefined {
298
+ const shell = /\$\{([A-Za-z_][A-Za-z0-9_]*)\}/.exec(value)
299
+ if (shell) return shell[1]
300
+ const envStyle = /\{env:([A-Za-z_][A-Za-z0-9_]*)\}/.exec(value)
301
+ return envStyle ? envStyle[1] : undefined
203
302
  }
204
303
 
205
304
  async function runDelphiReview(subject: string, request: ReviewRequest): Promise<string> {
206
- const { addressed, maxRounds, cwd, resolveApiKey, signal, writeEvidence } = request
305
+ const { addressed, cwd, resolveApiKey, signal, writeEvidence } = request
207
306
  try {
208
- // Resolve credentials BEFORE loading the config: the loader is synchronous and
209
- // throws as soon as a referenced variable has no value. Reading the reference
210
- // names first turns "the DSH process never inherited WHALECLOUD_API_KEY" from a
211
- // hard failure into a lookup in the credential store.
212
- const apiKeyOverrides: Record<string, string> = {}
213
- for (const name of listCredentialReferences(cwd)) {
214
- const value = await resolveApiKey(name)
215
- if (value !== undefined && value.length > 0) apiKeyOverrides[name] = value
216
- }
217
- const cfg = loadDelphiConfig(cwd, process.env, undefined, apiKeyOverrides)
218
- const options = toDelphiOptions(cfg)
219
- const outcome = await runReview(
220
- { ...options, maxRounds: maxRounds ?? cfg.maxRounds },
221
- subject,
222
- addressed,
223
- signal,
224
- )
225
- const seats = cfg.seats.map((s) => s.model).join(", ")
226
- const suffix = writeEvidence
307
+ const { options, provider, described, cfg } = await reviewOptions(request, resolveApiKey)
308
+ const outcome = await runReview(options, subject, addressed, signal)
309
+ const suffix = writeEvidence && cfg !== undefined
227
310
  ? await writeWalkthroughEvidence(cwd, cfg, outcome)
228
311
  : ""
229
- return `${formatOutcome(outcome)}\n\nProvider: ${cfg.provider}\nSeats: ${seats}${suffix}`
312
+ return `${formatOutcome(outcome)}\n\nProvider: ${provider}\nSeats: ${described}${suffix}`
230
313
  } catch (e) {
231
314
  // Config problems are the common failure; report them as guidance
232
315
  // rather than a stack trace.
233
316
  if (e instanceof DelphiSetupError) return `Delphi review not started: ${e.message}`
317
+ if (e instanceof DelphiConfigError) return `Delphi review not started: ${e.message}`
234
318
  throw e
235
319
  }
236
320
  }
237
321
 
238
- function delphiReviewTool(resolveApiKey: ApiKeyResolver) {
322
+ function delphiReviewTool(resolveApiKey: ApiKeyResolver, settings: DelphiSettingsLayer | undefined) {
239
323
  return defineTool({
240
324
  name: "delphi-review",
241
325
  description:
242
- "Run a Delphi cross-review of a subject (a diff, a design, a spec) using three DISTINCT models from an external OpenAI-compatible provider configured in .delphi-config.json. Round 1 is anonymous and concurrent; later rounds feed back the aggregate until the approval ratio reaches the configured threshold (default 90%) or the round cap (default 5). Use this whenever a change needs independent multi-model review rather than a single model's opinion.",
326
+ "Run a Delphi cross-review of a subject (a diff, a design, a spec) using three DISTINCT models from an external OpenAI-compatible provider. The seats come from the plugin's settings page (Plugins -> xp-gate -> Configure) when it holds any, otherwise from .delphi-config.json. Round 1 is anonymous and concurrent; later rounds feed back the aggregate until the approval ratio reaches the configured threshold (default 90%) or the round cap (default 5). Use this whenever a change needs independent multi-model review rather than a single model's opinion.",
243
327
  parameters: {
244
328
  subject: {
245
329
  type: "string",
@@ -280,6 +364,7 @@ function delphiReviewTool(resolveApiKey: ApiKeyResolver) {
280
364
  resolveApiKey,
281
365
  signal: exec.signal,
282
366
  writeEvidence: args.writeEvidence ?? false,
367
+ settings,
283
368
  })
284
369
  },
285
370
  })
@@ -337,14 +422,36 @@ interface XpGatePluginConfig {
337
422
  readonly principlesCheck: "auto" | "off"
338
423
  /** Sprint write guard: auto = deny until APPROVED; off = no hook. */
339
424
  readonly delphiGuard: "auto" | "off"
425
+ /** Delphi seats edited on the Plugins page; empty means "use .delphi-config.json". */
426
+ readonly delphi: DelphiSettingsLayer | undefined
340
427
  }
341
428
 
429
+ /**
430
+ * One Delphi seat, as the settings page stores it.
431
+ *
432
+ * `model` is a free string rather than an enum: the catalog the page offers comes
433
+ * from the Host's configured providers, which this schema cannot enumerate, and a
434
+ * model id that is not in the catalog yet must still be accepted so an operator can
435
+ * pin a gateway alias. The invariants that matter are checked by
436
+ * `seatsFromSettings` on every run, not by the schema.
437
+ */
438
+ const DelphiSeatSchema = z.object({
439
+ role: z.string().default(""),
440
+ model: z.string().default("").description("Model id sent to the gateway, e.g. g-glm-5.3-flash."),
441
+ maxTokens: z.number().min(0).description("Per-seat token budget; raise it for reasoning models."),
442
+ })
443
+
342
444
  /**
343
445
  * Runtime schema for the keys above.
344
446
  *
345
447
  * Exported as a schema, not just a type: DSH validates against it and surfaces the
346
448
  * keys in the profile schema, so a user can discover and set them. A bare interface
347
449
  * is invisible to the harness.
450
+ *
451
+ * The `delphi` block is `.volatile()`: the settings page edits it live, and DSH
452
+ * refuses to write a path that is not beneath a volatile node. The two hook
453
+ * switches stay ordinary fields — they are read at mount and remounting a hook is
454
+ * what "off" is supposed to require.
348
455
  */
349
456
  export const Config = z.object({
350
457
  principlesCheck: z
@@ -359,6 +466,21 @@ export const Config = z.object({
359
466
  .description(
360
467
  "Sprint write guard: auto = deny edits until delphi-review is APPROVED; off = do not register the hook.",
361
468
  ),
469
+ delphi: z
470
+ .object({
471
+ baseUrl: z.string().default("").description("OpenAI-compatible gateway, e.g. https://host/gpt-proxy."),
472
+ apiKey: z
473
+ .string()
474
+ .default("")
475
+ .description("Credential reference such as ${WHALECLOUD_API_KEY}; never a literal key."),
476
+ seats: z.array(DelphiSeatSchema).default([]).description("The three Delphi reviewers."),
477
+ thresholdPercent: z.number().min(0).max(100).description("Approval bar; 3 seats at 90% means 3/3."),
478
+ maxRounds: z.number().min(1).max(20).description("Round cap before the review reports NO CONSENSUS."),
479
+ })
480
+ .volatile()
481
+ .description(
482
+ "Delphi seats edited on the Plugins page. Leave seats empty to use .delphi-config.json.",
483
+ ),
362
484
  })
363
485
 
364
486
  export function apply(ctx: Context, config: unknown = {}): void {
@@ -380,7 +502,7 @@ export function apply(ctx: Context, config: unknown = {}): void {
380
502
  ctx.tools.register(gateCheckTool(shell, dialect))
381
503
  ctx.tools.register(gatePrinciplesTool(shell, dialect))
382
504
  ctx.tools.register(gateArchTool(shell, dialect))
383
- ctx.tools.register(delphiReviewTool(resolveApiKey))
505
+ ctx.tools.register(delphiReviewTool(resolveApiKey, cfg.delphi))
384
506
  ctx.tools.register(sprintTool(shell, dialect))
385
507
 
386
508
  // Publishing the 12 bundled skills is what makes them visible to the model.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boyingliu01/opencode-plugin",
3
- "version": "0.32.1",
3
+ "version": "0.32.2",
4
4
  "type": "module",
5
5
  "main": "index.ts",
6
6
  "description": "XP-Gate quality gates + AI workflow skills + Sprint Flow TUI sidebar for OpenCode",
@@ -1,9 +1,9 @@
1
1
  # SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-10-29
4
- **Commit:** 101a0ff3
3
+ **Generated:** 2026-10-30
4
+ **Commit:** 78786c3f
5
5
  **Branch:** main
6
- **Version:** 0.32.1.0
6
+ **Version:** 0.32.2.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, exactly 3 experts with distinct executable model IDs). Supports design, requirements, and code-walkthrough modes.
@@ -1,9 +1,9 @@
1
1
  # SKILLS/SPRINT-FLOW KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-10-29
4
- **Commit:** 101a0ff3
3
+ **Generated:** 2026-10-30
4
+ **Commit:** 78786c3f
5
5
  **Branch:** main
6
- **Version:** 0.32.1.0
6
+ **Version:** 0.32.2.0
7
7
 
8
8
  ## OVERVIEW
9
9
  **6-phase** development pipeline (v2.0 compact redesign, Issue #290): PREP → DESIGN → BUILD → VERIFY → SHIP → CLOSE. Phase 3/6 BUILD default build mode is **ralph-loop** (REQ-level iteration, 40-67% token savings vs parallel). HARD-GATE between DESIGN (2/6) and BUILD (3/6): design must pass Delphi review (≥90% consensus) before any coding.
@@ -1,9 +1,9 @@
1
1
  # SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-10-29
4
- **Commit:** 101a0ff3
3
+ **Generated:** 2026-10-30
4
+ **Commit:** 78786c3f
5
5
  **Branch:** main
6
- **Version:** 0.32.1.0
6
+ **Version:** 0.32.2.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xp-gate",
3
- "version": "0.32.1",
3
+ "version": "0.32.2",
4
4
  "displayName": "XP-Gate",
5
5
  "description": "Extreme Programming quality gates + AI workflow skills for Qoder. Includes 12 pre-commit gates (Gate 0-11), 8 pre-push gates, Sprint Flow (6 phases), and Delphi multi-expert review (>=90% consensus).",
6
6
  "author": {
@@ -1,9 +1,9 @@
1
1
  # SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-10-29
4
- **Commit:** 101a0ff3
3
+ **Generated:** 2026-10-30
4
+ **Commit:** 78786c3f
5
5
  **Branch:** main
6
- **Version:** 0.32.1.0
6
+ **Version:** 0.32.2.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, exactly 3 experts with distinct executable model IDs). Supports design, requirements, and code-walkthrough modes.
@@ -1,9 +1,9 @@
1
1
  # SKILLS/SPRINT-FLOW KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-10-29
4
- **Commit:** 101a0ff3
3
+ **Generated:** 2026-10-30
4
+ **Commit:** 78786c3f
5
5
  **Branch:** main
6
- **Version:** 0.32.1.0
6
+ **Version:** 0.32.2.0
7
7
 
8
8
  ## OVERVIEW
9
9
  **6-phase** development pipeline (v2.0 compact redesign, Issue #290): PREP → DESIGN → BUILD → VERIFY → SHIP → CLOSE. Phase 3/6 BUILD default build mode is **ralph-loop** (REQ-level iteration, 40-67% token savings vs parallel). HARD-GATE between DESIGN (2/6) and BUILD (3/6): design must pass Delphi review (≥90% consensus) before any coding.
@@ -1,9 +1,9 @@
1
1
  # SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-10-29
4
- **Commit:** 101a0ff3
3
+ **Generated:** 2026-10-30
4
+ **Commit:** 78786c3f
5
5
  **Branch:** main
6
- **Version:** 0.32.1.0
6
+ **Version:** 0.32.2.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
@@ -1,9 +1,9 @@
1
1
  # PRINCIPLES CHECKER MODULE
2
2
 
3
- **Generated:** 2026-10-29
4
- **Commit:** 101a0ff3
3
+ **Generated:** 2026-10-30
4
+ **Commit:** 78786c3f
5
5
  **Branch:** main
6
- **Version:** 0.32.1.0
6
+ **Version:** 0.32.2.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Clean Code & SOLID principles checker — **Gate 4** of pre-commit. 14 rules × 9 language adapters, SARIF 2.1.0 output. Houses the **Boy Scout Rule** enforcement engine (Gate 6) and warning-baseline storage.
@@ -1,9 +1,9 @@
1
1
  # SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-10-29
4
- **Commit:** 101a0ff3
3
+ **Generated:** 2026-10-30
4
+ **Commit:** 78786c3f
5
5
  **Branch:** main
6
- **Version:** 0.32.1.0
6
+ **Version:** 0.32.2.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, exactly 3 experts with distinct executable model IDs). Supports design, requirements, and code-walkthrough modes.
@@ -1,9 +1,9 @@
1
1
  # SKILLS/SPRINT-FLOW KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-10-29
4
- **Commit:** 101a0ff3
3
+ **Generated:** 2026-10-30
4
+ **Commit:** 78786c3f
5
5
  **Branch:** main
6
- **Version:** 0.32.1.0
6
+ **Version:** 0.32.2.0
7
7
 
8
8
  ## OVERVIEW
9
9
  **6-phase** development pipeline (v2.0 compact redesign, Issue #290): PREP → DESIGN → BUILD → VERIFY → SHIP → CLOSE. Phase 3/6 BUILD default build mode is **ralph-loop** (REQ-level iteration, 40-67% token savings vs parallel). HARD-GATE between DESIGN (2/6) and BUILD (3/6): design must pass Delphi review (≥90% consensus) before any coding.
@@ -1,9 +1,9 @@
1
1
  # SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-10-29
4
- **Commit:** 101a0ff3
3
+ **Generated:** 2026-10-30
4
+ **Commit:** 78786c3f
5
5
  **Branch:** main
6
- **Version:** 0.32.1.0
6
+ **Version:** 0.32.2.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.