@t275005746/gse 0.1.1 → 0.1.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 (84) hide show
  1. package/.gse/state.json +147 -26
  2. package/CHANGELOG.md +19 -3
  3. package/README.md +13 -6
  4. package/README.zh-CN.md +13 -6
  5. package/SKILL.md +32 -12
  6. package/assets/templates/dispatch-packet.md +22 -14
  7. package/assets/templates/evidence.md +11 -5
  8. package/assets/templates/role-fallback-packet.md +49 -0
  9. package/package.json +18 -10
  10. package/references/agent-roles.md +41 -20
  11. package/references/commands.md +74 -45
  12. package/references/drift-audit.md +2 -1
  13. package/references/evidence-taxonomy.md +46 -9
  14. package/references/file-ownership.md +17 -13
  15. package/references/final-form-roadmap.md +201 -0
  16. package/references/final-readiness.md +3 -3
  17. package/references/host-adapters.md +38 -10
  18. package/references/learning-system.md +24 -8
  19. package/references/maintenance-cadence.md +51 -0
  20. package/references/project-guards.md +52 -0
  21. package/references/quality-gates.md +18 -11
  22. package/references/role-dispatch-fallback.md +54 -0
  23. package/references/tool-adapters.md +13 -3
  24. package/scripts/audit-close-gate-hardening.mjs +188 -0
  25. package/scripts/audit-close-gate.mjs +219 -33
  26. package/scripts/audit-command-execution.mjs +30 -17
  27. package/scripts/audit-commands.mjs +7 -5
  28. package/scripts/audit-completion-plan-drill.mjs +230 -0
  29. package/scripts/audit-completion-readiness.mjs +9 -6
  30. package/scripts/audit-continue-preflight.mjs +234 -0
  31. package/scripts/audit-evidence-levels.mjs +217 -0
  32. package/scripts/audit-evidence-review-queue.mjs +206 -0
  33. package/scripts/audit-final-acceptance-packet.mjs +9 -9
  34. package/scripts/audit-final-form-progress-report.mjs +19 -18
  35. package/scripts/audit-final-form-roadmap.mjs +157 -0
  36. package/scripts/audit-final-form-stale-copy.mjs +2 -2
  37. package/scripts/audit-final-readiness.mjs +3 -3
  38. package/scripts/audit-fresh-session-readiness.mjs +1 -1
  39. package/scripts/audit-host-capabilities.mjs +237 -0
  40. package/scripts/audit-installed-sync.mjs +203 -0
  41. package/scripts/audit-learning-drift.mjs +292 -0
  42. package/scripts/audit-learning-promotion.mjs +351 -0
  43. package/scripts/audit-learning-system.mjs +24 -14
  44. package/scripts/audit-local-final-form-completion.mjs +11 -10
  45. package/scripts/audit-maintenance-cadence.mjs +139 -0
  46. package/scripts/audit-maintenance-snapshot.mjs +181 -0
  47. package/scripts/audit-npm-package-metadata.mjs +8 -2
  48. package/scripts/audit-npm-publish-dry-run.mjs +10 -4
  49. package/scripts/audit-npm-tarball-install.mjs +9 -3
  50. package/scripts/audit-owner-external-gate-kit.mjs +12 -11
  51. package/scripts/audit-project-guards.mjs +189 -0
  52. package/scripts/audit-project.mjs +14 -11
  53. package/scripts/audit-public-acceptance-handoff.mjs +10 -10
  54. package/scripts/audit-public-acceptance-readiness.mjs +6 -6
  55. package/scripts/audit-public-release-checklist.mjs +17 -15
  56. package/scripts/audit-release-bundle.mjs +13 -11
  57. package/scripts/audit-release-owner-action-plan-drill.mjs +5 -4
  58. package/scripts/audit-release-owner-action-plan.mjs +10 -10
  59. package/scripts/audit-release-status-manifest.mjs +4 -4
  60. package/scripts/audit-roadmap-consistency.mjs +11 -7
  61. package/scripts/audit-role-dispatch-fallback.mjs +212 -0
  62. package/scripts/audit-session-sync.mjs +127 -0
  63. package/scripts/audit-state-freshness.mjs +6 -5
  64. package/scripts/audit-state-repair.mjs +360 -0
  65. package/scripts/audit-target-hardening-drills.mjs +267 -0
  66. package/scripts/audit-tool-fallback-policy.mjs +180 -0
  67. package/scripts/audit-ui-browser-evidence-policy.mjs +191 -0
  68. package/scripts/audit-validation-profiles.mjs +6 -4
  69. package/scripts/backfill-evidence-levels.mjs +98 -0
  70. package/scripts/check-encoding.mjs +72 -0
  71. package/scripts/generate-continue-packet.mjs +1214 -0
  72. package/scripts/generate-final-acceptance-packet.mjs +23 -8
  73. package/scripts/generate-final-form-progress-report.mjs +25 -23
  74. package/scripts/generate-host-runtime-evidence-handoff.mjs +23 -16
  75. package/scripts/generate-maintenance-snapshot.mjs +216 -0
  76. package/scripts/generate-public-acceptance-handoff.mjs +26 -14
  77. package/scripts/generate-release-owner-action-plan.mjs +4 -3
  78. package/scripts/generate-release-status-manifest.mjs +4 -3
  79. package/scripts/init-project.mjs +144 -63
  80. package/scripts/record-learning.mjs +37 -8
  81. package/scripts/record-session-sync.mjs +87 -0
  82. package/scripts/run-gse-command.mjs +79 -47
  83. package/scripts/run-validation-profile.mjs +24 -5
  84. package/scripts/validate-gse.mjs +216 -0
@@ -1,8 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import fs from 'node:fs'
3
- import os from 'node:os'
4
- import path from 'node:path'
5
- import { spawnSync } from 'node:child_process'
2
+ import fs from 'node:fs'
3
+ import os from 'node:os'
4
+ import path from 'node:path'
5
+ import { spawnSync } from 'node:child_process'
6
+ import { analyzeEvidenceLevels } from './audit-evidence-levels.mjs'
7
+ import { readRoleDispatchFallback } from './audit-role-dispatch-fallback.mjs'
6
8
 
7
9
  const args = process.argv.slice(2)
8
10
 
@@ -50,7 +52,7 @@ function exists(target, relativePath) {
50
52
  return fs.existsSync(path.join(target, relativePath))
51
53
  }
52
54
 
53
- function runGit(target, commandArgs) {
55
+ function runGit(target, commandArgs) {
54
56
  const result = spawnSync('git', commandArgs, {
55
57
  cwd: target,
56
58
  encoding: 'utf8',
@@ -61,7 +63,58 @@ function runGit(target, commandArgs) {
61
63
  stdout: (result.stdout ?? '').trim(),
62
64
  stderr: (result.stderr ?? '').trim(),
63
65
  }
64
- }
66
+ }
67
+
68
+ function normalizeGitPath(rawPath) {
69
+ const normalized = rawPath.replace(/\\/g, '/')
70
+ const renameMarker = ' -> '
71
+ if (normalized.includes(renameMarker)) return normalized.split(renameMarker).pop()
72
+ return normalized
73
+ }
74
+
75
+ function parseGitPorcelain(text) {
76
+ const entries = []
77
+ for (const line of text.split(/\r?\n/).filter(Boolean)) {
78
+ const x = line[0] ?? ' '
79
+ const y = line[1] ?? ' '
80
+ const rawPath = line.slice(3).trim()
81
+ const filePath = normalizeGitPath(rawPath)
82
+ const untracked = x === '?' && y === '?'
83
+ const staged = !untracked && x !== ' '
84
+ const unstaged = !untracked && y !== ' '
85
+ const conflict = x === 'U' || y === 'U' || ['AA', 'DD', 'AU', 'UA', 'DU', 'UD', 'UU'].includes(x + y)
86
+ entries.push({ code: x + y, path: filePath, staged, unstaged, untracked, conflict })
87
+ }
88
+ return entries
89
+ }
90
+
91
+ const generatedArtifactPatterns = [
92
+ /^output\//,
93
+ /^playwright-report\//,
94
+ /^test-results\//,
95
+ /^coverage\//,
96
+ /^\.nyc_output\//,
97
+ /^node_modules\//,
98
+ /^\.turbo\//,
99
+ /^\.next\//,
100
+ /^dist\//,
101
+ ]
102
+
103
+ function isGeneratedArtifact(relativePath) {
104
+ const normalized = relativePath.replace(/\\/g, '/')
105
+ return generatedArtifactPatterns.some((pattern) => pattern.test(normalized))
106
+ }
107
+
108
+ function summarizeGitEntries(entries) {
109
+ const staged = entries.filter((entry) => entry.staged)
110
+ const unstaged = entries.filter((entry) => entry.unstaged)
111
+ const untracked = entries.filter((entry) => entry.untracked)
112
+ const conflicts = entries.filter((entry) => entry.conflict)
113
+ const mixed = entries.filter((entry) => entry.staged && entry.unstaged)
114
+ const stagedGenerated = staged.filter((entry) => isGeneratedArtifact(entry.path))
115
+ const dirtyGenerated = entries.filter((entry) => !entry.staged && isGeneratedArtifact(entry.path))
116
+ return { staged, unstaged, untracked, conflicts, mixed, stagedGenerated, dirtyGenerated }
117
+ }
65
118
 
66
119
  function statusFrom(ok, warn = false) {
67
120
  if (ok) return 'passed'
@@ -73,9 +126,10 @@ function check(id, label, status, evidence, recommendation = '') {
73
126
  return { id, label, status, evidence, recommendation }
74
127
  }
75
128
 
76
- function createFixture() {
77
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gse-close-gate-'))
78
- fs.mkdirSync(path.join(dir, '.gse', 'evidence'), { recursive: true })
129
+ function createFixture() {
130
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gse-close-gate-'))
131
+ fs.mkdirSync(path.join(dir, '.gse', 'evidence'), { recursive: true })
132
+ fs.mkdirSync(path.join(dir, '.gse', 'agents'), { recursive: true })
79
133
  fs.writeFileSync(path.join(dir, '.gse', 'README.md'), '# GSE\n', 'utf8')
80
134
  fs.writeFileSync(path.join(dir, '.gse', 'project-profile.md'), '# Project Profile\n', 'utf8')
81
135
  fs.writeFileSync(path.join(dir, '.gse', 'goal-map.md'), '# Goal Map\n\nNext action: archive slice.\n', 'utf8')
@@ -111,20 +165,40 @@ function createFixture() {
111
165
  ) + '\n',
112
166
  'utf8',
113
167
  )
114
- fs.writeFileSync(
115
- path.join(dir, '.gse', 'evidence', 'index.jsonl'),
168
+ fs.writeFileSync(
169
+ path.join(dir, '.gse', 'evidence', 'index.jsonl'),
116
170
  JSON.stringify({
117
171
  date: '2026-07-06',
118
- recordType: 'slice',
119
- status: 'verified',
120
- summary: 'Fixture close gate evidence.',
121
- evidenceFile: '.gse/evidence/2026-07-06.md',
172
+ recordType: 'slice',
173
+ status: 'verified',
174
+ evidenceLevel: 'verified-unit',
175
+ requiredEvidenceLevel: 'verified-unit',
176
+ summary: 'Fixture close gate evidence.',
177
+ evidenceFile: '.gse/evidence/2026-07-06.md',
122
178
  commands: ['node scripts/audit-close-gate.mjs --self-test'],
123
179
  nextAction: 'Archive slice.',
124
180
  }) + '\n',
125
- 'utf8',
126
- )
127
- runGit(dir, ['init'])
181
+ 'utf8',
182
+ )
183
+ fs.writeFileSync(
184
+ path.join(dir, '.gse', 'agents', 'role-fallback-packets.md'),
185
+ [
186
+ '# Role Fallback Packets',
187
+ '',
188
+ '| Role | Mode | Real delegation used | Tool status | Fallback output | Evidence | Stop condition | Write access |',
189
+ '|---|---|---|---|---|---|---|---|',
190
+ '| Planner | sequential-role | no | unknown | Plan | fixture plan | Plan accepted | read-only |',
191
+ '| Locator | sequential-role | no | unknown | File map | fixture map | Files identified | read-only |',
192
+ '| Implementer | sequential-role | no | unknown | Patch | fixture patch | Patch complete | assigned files |',
193
+ '| Verifier | sequential-role | no | unknown | Test results | fixture tests | Focused checks pass | evidence only |',
194
+ '| Reviewer | sequential-role | no | unknown | Review notes | fixture review | No blocking findings | read-only |',
195
+ '| Docs/Evidence | sequential-role | no | unknown | Evidence log | fixture evidence | Evidence recorded | docs/evidence only |',
196
+ '| Release | sequential-role | no | unknown | Claim boundary | fixture release | External gates visible | read-only |',
197
+ '',
198
+ ].join('\n'),
199
+ 'utf8',
200
+ )
201
+ runGit(dir, ['init'])
128
202
  runGit(dir, ['config', 'user.email', 'gse-fixture@example.local'])
129
203
  runGit(dir, ['config', 'user.name', 'GSE Fixture'])
130
204
  runGit(dir, ['add', '.'])
@@ -135,9 +209,10 @@ function createFixture() {
135
209
  function auditCloseGate(target) {
136
210
  const resolvedTarget = path.resolve(target)
137
211
  const gseDir = path.join(resolvedTarget, '.gse')
138
- const state = readJson(path.join(gseDir, 'state.json'))
139
- const evidenceIndex = readJsonl(path.join(gseDir, 'evidence', 'index.jsonl'))
140
- const checks = []
212
+ const state = readJson(path.join(gseDir, 'state.json'))
213
+ const evidenceIndex = readJsonl(path.join(gseDir, 'evidence', 'index.jsonl'))
214
+ const evidenceLevelAnalysis = analyzeEvidenceLevels(evidenceIndex.records)
215
+ const checks = []
141
216
 
142
217
  checks.push(
143
218
  check(
@@ -241,15 +316,103 @@ function auditCloseGate(target) {
241
316
  const gitRoot = runGit(resolvedTarget, ['rev-parse', '--show-toplevel'])
242
317
  const isGitRepo = gitRoot.status === 0
243
318
  const gseStatus = isGitRepo ? runGit(resolvedTarget, ['status', '--short', '.gse']) : { status: 1, stdout: '', stderr: 'not a git repository' }
244
- checks.push(
245
- check(
246
- 'CG08',
319
+ checks.push(
320
+ check(
321
+ 'CG08',
247
322
  '.gse git state is known',
248
323
  isGitRepo ? (gseStatus.stdout ? 'warning' : 'passed') : 'warning',
249
324
  isGitRepo ? gseStatus.stdout || 'clean' : 'not a git repository',
250
325
  isGitRepo && gseStatus.stdout ? 'Review and intentionally stage/commit or document project-local GSE changes.' : '',
251
- ),
252
- )
326
+ ),
327
+ )
328
+
329
+ const evidenceLevelStatus = evidenceLevelAnalysis.invalidLevel.length > 0
330
+ ? 'failed'
331
+ : evidenceLevelAnalysis.downgraded.length > 0 || evidenceLevelAnalysis.missingLevel.length > 0
332
+ ? 'warning'
333
+ : 'passed'
334
+ checks.push(
335
+ check(
336
+ 'CG09',
337
+ 'evidence level validity and downgrade labels are visible',
338
+ evidenceLevelStatus,
339
+ evidenceLevelAnalysis.invalidLevel.length
340
+ ? `invalid evidence level(s): ${evidenceLevelAnalysis.invalidLevel.map((item) => `${item.summary}:${item.evidenceLevel}`).join('; ')}`
341
+ : `${evidenceLevelAnalysis.recordsWithLevel}/${evidenceLevelAnalysis.records} record(s) with evidenceLevel; ${evidenceLevelAnalysis.downgraded.length} downgrade(s); ${evidenceLevelAnalysis.missingLevel.length} historical missing`,
342
+ evidenceLevelAnalysis.invalidLevel.length
343
+ ? 'Use one of the evidence levels from references/evidence-taxonomy.md.'
344
+ : evidenceLevelAnalysis.downgraded.length
345
+ ? 'Record whether the downgrade is acceptable for this slice before claiming browser, CI, owner, or release proof.'
346
+ : '',
347
+ ),
348
+ )
349
+
350
+ const roleFallback = readRoleDispatchFallback(resolvedTarget)
351
+ const roleFallbackStatus = roleFallback.status === 'failed' ? 'failed' : roleFallback.status === 'warning' ? 'warning' : 'passed'
352
+ checks.push(
353
+ check(
354
+ 'CG10',
355
+ 'role dispatch and subagent claims are honest',
356
+ roleFallbackStatus,
357
+ roleFallback.exists
358
+ ? roleFallback.summary.fakeDelegationRisk.length
359
+ ? `fake delegation risk: ${roleFallback.summary.fakeDelegationRisk.join(', ')}`
360
+ : `${roleFallback.summary.total} role packet(s); ${roleFallback.summary.sequentialFallbackRoles.length} sequential fallback role(s)`
361
+ : 'role fallback packet missing',
362
+ roleFallbackStatus === 'failed'
363
+ ? 'Do not claim real subagent dispatch unless the current host/tool status is verified.'
364
+ : roleFallbackStatus === 'warning'
365
+ ? 'Adopt role fallback packets or record why role dispatch evidence is unavailable.'
366
+ : '',
367
+ ),
368
+ )
369
+
370
+ const fullGitStatus = isGitRepo ? runGit(resolvedTarget, ['status', '--porcelain=v1']) : { status: 1, stdout: '', stderr: 'not a git repository' }
371
+ const gitEntries = isGitRepo && fullGitStatus.status === 0 ? parseGitPorcelain(fullGitStatus.stdout) : []
372
+ const gitSummary = summarizeGitEntries(gitEntries)
373
+ const ownershipStatus = !isGitRepo
374
+ ? 'warning'
375
+ : gitSummary.conflicts.length > 0 || gitSummary.mixed.length > 0
376
+ ? 'failed'
377
+ : gitEntries.length > 0
378
+ ? 'warning'
379
+ : 'passed'
380
+ checks.push(
381
+ check(
382
+ 'CG11',
383
+ 'worktree change ownership is bounded before close',
384
+ ownershipStatus,
385
+ isGitRepo
386
+ ? gitEntries.length
387
+ ? `${gitSummary.staged.length} staged, ${gitSummary.unstaged.length} unstaged, ${gitSummary.untracked.length} untracked, ${gitSummary.mixed.length} mixed, ${gitSummary.conflicts.length} conflict(s)`
388
+ : 'worktree clean'
389
+ : 'not a git repository',
390
+ ownershipStatus === 'failed'
391
+ ? 'Resolve merge conflicts or mixed staged/unstaged edits before closing so ownership and evidence are unambiguous.'
392
+ : ownershipStatus === 'warning'
393
+ ? 'Review changed files, exclude unrelated or generated artifacts, and stage/commit only the current slice.'
394
+ : '',
395
+ ),
396
+ )
397
+
398
+ const artifactStatus = gitSummary.stagedGenerated.length > 0 ? 'failed' : gitSummary.dirtyGenerated.length > 0 ? 'warning' : 'passed'
399
+ checks.push(
400
+ check(
401
+ 'CG12',
402
+ 'generated test artifacts are not staged as slice evidence',
403
+ artifactStatus,
404
+ gitSummary.stagedGenerated.length
405
+ ? `staged generated artifact(s): ${gitSummary.stagedGenerated.map((entry) => entry.path).join(', ')}`
406
+ : gitSummary.dirtyGenerated.length
407
+ ? `dirty generated artifact(s): ${gitSummary.dirtyGenerated.map((entry) => entry.path).join(', ')}`
408
+ : 'no generated/test output artifacts staged',
409
+ artifactStatus === 'failed'
410
+ ? 'Unstage generated test/browser/build artifacts unless the project explicitly requires them as source.'
411
+ : artifactStatus === 'warning'
412
+ ? 'Keep generated artifacts out of the commit or explain why they are required.'
413
+ : '',
414
+ ),
415
+ )
253
416
 
254
417
  const failed = checks.filter((item) => item.status === 'failed').length
255
418
  const warnings = checks.filter((item) => item.status === 'warning').length
@@ -275,13 +438,36 @@ function auditCloseGate(target) {
275
438
  currentSliceStatus: state.data?.currentSlice?.status ?? null,
276
439
  nextAction: state.data?.currentSlice?.nextAction ?? null,
277
440
  },
278
- evidenceIndex: {
279
- exists: evidenceIndex.exists,
280
- valid: indexValid,
281
- records: evidenceIndex.records.length,
282
- closeableRecords: closeableEvidenceRecords.length,
283
- },
284
- checks,
441
+ evidenceIndex: {
442
+ exists: evidenceIndex.exists,
443
+ valid: indexValid,
444
+ records: evidenceIndex.records.length,
445
+ closeableRecords: closeableEvidenceRecords.length,
446
+ evidenceLevels: {
447
+ recordsWithLevel: evidenceLevelAnalysis.recordsWithLevel,
448
+ missingLevel: evidenceLevelAnalysis.missingLevel.length,
449
+ invalidLevel: evidenceLevelAnalysis.invalidLevel,
450
+ downgraded: evidenceLevelAnalysis.downgraded,
451
+ },
452
+ },
453
+ roleFallback: {
454
+ exists: roleFallback.exists,
455
+ status: roleFallback.status,
456
+ total: roleFallback.summary.total,
457
+ fakeDelegationRisk: roleFallback.summary.fakeDelegationRisk,
458
+ },
459
+ git: {
460
+ isGitRepo,
461
+ changedFiles: gitEntries.length,
462
+ staged: gitSummary.staged.length,
463
+ unstaged: gitSummary.unstaged.length,
464
+ untracked: gitSummary.untracked.length,
465
+ mixed: gitSummary.mixed.length,
466
+ conflicts: gitSummary.conflicts.length,
467
+ stagedGenerated: gitSummary.stagedGenerated.map((entry) => entry.path),
468
+ dirtyGenerated: gitSummary.dirtyGenerated.map((entry) => entry.path),
469
+ },
470
+ checks,
285
471
  limits: [
286
472
  'Close gate is diagnostic. It reports ready/not-ready and does not modify files.',
287
473
  'A ready close gate does not replace user, reviewer, release, or owner acceptance when the project requires it.',
@@ -64,9 +64,10 @@ fs.writeFileSync(path.join(target, 'docs', 'productization-architecture.md'), '#
64
64
  fs.appendFileSync(path.join(target, '.gse', 'README.md'), '\nCanonical plan: `docs/productization-architecture.md`.\n', 'utf8')
65
65
 
66
66
  const adapterRun = run('generate-command-adapter.mjs', ['--target', target, '--host', 'all', '--json'])
67
- const helpRun = run('run-gse-command.mjs', ['--root', root, '--target', target, '--command', '/gse help', '--json'])
68
- const continueRun = run('run-gse-command.mjs', ['--root', root, '--target', target, '--command', '/gse continue', '--json'])
69
- const shortCliRun = run('gse.mjs', ['status', '--target', target, '--json'])
67
+ const helpRun = run('run-gse-command.mjs', ['--root', root, '--target', target, '--command', '/gse help', '--json'])
68
+ const continueRun = run('run-gse-command.mjs', ['--root', root, '--target', target, '--command', '/gse continue', '--json'])
69
+ const repairRun = run('run-gse-command.mjs', ['--root', root, '--target', target, '--command', '/gse repair', '--json'])
70
+ const shortCliRun = run('gse.mjs', ['status', '--target', target, '--json'])
70
71
 
71
72
  const full = profile === 'full'
72
73
  const statusRun = maybeRun(full, 'run-gse-command.mjs', ['--root', root, '--target', root, '--command', '/gse status', '--json'])
@@ -92,11 +93,19 @@ const publicReleaseExecuteRun = maybeRun(full, 'run-gse-command.mjs', ['--root',
92
93
  const doctorTargetRun = maybeRun(full, 'run-gse-command.mjs', ['--root', root, '--target', target, '--command', '/gse doctor', '--json'])
93
94
  const verifyRun = maybeRun(full, 'run-gse-command.mjs', ['--root', root, '--target', root, '--command', '/gse verify --profile lite', '--json'])
94
95
  const auditRun = maybeRun(full, 'run-gse-command.mjs', ['--root', root, '--target', target, '--command', '/gse audit', '--json'])
95
- const closeRun = maybeRun(full, 'run-gse-command.mjs', ['--root', root, '--target', target, '--command', '/gse close', '--json'])
96
- const changeRun = maybeRun(full, 'run-gse-command.mjs', ['--root', root, '--target', target, '--command', '/gse change add-login --level lite', '--execute', '--json'])
97
-
98
- const help = parseJson(helpRun.stdout)
99
- const cont = parseJson(continueRun.stdout)
96
+ const closeRun = maybeRun(full, 'run-gse-command.mjs', ['--root', root, '--target', target, '--command', '/gse close', '--json'])
97
+ const changeRun = maybeRun(full, 'run-gse-command.mjs', ['--root', root, '--target', target, '--command', '/gse change add-login --level lite', '--execute', '--json'])
98
+ if (full) {
99
+ const statePath = path.join(target, '.gse', 'state.json')
100
+ const stateForRepair = JSON.parse(fs.readFileSync(statePath, 'utf8'))
101
+ stateForRepair.residualRisks = Array.from({ length: 9 }, (_, index) => `Command repair fixture risk ${index + 1}.`)
102
+ fs.writeFileSync(statePath, JSON.stringify(stateForRepair, null, 2) + '\n', 'utf8')
103
+ }
104
+ const repairExecuteRun = maybeRun(full, 'run-gse-command.mjs', ['--root', root, '--target', target, '--command', '/gse repair --max-active-risks 3', '--execute', '--json'])
105
+
106
+ const help = parseJson(helpRun.stdout)
107
+ const cont = parseJson(continueRun.stdout)
108
+ const repair = parseJson(repairRun.stdout)
100
109
  const status = statusRun ? parseJson(statusRun.stdout) : null
101
110
  const doctor = doctorRun ? parseJson(doctorRun.stdout) : null
102
111
  const acceptance = acceptanceRun ? parseJson(acceptanceRun.stdout) : null
@@ -110,7 +119,9 @@ const doctorTarget = doctorTargetRun ? parseJson(doctorTargetRun.stdout) : null
110
119
  const verify = verifyRun ? parseJson(verifyRun.stdout) : null
111
120
  const audit = auditRun ? parseJson(auditRun.stdout) : null
112
121
  const close = closeRun ? parseJson(closeRun.stdout) : null
113
- const change = changeRun ? parseJson(changeRun.stdout) : null
122
+ const change = changeRun ? parseJson(changeRun.stdout) : null
123
+ const repairExecute = repairExecuteRun ? parseJson(repairExecuteRun.stdout) : null
124
+ const repairExecuteData = repairExecute ? parseJson(repairExecute.execution?.stdout ?? '') : null
114
125
  const doctorData = doctor ? parseJson(doctor.execution?.stdout ?? '') : null
115
126
  const ownerActionsData = ownerActions ? parseJson(ownerActions.execution?.stdout ?? '') : null
116
127
  const probeWaitingData = probeWaiting ? parseJson(probeWaiting.execution?.stdout ?? '') : null
@@ -148,17 +159,18 @@ const liteChecks = [
148
159
  check('CMDX02', 'target project initializes for command smoke', initRun.status === 0, initRun.command),
149
160
  check('CMDX03', 'host command adapters are generated', adapterRun.status === 0 && claudeCommand.includes('run-gse-command.mjs') && codexPointer.includes('run-gse-command.mjs') && copilotPointer.includes('GitHub Copilot GSE Adapter') && geminiPointer.includes('Gemini GSE Adapter'), 'generated supported host adapters'),
150
161
  check('CMDX04', '/gse help resolves command reference', helpRun.status === 0 && help?.route?.route === 'references/commands.md', '/gse help'),
151
- check('CMDX05', '/gse continue executes session prompt generator', continueRun.status === 0 && cont?.execution?.status === 0 && cont?.execution?.stdout?.includes('Use GSE to continue'), '/gse continue'),
152
- check('CMDX05b', 'short CLI wrapper routes to portable command runner', shortCliRun.status === 0 && shortCliRun.stdout.includes('"/gse status"') && shortCliRun.stdout.includes('"stateValid": true'), 'scripts/gse.mjs status --target <target> --json'),
162
+ check('CMDX05', '/gse continue executes hard preflight and compact state generator', continueRun.status === 0 && cont?.execution?.status === 0 && cont?.execution?.command?.includes('generate-continue-packet.mjs') && cont?.execution?.stdout?.includes('"compactState"'), '/gse continue'),
163
+ check('CMDX05c', '/gse repair executes state/evidence repair audit in read-only mode', repairRun.status === 0 && repair?.execution?.status === 0 && repair?.execution?.command?.includes('audit-state-repair.mjs') && repair?.execution?.stdout?.includes('"repairActions"'), '/gse repair'),
164
+ check('CMDX05b', 'short CLI wrapper routes to portable command runner', shortCliRun.status === 0 && shortCliRun.stdout.includes('"/gse status"') && shortCliRun.stdout.includes('"stateValid": true'), 'scripts/gse.mjs status --target <target> --json'),
153
165
  ]
154
166
 
155
167
  const fullChecks = full
156
168
  ? [
157
169
  check('CMDX06', '/gse status exposes GSE final-form progress when target is GSE skill', statusRun.status === 0 && status?.execution?.status === 0 && status?.execution?.stdout?.includes('fullFinalFormReadiness') && status?.execution?.stdout?.includes('pendingGateCount'), '/gse status'),
158
- check('CMDX07', '/gse doctor exposes GSE public acceptance blockers when target is GSE skill', doctorRun.status === 0 && doctor?.execution?.status === 0 && doctorData?.workflows?.publicAcceptanceDoctor === 'verified' && doctorData?.summary?.pendingGates > 0 && doctorData?.summary?.publicAccepted === 'not-accepted', '/gse doctor'),
170
+ check('CMDX07', '/gse doctor exposes GSE public acceptance evidence state when target is GSE skill', doctorRun.status === 0 && doctor?.execution?.status === 0 && doctorData?.workflows?.publicAcceptanceDoctor === 'verified' && doctorData?.summary?.pendingGates > 0 && doctorData?.summary?.publicAccepted === 'not-accepted', '/gse doctor'),
159
171
  check('CMDX08', '/gse acceptance aliases the public acceptance doctor', acceptanceRun.status === 0 && acceptance?.execution?.status === 0 && acceptance?.execution?.stdout?.includes('"publicAcceptanceDoctor": "verified"'), '/gse acceptance'),
160
- check('CMDX08b', '/gse owner-actions exposes compact owner/external action commands without claiming acceptance', ownerActionsRun.status === 0 && ownerActions?.execution?.status === 0 && ownerActionsData?.pendingGateCount === 7 && ownerActionsData?.publicAccepted === 'not-accepted' && ownerActionsData.actions?.every((item) => item.recordCommand && item.preflightCommand), '/gse owner-actions'),
161
- check('CMDX08c', '/gse owner-actions --compact returns owner packet without wrapper path noise', ownerActionsCompactRun.status === 0 && ownerActionsCompact?.pendingGateCount === 7 && ownerActionsCompact?.publicAccepted === 'not-accepted' && !ownerActionsCompactRun.stdout.includes(root) && !ownerActionsCompactRun.stdout.includes(process.execPath), '/gse owner-actions --json --compact'),
172
+ check('CMDX08b', '/gse owner-actions exposes compact owner/external action commands without claiming acceptance', ownerActionsRun.status === 0 && ownerActions?.execution?.status === 0 && ownerActionsData?.pendingGateCount > 0 && ownerActionsData?.publicAccepted === 'not-accepted' && ownerActionsData.actions?.every((item) => item.recordCommand && item.preflightCommand), '/gse owner-actions'),
173
+ check('CMDX08c', '/gse owner-actions --compact returns owner packet without wrapper path noise', ownerActionsCompactRun.status === 0 && ownerActionsCompact?.pendingGateCount > 0 && ownerActionsCompact?.publicAccepted === 'not-accepted' && !ownerActionsCompactRun.stdout.includes(root) && !ownerActionsCompactRun.stdout.includes(process.execPath), '/gse owner-actions --json --compact'),
162
174
  check('CMDX08c2', '/gse owner-actions --compact routes probe verification through portable command runner', ownerActionsCompactRun.status === 0 && ownerActionsCompact?.verificationCommands?.some((command) => command.includes('run-gse-command.mjs') && command.includes('/gse probe')) && !ownerActionsCompact?.verificationCommands?.some((command) => command.startsWith('node scripts/probe-public-external-gates.mjs')), '/gse owner-actions compact verificationCommands'),
163
175
  check('CMDX08d', '/gse probe runs as a waiting diagnostic without evidence inputs', probeWaitingRun.status === 0 && probeWaiting?.execution?.status === 0 && probeWaitingData?.status === 'waiting-for-input' && probeWaitingData?.summary?.checked === 0, '/gse probe'),
164
176
  check('CMDX08e', '/gse probe rejects placeholder public evidence through portable command runner', probeRejectRun.status !== 0 && probeReject?.execution?.status !== 0 && probeRejectData?.status === 'failed' && probeRejectData?.probes?.some((item) => item.errors?.some((error) => error.includes('not a placeholder'))), '/gse probe --public-repo-url https://github.com/example/gse'),
@@ -174,14 +186,15 @@ const fullChecks = full
174
186
  check('CMDX10', '/gse verify executes validation profile runner', verifyRun.status === 0 && verify?.execution?.status === 0 && verify?.execution?.stdout?.includes('"profile": "lite"') && verify?.execution?.stdout?.includes('"validationProfile": "verified"'), '/gse verify --profile lite'),
175
187
  check('CMDX11', '/gse audit executes target project doctor', diagnosticOk(auditRun, audit), '/gse audit'),
176
188
  check('CMDX12', '/gse close executes close gate', diagnosticOk(closeRun, close), '/gse close'),
177
- check('CMDX13', '/gse change executes change pack creation only with --execute', changeRun.status === 0 && change?.execution?.status === 0 && fs.existsSync(path.join(target, '.gse', 'changes', 'add-login', 'brief.md')), '/gse change --execute'),
178
- ]
189
+ check('CMDX13', '/gse change executes change pack creation only with --execute', changeRun.status === 0 && change?.execution?.status === 0 && fs.existsSync(path.join(target, '.gse', 'changes', 'add-login', 'brief.md')), '/gse change --execute'),
190
+ check('CMDX14', '/gse repair --execute performs reversible residual risk compaction only when requested', repairExecuteRun.status === 0 && repairExecute?.execution?.status === 0 && repairExecuteData?.summary?.writes === 1 && repairExecuteData?.writes?.some((item) => item.action === 'compact-residual-risks') && fs.existsSync(path.join(target, '.gse', 'backups')), '/gse repair --execute'),
191
+ ]
179
192
  : []
180
193
 
181
194
  const checks = [...liteChecks, ...fullChecks]
182
195
  const passed = checks.filter((item) => item.status === 'passed').length
183
196
  const failed = checks.length - passed
184
- const commandRuns = [initRun, adapterRun, helpRun, continueRun, shortCliRun, statusRun, doctorRun, acceptanceRun, ownerActionsRun, ownerActionsCompactRun, probeWaitingRun, probeRejectRun, releaseDryRun, releaseExecuteRun, packageDryRun, packageExecuteRun, installDryRun, installExecuteRun, publicReleaseDryRun, publicReleaseExecuteRun, doctorTargetRun, verifyRun, auditRun, closeRun, changeRun].filter(Boolean)
197
+ const commandRuns = [initRun, adapterRun, helpRun, continueRun, repairRun, shortCliRun, statusRun, doctorRun, acceptanceRun, ownerActionsRun, ownerActionsCompactRun, probeWaitingRun, probeRejectRun, releaseDryRun, releaseExecuteRun, packageDryRun, packageExecuteRun, installDryRun, installExecuteRun, publicReleaseDryRun, publicReleaseExecuteRun, doctorTargetRun, verifyRun, auditRun, closeRun, changeRun, repairExecuteRun].filter(Boolean)
185
198
  const report = {
186
199
  root,
187
200
  generatedAt: new Date().toISOString(),
@@ -36,9 +36,10 @@ const requiredCommands = [
36
36
  '/gse init',
37
37
  '/gse adopt',
38
38
  '/gse continue',
39
- '/gse status',
40
- '/gse doctor',
41
- '/gse acceptance',
39
+ '/gse status',
40
+ '/gse doctor',
41
+ '/gse repair',
42
+ '/gse acceptance',
42
43
  '/gse owner-actions',
43
44
  '/gse probe',
44
45
  '/gse release',
@@ -68,8 +69,9 @@ const checks = [
68
69
  check('CMD12', 'public-release command is documented as checklist/runway and not acceptance', skill.includes('/gse public-release') && commands.includes('/gse public-release') && commands.includes('ordered public release checklist') && commands.includes('does not publish, approve, configure, or accept'), 'SKILL.md, references/commands.md'),
69
70
  check('CMD13', 'package/install commands expose installability without claiming publication', skill.includes('/gse package') && skill.includes('/gse install') && commands.includes('/gse package') && commands.includes('/gse install') && commands.includes('Only write a package when the command is run with `--execute`') && commands.includes('Only write the install target when the command is run with `--execute`') && commands.includes('does not publish to a registry'), 'SKILL.md, references/commands.md'),
70
71
  check('CMD14', 'close command is documented as read-only readiness check', commands.includes('| `/gse close` | Check whether the current slice is ready to close') && commands.includes('/gse close` is read-only in the portable command runner') && commands.includes('close-change.mjs --target <project-root> --change-id <change-id>'), 'references/commands.md'),
71
- check('CMD15', 'learn command is documented as execute-gated learning capture', commands.includes('| `/gse learn` | Record a reusable lesson') && commands.includes('/gse learn --summary') && commands.includes('Append the entry only when `--execute` is supplied') && skill.includes('scripts/record-learning.mjs'), 'SKILL.md, references/commands.md'),
72
- ]
72
+ check('CMD15', 'learn command is documented as execute-gated learning capture', commands.includes('| `/gse learn` | Record a reusable lesson') && commands.includes('/gse learn --summary') && commands.includes('Append the entry only when `--execute` is supplied') && skill.includes('scripts/record-learning.mjs'), 'SKILL.md, references/commands.md'),
73
+ check('CMD16', 'repair command is documented as diagnostic by default and execute-gated for safe repair', commands.includes('| `/gse repair` | Diagnose or repair stale state') && commands.includes('/gse repair') && commands.includes('Automatic writes are limited to reversible residual-risk compaction') && skill.includes('audit-state-repair.mjs'), 'SKILL.md, references/commands.md'),
74
+ ]
73
75
 
74
76
  const passed = checks.filter((item) => item.status === 'passed').length
75
77
  const failed = checks.length - passed