@boyingliu01/xp-gate 0.10.10 → 0.10.11

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.
@@ -3,7 +3,7 @@
3
3
  **Generated:** 2026-06-23
4
4
  **Commit:** bac916f
5
5
  **Branch:** main
6
- **Version:** 0.10.10.0
6
+ **Version:** 0.10.11.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Mock layering policy enforcement — Gate M3 of pre-push hook. Ensures integration tests use real implementations for internal dependencies, mock external dependencies, and annotate pending mocks with removal plans. Combines project scope scanning, mock decision engine, and per-file validation into a single pipeline.
@@ -3,7 +3,7 @@
3
3
  **Generated:** 2026-06-23
4
4
  **Commit:** bac916f
5
5
  **Branch:** main
6
- **Version:** 0.10.10.0
6
+ **Version:** 0.10.11.0
7
7
 
8
8
  ## OVERVIEW
9
9
  **Gate M** (incremental mutation testing) + **Gate M2** helpers (test-layer detection used by `src/mock-policy/`). Pre-push quality gate. TypeScript-only; uses Stryker.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boyingliu01/xp-gate",
3
- "version": "0.10.10",
3
+ "version": "0.10.11",
4
4
  "description": "AI-driven development workflow: 6 quality gates + Delphi review + Sprint Flow",
5
5
  "bin": {
6
6
  "xp-gate": "./bin/xp-gate.js"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xp-gate",
3
- "version": "0.10.10",
3
+ "version": "0.10.11",
4
4
  "displayName": "XP-Gate",
5
5
  "description": "Extreme Programming quality gates + AI workflow skills for Claude Code. Includes 10 quality gates (Gate 0-9), Sprint Flow (11 phases), and Delphi multi-expert review (>=90% consensus).",
6
6
  "author": {
@@ -3,7 +3,7 @@
3
3
  **Generated:** 2026-06-23
4
4
  **Commit:** bac916f
5
5
  **Branch:** main
6
- **Version:** 0.10.10.0
6
+ **Version:** 0.10.11.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, 3 experts from ≥2 providers, domestic models only). Supports design + code-walkthrough modes.
@@ -3,7 +3,7 @@
3
3
  **Generated:** 2026-06-23
4
4
  **Commit:** bac916f
5
5
  **Branch:** main
6
- **Version:** 0.10.10.0
6
+ **Version:** 0.10.11.0
7
7
 
8
8
  ## OVERVIEW
9
9
  **11-phase** development pipeline: ISOLATE → AUTO-ESTIMATE → THINK → PLAN → BUILD → REVIEW → USER ACCEPTANCE → FEEDBACK → SHIP → LAND → CLEANUP. Phase 2 default build mode is **ralph-loop** (REQ-level iteration, 40-67% token savings vs parallel). HARD-GATE in Phase 1: design must pass Delphi review (≥90% consensus) before any coding.
@@ -3,7 +3,7 @@
3
3
  **Generated:** 2026-06-23
4
4
  **Commit:** bac916f
5
5
  **Branch:** main
6
- **Version:** 0.10.10.0
6
+ **Version:** 0.10.11.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
@@ -26,28 +26,28 @@ import { readSprintState, renderSprintSidebar } from "../tui-plugin.ts"
26
26
  // since it uses module-level state. We test the composition here.)
27
27
  function buildMultiSprintBlock(state: Record<string, unknown>, index: number): string {
28
28
  const lines: string[] = []
29
- const id = (state as any).id || `sprint-${index}`
30
- const desc = (state as any).task_description || id
29
+ const id = (state as Record<string, unknown>).id || `sprint-${index}`
30
+ const desc = (state as Record<string, unknown>).task_description || id
31
31
  lines.push(`SPRINT: ${desc}`)
32
- if ((state as any).isolation?.branch) {
33
- lines.push(` ${(state as any).isolation.branch}`)
32
+ if ((state as Record<string, unknown>).isolation?.branch) {
33
+ lines.push(` ${(state as Record<string, unknown>).isolation.branch}`)
34
34
  }
35
35
  const historyByPhase: Record<string, { status?: string; phase_name?: string }> = {}
36
- if (Array.isArray((state as any).phase_history)) {
37
- for (const ph of (state as any).phase_history) {
36
+ if (Array.isArray((state as Record<string, unknown>).phase_history)) {
37
+ for (const ph of (state as Record<string, unknown>).phase_history) {
38
38
  historyByPhase[String(ph.phase)] = ph
39
39
  }
40
40
  }
41
41
  for (const key of ['-1', '-0.5', '0', '1', '2', '3', '4', '5', '6', '7', '8']) {
42
42
  const history = historyByPhase[key]
43
- if (!history && String((state as any).phase) !== key) continue
43
+ if (!history && String((state as Record<string, unknown>).phase) !== key) continue
44
44
  const name = history?.phase_name || ({
45
45
  '-1': 'ISOLATE', '-0.5': 'AUTO-ESTIMATE', '0': 'THINK', '1': 'PLAN', '2': 'BUILD',
46
46
  '3': 'REVIEW', '4': 'USER ACCEPT', '5': 'FEEDBACK', '6': 'SHIP', '7': 'LAND', '8': 'CLEANUP',
47
47
  })[key] || key
48
48
  const sym = history?.status === 'completed' ? '✓' :
49
49
  history?.status === 'in_progress' ? '→' :
50
- (String((state as any).phase) === key ? '·' : '○')
50
+ (String((state as Record<string, unknown>).phase) === key ? '·' : '○')
51
51
  lines.push(`${sym} ${name.padEnd(14)} ${history?.status === 'completed' ? 'done' : history?.status === 'in_progress' ? 'active' : ''}`.replace(/\s+$/, ''))
52
52
  }
53
53
  return lines.join('\n')
@@ -296,7 +296,7 @@ void describe("renderSprintSidebar", () => {
296
296
  })
297
297
 
298
298
  void it("omits metrics section when none present", () => {
299
- const output = renderSprintSidebar(makeSprintState({ metrics: {} }) as any)
299
+ const output = renderSprintSidebar(makeSprintState({ metrics: {} }) as unknown as Parameters<typeof renderSprintSidebar>[0])
300
300
  assert.ok(!output.includes("tests:"))
301
301
  assert.ok(!output.includes("cov:"))
302
302
  })
@@ -160,20 +160,22 @@ async function checkXpGateUpdate(): Promise<UpgradeResult> {
160
160
  return { action: "noop", localVersion, remoteVersion }
161
161
  }
162
162
 
163
- // 5. Outdated — auto upgrade (non-blocking spawn)
163
+ // 5. Outdated — auto upgrade (awaited spawn)
164
164
  writeXpGateCache({ ts: Date.now(), localVersion, remoteVersion })
165
165
  try {
166
- const child = spawn("npm", ["install", "-g", `${XP_GATE_NPM_PKG}@${remoteVersion}`], {
167
- stdio: "pipe",
168
- timeout: 120_000,
166
+ const exitCode = await new Promise<number | null>((resolve, reject) => {
167
+ const child = spawn("npm", ["install", "-g", `${XP_GATE_NPM_PKG}@${remoteVersion}`], {
168
+ stdio: "pipe",
169
+ timeout: 120_000,
170
+ })
171
+ child.on("close", (code) => resolve(code))
172
+ child.on("error", (err) => reject(err))
169
173
  })
170
- child.on("close", (code) => {
171
- if (code === 0) {
172
- writeXpGateCache({ ts: Date.now(), localVersion: remoteVersion, remoteVersion, status: "current" })
173
- }
174
- })
175
- child.on("error", () => { /* empty — cache won't get status:current, so next check retries */ })
176
- return { action: "upgraded", localVersion, remoteVersion }
174
+ if (exitCode === 0) {
175
+ writeXpGateCache({ ts: Date.now(), localVersion: remoteVersion, remoteVersion, status: "current" })
176
+ return { action: "upgraded", localVersion, remoteVersion }
177
+ }
178
+ return { action: "error", localVersion, remoteVersion, error: `npm install exited with code ${exitCode}` }
177
179
  } catch (err) {
178
180
  const msg = err instanceof Error ? err.message : String(err)
179
181
  return { action: "error", localVersion, remoteVersion, error: msg }
@@ -410,3 +412,107 @@ void describe("checkXpGateUpdate — opt-out config integration (UPG-003)", () =
410
412
  assert.ok(["noop", "upgraded", "error"].includes(result.action))
411
413
  })
412
414
  })
415
+
416
+ // ── UPG-004: Fire-and-forget spawn → cache write test ──
417
+
418
+ void describe("checkXpGateUpdate — spawn completes and writes cache (UPG-004)", () => {
419
+ const fakeHome = join(tmpdir(), "xp-gate-upd-spawn-cache-" + randomUUID())
420
+ const origHome = process.env.HOME
421
+
422
+ before(() => {
423
+ process.env.HOME = fakeHome
424
+ mkdirSync(join(fakeHome, ".xp-gate"), { recursive: true })
425
+ })
426
+
427
+ after(() => {
428
+ process.env.HOME = origHome
429
+ rmSync(fakeHome, { recursive: true, force: true })
430
+ })
431
+
432
+ void it("should write cache with status:current after spawn-based upgrade completes", async () => {
433
+ // WARNING: This test ACTUALLY spawns npm install -g.
434
+ // It's skipped in CI environments.
435
+ if (process.env.CI) {
436
+ console.log("SKIP: UPG-004 spawn test disabled in CI")
437
+ return
438
+ }
439
+
440
+ // Simulate: local is old, remote is newer → should trigger spawn
441
+ const cachePath = join(fakeHome, ".xp-gate", "xp-gate-version-check.json")
442
+ writeFileSync(cachePath, JSON.stringify({
443
+ ts: Date.now() - 86_400_000 - 3600_000, // stale cache
444
+ localVersion: "0.9.0",
445
+ remoteVersion: "0.9.1",
446
+ }))
447
+
448
+ // Override local version to force upgrade path
449
+ getLocalVersionOverride = () => "0.9.0"
450
+
451
+ const result = await checkXpGateUpdate()
452
+ getLocalVersionOverride = null
453
+
454
+ // After fix: spawn is awaited, so cache is already written when function returns
455
+ assert.equal(result.action, "upgraded")
456
+
457
+ // Cache should have status:current immediately (no delay needed)
458
+ const finalCache = readXpGateCache()
459
+ if (finalCache) {
460
+ assert.equal(finalCache.status, "current",
461
+ "UPG-004 FAIL: spawn did not write status:current. The upgrade promise " +
462
+ "must be awaited so the cache reflects the completed install.")
463
+ }
464
+ })
465
+ })
466
+
467
+ // ── UPG-005: runBackgroundUpdates awaits checkXpGateUpdate ──
468
+
469
+ /**
470
+ * Simulates runBackgroundUpdates to verify it properly awaits the upgrade.
471
+ * This test validates that the chat.message hook doesn't lose the upgrade.
472
+ */
473
+ void describe("runBackgroundUpdates — await verification (UPG-005)", () => {
474
+ const fakeHome = join(tmpdir(), "xp-gate-upd-await-" + randomUUID())
475
+ const origHome = process.env.HOME
476
+
477
+ before(() => {
478
+ process.env.HOME = fakeHome
479
+ mkdirSync(join(fakeHome, ".xp-gate"), { recursive: true })
480
+ })
481
+
482
+ after(() => {
483
+ process.env.HOME = origHome
484
+ rmSync(fakeHome, { recursive: true, force: true })
485
+ })
486
+
487
+ void it("checkXpGateUpdate resolves its promise BEFORE returning result", async () => {
488
+ // If spawn is fire-and-forget, the function returns before spawn completes.
489
+ // This test measures: does the returned promise resolve before or after spawn?
490
+ if (process.env.CI) {
491
+ console.log("SKIP: UPG-005 spawn timing test disabled in CI")
492
+ return
493
+ }
494
+
495
+ const cachePath = join(fakeHome, ".xp-gate", "xp-gate-version-check.json")
496
+ writeFileSync(cachePath, JSON.stringify({
497
+ ts: Date.now() - 86_400_000 - 3600_000,
498
+ localVersion: "0.9.0",
499
+ remoteVersion: "0.9.1",
500
+ }))
501
+
502
+ getLocalVersionOverride = () => "0.9.0"
503
+
504
+ const startTime = Date.now()
505
+ const result = await checkXpGateUpdate()
506
+ const elapsed = Date.now() - startTime
507
+ getLocalVersionOverride = null
508
+
509
+ assert.equal(result.action, "upgraded")
510
+
511
+ // After fix: spawn is properly awaited, so elapsed >= 1000ms
512
+ console.log(`UPG-005: checkXpGateUpdate() returned in ${elapsed}ms`)
513
+ assert.ok(elapsed > 1000,
514
+ `UPG-005 FAIL: resolve took only ${elapsed}ms — spawn is NOT awaited. ` +
515
+ "The chat.message hook must await the upgrade promise so " +
516
+ "npm install completes and cache gets status:current.")
517
+ })
518
+ })
@@ -129,17 +129,19 @@ async function checkXpGateUpdate(): Promise<UpgradeResult> {
129
129
 
130
130
  writeCache(XP_GATE_CACHE_FILE, { ts: Date.now(), localVersion, remoteVersion })
131
131
  try {
132
- const child = spawn("npm", ["install", "-g", `${XP_GATE_NPM_PKG}@${remoteVersion}`], {
133
- stdio: "pipe",
134
- timeout: 120_000,
132
+ const exitCode = await new Promise<number | null>((resolve, reject) => {
133
+ const child = spawn("npm", ["install", "-g", `${XP_GATE_NPM_PKG}@${remoteVersion}`], {
134
+ stdio: "pipe",
135
+ timeout: 120_000,
136
+ })
137
+ child.on("close", (code) => resolve(code))
138
+ child.on("error", (err) => reject(err))
135
139
  })
136
- child.on("close", (code) => {
137
- if (code === 0) {
138
- writeCache(XP_GATE_CACHE_FILE, { ts: Date.now(), localVersion: remoteVersion, remoteVersion, status: "current" })
139
- }
140
- })
141
- child.on("error", () => { /* empty — cache won't get status:current, so next check retries */ })
142
- return { action: "upgraded", localVersion, remoteVersion }
140
+ if (exitCode === 0) {
141
+ writeCache(XP_GATE_CACHE_FILE, { ts: Date.now(), localVersion: remoteVersion, remoteVersion, status: "current" })
142
+ return { action: "upgraded", localVersion, remoteVersion }
143
+ }
144
+ return { action: "error", localVersion, remoteVersion, error: `npm install exited with code ${exitCode}` }
143
145
  } catch (err) {
144
146
  const msg = err instanceof Error ? err.message : String(err)
145
147
  return { action: "error", localVersion, remoteVersion, error: msg }
@@ -319,11 +321,9 @@ export const XpGatePlugin = async (input: OpenCodePluginInput) => {
319
321
  "chat.message": async (_input: { message: string }) => {
320
322
  if (!checked) {
321
323
  checked = true
322
- runBackgroundUpdates(directory).then((msg) => {
323
- if (msg) process.stderr.write(`${msg}\n`)
324
- })
324
+ const msg = await runBackgroundUpdates(directory).catch(() => null)
325
+ if (msg) process.stderr.write(`${msg}\n`)
325
326
  }
326
- return { action: "continue" }
327
327
  },
328
328
  }
329
329
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@boyingliu01/opencode-plugin",
3
- "version": "0.10.10",
3
+ "version": "0.10.11",
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",
@@ -3,7 +3,7 @@
3
3
  **Generated:** 2026-06-23
4
4
  **Commit:** bac916f
5
5
  **Branch:** main
6
- **Version:** 0.10.10.0
6
+ **Version:** 0.10.11.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, 3 experts from ≥2 providers, domestic models only). Supports design + code-walkthrough modes.
@@ -3,7 +3,7 @@
3
3
  **Generated:** 2026-06-23
4
4
  **Commit:** bac916f
5
5
  **Branch:** main
6
- **Version:** 0.10.10.0
6
+ **Version:** 0.10.11.0
7
7
 
8
8
  ## OVERVIEW
9
9
  **11-phase** development pipeline: ISOLATE → AUTO-ESTIMATE → THINK → PLAN → BUILD → REVIEW → USER ACCEPTANCE → FEEDBACK → SHIP → LAND → CLEANUP. Phase 2 default build mode is **ralph-loop** (REQ-level iteration, 40-67% token savings vs parallel). HARD-GATE in Phase 1: design must pass Delphi review (≥90% consensus) before any coding.
@@ -3,7 +3,7 @@
3
3
  **Generated:** 2026-06-23
4
4
  **Commit:** bac916f
5
5
  **Branch:** main
6
- **Version:** 0.10.10.0
6
+ **Version:** 0.10.11.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.10.10",
3
+ "version": "0.10.11",
4
4
  "displayName": "XP-Gate",
5
5
  "description": "Extreme Programming quality gates + AI workflow skills for Qoder. Includes 10 quality gates (Gate 0-9), Sprint Flow (11 phases), and Delphi multi-expert review (>=90% consensus).",
6
6
  "author": {
@@ -3,7 +3,7 @@
3
3
  **Generated:** 2026-06-23
4
4
  **Commit:** bac916f
5
5
  **Branch:** main
6
- **Version:** 0.10.10.0
6
+ **Version:** 0.10.11.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, 3 experts from ≥2 providers, domestic models only). Supports design + code-walkthrough modes.
@@ -3,7 +3,7 @@
3
3
  **Generated:** 2026-06-23
4
4
  **Commit:** bac916f
5
5
  **Branch:** main
6
- **Version:** 0.10.10.0
6
+ **Version:** 0.10.11.0
7
7
 
8
8
  ## OVERVIEW
9
9
  **11-phase** development pipeline: ISOLATE → AUTO-ESTIMATE → THINK → PLAN → BUILD → REVIEW → USER ACCEPTANCE → FEEDBACK → SHIP → LAND → CLEANUP. Phase 2 default build mode is **ralph-loop** (REQ-level iteration, 40-67% token savings vs parallel). HARD-GATE in Phase 1: design must pass Delphi review (≥90% consensus) before any coding.
@@ -3,7 +3,7 @@
3
3
  **Generated:** 2026-06-23
4
4
  **Commit:** bac916f
5
5
  **Branch:** main
6
- **Version:** 0.10.10.0
6
+ **Version:** 0.10.11.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
@@ -3,7 +3,7 @@
3
3
  **Generated:** 2026-06-23
4
4
  **Commit:** bac916f
5
5
  **Branch:** main
6
- **Version:** 0.10.10.0
6
+ **Version:** 0.10.11.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.
@@ -3,7 +3,7 @@
3
3
  **Generated:** 2026-06-23
4
4
  **Commit:** bac916f
5
5
  **Branch:** main
6
- **Version:** 0.10.10.0
6
+ **Version:** 0.10.11.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, 3 experts from ≥2 providers, domestic models only). Supports design + code-walkthrough modes.
@@ -3,7 +3,7 @@
3
3
  **Generated:** 2026-06-23
4
4
  **Commit:** bac916f
5
5
  **Branch:** main
6
- **Version:** 0.10.10.0
6
+ **Version:** 0.10.11.0
7
7
 
8
8
  ## OVERVIEW
9
9
  **11-phase** development pipeline: ISOLATE → AUTO-ESTIMATE → THINK → PLAN → BUILD → REVIEW → USER ACCEPTANCE → FEEDBACK → SHIP → LAND → CLEANUP. Phase 2 default build mode is **ralph-loop** (REQ-level iteration, 40-67% token savings vs parallel). HARD-GATE in Phase 1: design must pass Delphi review (≥90% consensus) before any coding.
@@ -3,7 +3,7 @@
3
3
  **Generated:** 2026-06-23
4
4
  **Commit:** bac916f
5
5
  **Branch:** main
6
- **Version:** 0.10.10.0
6
+ **Version:** 0.10.11.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.