@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
@@ -88,9 +88,10 @@ function actionFor(row, publicGate) {
88
88
 
89
89
  function renderPacket(audit) {
90
90
  const publicDoctor = runPublicDoctor()
91
- const matrix = audit.matrix ?? []
92
- const verified = matrix.filter((row) => row.status === 'verified')
93
- const pending = matrix.filter((row) => row.status !== 'verified')
91
+ const matrix = audit.matrix ?? []
92
+ const verified = matrix.filter((row) => row.status === 'verified')
93
+ const pending = matrix.filter((row) => row.status === 'owner-required' || row.status === 'external-required')
94
+ const notClaimed = matrix.filter((row) => row.status === 'not-claimed')
94
95
  const lines = []
95
96
  lines.push('# GSE Final Acceptance Packet')
96
97
  lines.push('')
@@ -105,8 +106,9 @@ function renderPacket(audit) {
105
106
  lines.push('')
106
107
  lines.push('- Local readiness: ' + (audit.workflows?.finalReadinessMatrix ?? 'unknown'))
107
108
  lines.push('- Public accepted: ' + (audit.workflows?.publicAccepted ?? 'unknown'))
108
- lines.push('- Verified rows: ' + verified.length)
109
- lines.push('- Pending owner/external rows: ' + pending.length)
109
+ lines.push('- Verified rows: ' + verified.length)
110
+ lines.push('- Pending owner/external rows: ' + pending.length)
111
+ lines.push('- Optional not-claimed rows: ' + notClaimed.length)
110
112
  lines.push('')
111
113
  lines.push('## Verified Local Capabilities')
112
114
  lines.push('')
@@ -114,7 +116,7 @@ function renderPacket(audit) {
114
116
  lines.push('')
115
117
  lines.push('## Pending Acceptance Gates')
116
118
  lines.push('')
117
- for (const row of pending) {
119
+ for (const row of pending) {
118
120
  lines.push('### ' + row.area)
119
121
  lines.push('')
120
122
  lines.push('- Status: ' + row.status)
@@ -122,7 +124,19 @@ function renderPacket(audit) {
122
124
  lines.push('- Required action: ' + actionFor(row, publicDoctor.gates.get(row.area)))
123
125
  lines.push('- Acceptance rule: do not mark accepted until the evidence is real, dated, and re-audited.')
124
126
  lines.push('')
125
- }
127
+ }
128
+ if (pending.length === 0) {
129
+ lines.push('- No owner/external acceptance gates are pending.')
130
+ lines.push('')
131
+ }
132
+ if (notClaimed.length > 0) {
133
+ lines.push('## Optional Not-Claimed Rows')
134
+ lines.push('')
135
+ for (const row of notClaimed) {
136
+ lines.push('- ' + row.area + ': not claimed; evidence boundary: ' + row.evidence)
137
+ }
138
+ lines.push('')
139
+ }
126
140
  lines.push('## Re-Verification Commands')
127
141
  lines.push('')
128
142
  lines.push('```bash')
@@ -172,7 +186,8 @@ const report = {
172
186
  written: dryRun ? 0 : 1,
173
187
  summary: {
174
188
  verifiedRows: audit.data.matrix.filter((row) => row.status === 'verified').length,
175
- pendingRows: audit.data.matrix.filter((row) => row.status !== 'verified').length,
189
+ pendingRows: audit.data.matrix.filter((row) => row.status === 'owner-required' || row.status === 'external-required').length,
190
+ notClaimedRows: audit.data.matrix.filter((row) => row.status === 'not-claimed').length,
176
191
  publicAccepted: audit.data.workflows?.publicAccepted ?? 'unknown',
177
192
  },
178
193
  }
@@ -60,12 +60,13 @@ const publicAcceptanceData = parseJson(publicAcceptance.stdout)
60
60
  const hostRuntimeData = parseJson(hostRuntime.stdout)
61
61
 
62
62
  const matrix = finalReadinessData?.matrix ?? []
63
- const verifiedRows = countBy(matrix, 'verified')
64
- const ownerRequiredRows = countBy(matrix, 'owner-required')
65
- const externalRequiredRows = countBy(matrix, 'external-required')
66
- const acceptedRows = countBy(matrix, 'accepted')
67
- const totalRows = matrix.length
68
- const localReadyRows = verifiedRows + acceptedRows
63
+ const verifiedRows = countBy(matrix, 'verified')
64
+ const ownerRequiredRows = countBy(matrix, 'owner-required')
65
+ const externalRequiredRows = countBy(matrix, 'external-required')
66
+ const notClaimedRows = countBy(matrix, 'not-claimed')
67
+ const acceptedRows = countBy(matrix, 'accepted')
68
+ const totalRows = matrix.length
69
+ const localReadyRows = verifiedRows + acceptedRows
69
70
  const publicAccepted = finalReadinessData?.workflows?.publicAccepted ?? 'unknown'
70
71
  const pendingGates = publicAcceptanceData?.pendingGates ?? matrix.filter((row) => row.status !== 'verified')
71
72
  const licenseDecisionVerified = matrix.some((row) => row.area === 'License decision' && (row.status === 'verified' || row.status === 'accepted'))
@@ -78,10 +79,10 @@ const cannotClaim = [
78
79
  'native slash-command support unless real host runtime evidence exists',
79
80
  ]
80
81
 
81
- const localRows = totalRows - ownerRequiredRows - externalRequiredRows
82
- const localEngineeringScore = percent(localReadyRows, localRows)
83
- const finalFormScore = publicAccepted === 'verified' ? 100 : percent(localReadyRows, totalRows)
84
- const status = publicAccepted === 'verified' ? 'accepted' : 'verified-with-external-blockers'
82
+ const localRows = totalRows - ownerRequiredRows - externalRequiredRows - notClaimedRows
83
+ const localEngineeringScore = percent(localReadyRows, localRows)
84
+ const finalFormScore = publicAccepted === 'verified' ? 100 : percent(localReadyRows, totalRows)
85
+ const status = publicAccepted === 'verified' ? 'accepted' : 'release-evidence-pending'
85
86
 
86
87
  const report = {
87
88
  schemaVersion: 1,
@@ -96,14 +97,15 @@ const report = {
96
97
  readiness: {
97
98
  totalRows,
98
99
  localRows,
99
- verifiedRows,
100
- acceptedRows,
101
- ownerRequiredRows,
102
- externalRequiredRows,
103
- publicAccepted,
100
+ verifiedRows,
101
+ acceptedRows,
102
+ ownerRequiredRows,
103
+ externalRequiredRows,
104
+ notClaimedRows,
105
+ publicAccepted,
104
106
  pendingGateCount: pendingGates.length,
105
107
  },
106
- blockers: pendingGates.map((gate) => ({
108
+ pendingReleaseEvidence: pendingGates.map((gate) => ({
107
109
  area: gate.area,
108
110
  status: gate.status,
109
111
  owner: gate.owner ?? (gate.status === 'owner-required' ? 'project owner' : 'external system or host'),
@@ -149,15 +151,15 @@ function renderMarkdown(data) {
149
151
  lines.push('- Full final-form readiness: ' + data.scores.fullFinalFormReadiness + '%')
150
152
  lines.push('- Scoring basis: ' + data.scores.scoringBasis)
151
153
  lines.push('- Public accepted: ' + data.readiness.publicAccepted)
152
- lines.push('- Matrix rows: ' + data.readiness.verifiedRows + ' verified, ' + data.readiness.ownerRequiredRows + ' owner-required, ' + data.readiness.externalRequiredRows + ' external-required, ' + data.readiness.totalRows + ' total')
154
+ lines.push('- Matrix rows: ' + data.readiness.verifiedRows + ' verified, ' + data.readiness.ownerRequiredRows + ' owner-required, ' + data.readiness.externalRequiredRows + ' external-required, ' + data.readiness.notClaimedRows + ' not-claimed, ' + data.readiness.totalRows + ' total')
153
155
  lines.push('- Local rows: ' + data.readiness.localRows)
154
156
  lines.push('')
155
- lines.push('## Remaining Blockers')
156
- lines.push('')
157
- if (data.blockers.length === 0) {
158
- lines.push('- None reported by final readiness. Re-run close gate and release audits before claiming completion.')
159
- } else {
160
- for (const blocker of data.blockers) {
157
+ lines.push('## Pending Release Evidence')
158
+ lines.push('')
159
+ if (data.pendingReleaseEvidence.length === 0) {
160
+ lines.push('- None reported by final readiness. Re-run close gate and release audits before claiming completion.')
161
+ } else {
162
+ for (const blocker of data.pendingReleaseEvidence) {
161
163
  lines.push('- ' + blocker.area + ': ' + blocker.status)
162
164
  lines.push(' - Owner: ' + blocker.owner)
163
165
  lines.push(' - Evidence now: ' + blocker.evidence)
@@ -108,21 +108,28 @@ function commandFor(hostFamily) {
108
108
  ].join(' ')
109
109
  }
110
110
 
111
- function renderHandoff(audit, hostRows) {
112
- const inventory = audit.inventory ?? {}
113
- const lines = []
114
- lines.push('# GSE Host Runtime Evidence Handoff')
111
+ function renderHandoff(audit, hostRows) {
112
+ const inventory = audit.inventory ?? {}
113
+ const lines = []
114
+ lines.push('# GSE Host Runtime Evidence Handoff')
115
115
  lines.push('')
116
116
  lines.push('Generated: ' + new Date().toISOString())
117
117
  lines.push('Root: ' + displayRoot)
118
118
  lines.push('')
119
- lines.push('## Purpose')
120
- lines.push('')
121
- lines.push('Turn cross-host support into auditable runtime evidence. Generated adapters, docs, or command pointers are useful setup, but they are not proof that a host actually invoked GSE.')
122
- lines.push('')
123
- lines.push('## Current Runtime Evidence')
124
- lines.push('')
125
- lines.push('- Host runtime invocation records: ' + (inventory.records ?? 0))
119
+ lines.push('## Purpose')
120
+ lines.push('')
121
+ lines.push('Turn cross-host support into auditable runtime evidence. Generated adapters, docs, or command pointers are useful setup, but they are not proof that a host actually invoked GSE.')
122
+ lines.push('')
123
+ lines.push('## Fast Path')
124
+ lines.push('')
125
+ lines.push('- Native slash-command evidence is the final external gate.')
126
+ lines.push('- If a host can invoke `/gse continue` natively, record that first.')
127
+ lines.push('- Use portable text-command records only when native slash-command proof is unavailable.')
128
+ lines.push('- Keep host capability status separate from portable `.gse/` workflow status.')
129
+ lines.push('')
130
+ lines.push('## Current Runtime Evidence')
131
+ lines.push('')
132
+ lines.push('- Host runtime invocation records: ' + (inventory.records ?? 0))
126
133
  lines.push('- Verified or accepted records: ' + (inventory.closeableRecords ?? 0))
127
134
  lines.push('- Hosts with records: ' + (inventory.hosts?.length ? inventory.hosts.join(', ') : 'none'))
128
135
  lines.push('- Native slash-command records: ' + (inventory.nativeSlashCommandRecords ?? 0))
@@ -164,11 +171,11 @@ function renderHandoff(audit, hostRows) {
164
171
  lines.push('- Keep host capability status separate from portable `.gse/` workflow status.')
165
172
  lines.push('- Treat subagents, MCP, LSP, browser tools, hooks, and plugins as host/session-specific until current evidence proves them.')
166
173
  lines.push('')
167
- lines.push('## Next Action')
168
- lines.push('')
169
- lines.push('Record real invocation evidence for Claude Code-style, Hermes/AION-style, WorkBuddy/other IDE agents, and any host-native slash-command mechanism that becomes available.')
170
- return lines.join('\n') + '\n'
171
- }
174
+ lines.push('## Next Action')
175
+ lines.push('')
176
+ lines.push('Record real invocation evidence for Claude Code-style, Hermes/AION-style, WorkBuddy/other IDE agents, and any host-native slash-command mechanism that becomes available. Start with native slash-command proof when the host supports it.')
177
+ return lines.join('\n') + '\n'
178
+ }
172
179
 
173
180
  const audit = runHostAudit()
174
181
  if (!audit.ok) {
@@ -0,0 +1,216 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs'
3
+ import path from 'node:path'
4
+ import { spawnSync } from 'node:child_process'
5
+
6
+ const args = process.argv.slice(2)
7
+
8
+ function readArg(name, fallback = null) {
9
+ const index = args.indexOf(name)
10
+ if (index === -1) return fallback
11
+ return args[index + 1] ?? fallback
12
+ }
13
+
14
+ const root = path.resolve(readArg('--root', path.join(import.meta.dirname, '..')))
15
+ const target = path.resolve(readArg('--target', root))
16
+ const installedRoot = readArg('--installed-root', null)
17
+ const execute = args.includes('--execute')
18
+ const jsonOnly = args.includes('--json')
19
+ const skipReleaseBundle = args.includes('--skip-release-bundle')
20
+ const packageSmoke = args.includes('--package-smoke')
21
+ const canonicalOut = path.join(root, '.gse', 'maintenance', 'latest-maintenance-snapshot.json')
22
+ const out = path.resolve(readArg('--out', canonicalOut))
23
+ const isCanonicalOut = out === path.resolve(canonicalOut)
24
+
25
+ function exists(relativePath) {
26
+ return fs.existsSync(path.join(root, relativePath))
27
+ }
28
+
29
+ function runNode(script, commandArgs, options = {}) {
30
+ const command = [process.execPath, path.join(root, 'scripts', script), ...commandArgs]
31
+ const result = spawnSync(command[0], command.slice(1), {
32
+ cwd: root,
33
+ encoding: 'utf8',
34
+ windowsHide: true,
35
+ ...options,
36
+ })
37
+ const stdout = (result.stdout ?? '').trim()
38
+ let parsed = null
39
+ try {
40
+ parsed = JSON.parse(stdout)
41
+ } catch {
42
+ parsed = null
43
+ }
44
+ const failed = parsed?.summary?.failed
45
+ const ok = (result.status ?? 1) === 0 || failed === 0
46
+ return {
47
+ command: command.join(' '),
48
+ status: result.status ?? 1,
49
+ ok,
50
+ summary: parsed?.summary ?? null,
51
+ workflows: parsed?.workflows ?? null,
52
+ stdout,
53
+ stderr: (result.stderr ?? '').trim(),
54
+ }
55
+ }
56
+
57
+ function maintenanceChecks() {
58
+ const checks = [
59
+ {
60
+ id: 'MS01',
61
+ area: 'maintenance cadence',
62
+ script: 'audit-maintenance-cadence.mjs',
63
+ args: ['--root', root, '--json'],
64
+ },
65
+ {
66
+ id: 'MS02',
67
+ area: 'final-form roadmap',
68
+ script: 'audit-final-form-roadmap.mjs',
69
+ args: ['--root', root, '--json'],
70
+ },
71
+ {
72
+ id: 'MS03',
73
+ area: 'state freshness',
74
+ script: 'audit-state-freshness.mjs',
75
+ args: ['--root', root, '--json'],
76
+ },
77
+ {
78
+ id: 'MS04',
79
+ area: 'continue preflight',
80
+ script: 'run-gse-command.mjs',
81
+ args: ['--root', root, '--target', target, '--command', '/gse continue', '--json', '--compact'],
82
+ },
83
+ {
84
+ id: 'MS05',
85
+ area: 'evidence levels',
86
+ script: 'audit-evidence-levels.mjs',
87
+ args: ['--root', root, '--target', target, '--json'],
88
+ },
89
+ {
90
+ id: 'MS06',
91
+ area: 'learning drift',
92
+ script: 'audit-learning-drift.mjs',
93
+ args: ['--root', root, '--target', target, '--json'],
94
+ },
95
+ {
96
+ id: 'MS07',
97
+ area: 'public acceptance',
98
+ script: 'audit-public-acceptance-readiness.mjs',
99
+ args: ['--root', root, '--json'],
100
+ },
101
+ {
102
+ id: 'MS08',
103
+ area: 'installed sync',
104
+ script: 'audit-installed-sync.mjs',
105
+ args: installedRoot
106
+ ? ['--root', root, '--installed-root', path.resolve(installedRoot), '--json']
107
+ : ['--root', root, '--json'],
108
+ },
109
+ {
110
+ id: 'MS09',
111
+ area: 'session sync',
112
+ script: 'audit-session-sync.mjs',
113
+ args: ['--root', root, '--json'],
114
+ },
115
+ ]
116
+ if (!skipReleaseBundle) {
117
+ checks.push({
118
+ id: 'MS10',
119
+ area: 'release bundle freshness',
120
+ script: 'audit-release-bundle.mjs',
121
+ args: ['--root', root, '--json'],
122
+ })
123
+ }
124
+ return checks.filter((item) => exists(path.join('scripts', item.script)))
125
+ }
126
+
127
+ const checks = packageSmoke
128
+ ? maintenanceChecks().filter((item) => item.id !== 'MS02')
129
+ : maintenanceChecks()
130
+ const results = checks.map((item) => {
131
+ const run = runNode(item.script, item.args)
132
+ return {
133
+ id: item.id,
134
+ area: item.area,
135
+ status: run.ok ? 'passed' : 'failed',
136
+ command: run.command,
137
+ summary: run.summary,
138
+ workflows: run.workflows,
139
+ stderr: run.stderr,
140
+ }
141
+ })
142
+
143
+ const passed = results.filter((item) => item.status === 'passed').length
144
+ const failed = results.length - passed
145
+ const installedSync = results.find((item) => item.id === 'MS08')
146
+ const releaseBundle = results.find((item) => item.id === 'MS10')
147
+ const report = {
148
+ schemaVersion: 1,
149
+ root,
150
+ target,
151
+ installedRoot: installedRoot ? path.resolve(installedRoot) : null,
152
+ generatedAt: new Date().toISOString(),
153
+ execute,
154
+ out,
155
+ summary: {
156
+ status: failed === 0 ? 'passed' : 'failed',
157
+ passed,
158
+ failed,
159
+ total: results.length,
160
+ installedSyncMode: installedRoot ? 'installed-root' : 'package-only',
161
+ releaseBundleChecked: Boolean(releaseBundle),
162
+ packageSmoke,
163
+ },
164
+ workflows: {
165
+ maintenanceSnapshot: failed === 0 ? 'verified' : 'failed',
166
+ installedSync: installedSync?.workflows?.installedSync ?? (installedRoot ? 'unknown' : 'package-only'),
167
+ releaseBundleFreshness: releaseBundle?.workflows?.releaseBundle ?? (skipReleaseBundle ? 'skipped' : 'unknown'),
168
+ },
169
+ results,
170
+ limits: [
171
+ 'This snapshot proves recurring maintenance checks ran at a point in time.',
172
+ 'When writing the canonical latest-maintenance-snapshot.json, failed checks are written to latest-maintenance-snapshot.failed.json and do not overwrite the last passing snapshot.',
173
+ packageSmoke ? 'Package smoke mode skips source-worktree final-form roadmap freshness because package installs intentionally omit full local evidence history.' : '',
174
+ 'It does not prove native host slash-command support.',
175
+ 'Without --installed-root, installed sync is package-only and does not prove an installed copy is fresh.',
176
+ ].filter(Boolean),
177
+ }
178
+
179
+ function writeSnapshotFiles(targetPath, snapshot) {
180
+ fs.mkdirSync(path.dirname(targetPath), { recursive: true })
181
+ fs.writeFileSync(targetPath, JSON.stringify(snapshot, null, 2) + '\n', 'utf8')
182
+ const mdPath = targetPath.replace(/\.json$/i, '.md')
183
+ fs.writeFileSync(mdPath, [
184
+ '# GSE Maintenance Snapshot',
185
+ '',
186
+ 'Generated: ' + snapshot.generatedAt,
187
+ 'Root: ' + snapshot.root,
188
+ 'Target: ' + snapshot.target,
189
+ 'Status: ' + snapshot.summary.status,
190
+ '',
191
+ '## Checks',
192
+ '',
193
+ ...snapshot.results.map((item) => `- ${item.status === 'passed' ? '[x]' : '[ ]'} ${item.id} ${item.area}: ${item.summary?.status ?? item.status}`),
194
+ '',
195
+ '## Limits',
196
+ '',
197
+ ...snapshot.limits.map((item) => '- ' + item),
198
+ '',
199
+ ].join('\n'), 'utf8')
200
+ }
201
+
202
+ if (execute) {
203
+ const targetOut = failed > 0 && isCanonicalOut
204
+ ? out.replace(/\.json$/i, '.failed.json')
205
+ : out
206
+ report.writtenTo = targetOut
207
+ report.canonicalWritePolicy = failed > 0 && isCanonicalOut
208
+ ? 'failed-canonical-write-isolated'
209
+ : 'written'
210
+ writeSnapshotFiles(targetOut, report)
211
+ }
212
+
213
+ if (jsonOnly) console.log(JSON.stringify(report, null, 2))
214
+ else console.log(JSON.stringify(report, null, 2))
215
+
216
+ if (failed > 0) process.exit(1)
@@ -71,19 +71,29 @@ function renderHandoff(doctor) {
71
71
  lines.push('Root: ' + displayRoot)
72
72
  lines.push('Release label: ' + releaseLabel)
73
73
  lines.push('')
74
- lines.push('## Purpose')
75
- lines.push('')
76
- lines.push('Give the owner and future agents one executable checklist for public acceptance. This handoff does not choose a license, publish a package, configure a repository, approve a marketplace listing, or prove host-native slash-command support.')
77
- lines.push('')
78
- lines.push('## Current Boundary')
79
- lines.push('')
74
+ lines.push('## Purpose')
75
+ lines.push('')
76
+ lines.push('Give the owner and future agents one executable checklist for public acceptance. This handoff does not choose a license, publish a package, configure a repository, approve a marketplace listing, or prove optional host-native slash-command support.')
77
+ lines.push('')
78
+ lines.push('## Fast Path')
79
+ lines.push('')
80
+ lines.push('- Portable `/gse` command execution is the core command path.')
81
+ lines.push('- Native slash-command evidence is optional per host adapter, not a GSE core completion gate.')
82
+ lines.push('- Do not treat portable text-command records or generated pointers as native proof for a host adapter.')
83
+ lines.push('')
84
+ lines.push('## Current Boundary')
85
+ lines.push('')
80
86
  lines.push('- Public accepted: ' + (doctor.summary?.publicAccepted ?? doctor.workflows?.publicAccepted ?? 'unknown'))
81
87
  lines.push('- Pending owner/external gates: ' + gates.length)
82
88
  lines.push('- Source of truth: `scripts/audit-public-acceptance-readiness.mjs` and `references/final-readiness.md`')
83
89
  lines.push('')
84
90
  lines.push('## Execution Order')
85
91
  lines.push('')
86
- for (const gate of gates) {
92
+ if (gates.length === 0) {
93
+ lines.push('- No owner/external gates are pending. Re-run final readiness and close gate before publishing a final claim.')
94
+ lines.push('')
95
+ }
96
+ for (const gate of gates) {
87
97
  lines.push('### ' + stageFor(gate) + ' - ' + gate.area)
88
98
  lines.push('')
89
99
  lines.push('- Current status: ' + gate.status)
@@ -127,13 +137,15 @@ function renderHandoff(doctor) {
127
137
  lines.push('')
128
138
  lines.push('## Next Action')
129
139
  lines.push('')
130
- if (gateNames.has('License decision')) {
131
- lines.push('Start with the owner-required license decision and public security contact, then attach public repository, CI, publication, and host runtime evidence as those systems become available.')
132
- } else {
133
- lines.push('Start with the public security contact, then attach public repository, CI, publication, and host runtime evidence as those systems become available.')
134
- }
135
- return lines.join('\n') + '\n'
136
- }
140
+ if (gates.length === 0) {
141
+ lines.push('No owner/external acceptance gate is pending. Optional host-native slash-command support can be recorded later per host adapter if a host exposes it.')
142
+ } else if (gateNames.has('License decision')) {
143
+ lines.push('Start with the owner-required license decision and public security contact, then attach public repository, CI, publication, and host runtime evidence as those systems become available. Record native slash-command evidence first when the host supports it.')
144
+ } else {
145
+ lines.push('Start with the public security contact, then attach public repository, CI, publication, and host runtime evidence as those systems become available. Record native slash-command evidence first when the host supports it.')
146
+ }
147
+ return lines.join('\n') + '\n'
148
+ }
137
149
 
138
150
  const doctor = runDoctor()
139
151
  if (!doctor.ok) {
@@ -88,8 +88,9 @@ function render(manifest) {
88
88
  lines.push('## Claim Boundary')
89
89
  lines.push('')
90
90
  lines.push('- Local validation does not mean public acceptance.')
91
- lines.push('- Native slash-command support requires a real host invocation record.')
92
- lines.push('- Owner and external gates must be recorded with accepted evidence before GSE can claim accepted public release readiness.')
91
+ lines.push('- Portable command execution is the GSE core command path.')
92
+ lines.push('- Native slash-command support requires a real host invocation record only when a host adapter claims it.')
93
+ lines.push('- Owner and external gates must be recorded with accepted evidence before GSE can claim accepted public release readiness.')
93
94
  lines.push('')
94
95
  lines.push('## Action Groups')
95
96
  lines.push('')
@@ -132,7 +133,7 @@ function render(manifest) {
132
133
  lines.push('## Limits')
133
134
  lines.push('')
134
135
  lines.push('- This plan is generated from the current release status manifest.')
135
- lines.push('- It does not select a license, publish a package, configure a repository, run public CI, approve a marketplace listing, or prove host-native slash-command support.')
136
+ lines.push('- It does not select a license, publish a package, configure a repository, run public CI, approve a marketplace listing, or prove optional host-native slash-command support.')
136
137
  return lines.join('\n') + '\n'
137
138
  }
138
139
 
@@ -81,7 +81,8 @@ try {
81
81
  publicAccepted: workflowStatus(finalReadiness.data, 'publicAccepted'),
82
82
  finalReadinessMatrix: workflowStatus(finalReadiness.data, 'finalReadinessMatrix'),
83
83
  localValidationDoesNotMeanPublicAcceptance: true,
84
- nativeSlashCommandRequiresHostRecord: true,
84
+ nativeSlashCommandRequiresHostRecord: true,
85
+ nativeSlashCommandIsOptionalAdapterClaim: true,
85
86
  },
86
87
  readiness: {
87
88
  verified: compactRows(matrix, 'verified'),
@@ -160,8 +161,8 @@ try {
160
161
  },
161
162
  limits: [
162
163
  'This manifest is generated from local audits and evidence records.',
163
- 'It does not choose a license, publish a package, configure a public repository, run public CI, approve a marketplace listing, or prove native slash-command support.',
164
- 'Owner-required and external-required gates remain incomplete until accepted evidence records promote them.',
164
+ 'It does not prove optional native slash-command support for any host adapter.',
165
+ 'Owner-required and external-required gates remain incomplete until accepted evidence records promote them.',
165
166
  ],
166
167
  }
167
168
  } catch (error) {