@t275005746/gse 0.1.0 → 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 (228) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.yml +42 -42
  2. package/.github/ISSUE_TEMPLATE/change_request.yml +50 -50
  3. package/.github/ISSUE_TEMPLATE/config.yml +5 -5
  4. package/.github/PULL_REQUEST_TEMPLATE.md +38 -38
  5. package/.github/workflows/validate-gse.yml +33 -33
  6. package/.gse/README.md +18 -18
  7. package/.gse/gse-development-protocol.md +50 -50
  8. package/.gse/project-profile.md +29 -29
  9. package/.gse/quality-gates.md +25 -25
  10. package/.gse/releases/public-registry-publication-npm.md +49 -0
  11. package/.gse/releases/public-release-owner-required.md +65 -65
  12. package/.gse/releases/public-security-contact-owner-required.md +45 -45
  13. package/.gse/state.json +147 -27
  14. package/CHANGELOG.md +31 -15
  15. package/CONTRIBUTING.md +64 -64
  16. package/LICENSE +21 -21
  17. package/README.md +14 -7
  18. package/README.zh-CN.md +14 -7
  19. package/SECURITY.md +41 -41
  20. package/SKILL.md +32 -12
  21. package/SUPPORT.md +38 -38
  22. package/assets/marketplace/README.md +7 -7
  23. package/assets/marketplace/gse-listing.json +75 -75
  24. package/assets/templates/acceptance-execution-packet.md +73 -73
  25. package/assets/templates/adr.md +14 -14
  26. package/assets/templates/change-brief.md +16 -16
  27. package/assets/templates/design.md +18 -18
  28. package/assets/templates/dispatch-packet.md +100 -92
  29. package/assets/templates/evidence.md +15 -9
  30. package/assets/templates/execution-quality-pack.md +65 -65
  31. package/assets/templates/goal-map.md +21 -21
  32. package/assets/templates/host-adapter.md +48 -48
  33. package/assets/templates/host-ui-invocation-record.md +42 -42
  34. package/assets/templates/incident-review.md +60 -60
  35. package/assets/templates/public-channel-publication-record.md +49 -49
  36. package/assets/templates/public-ci-run-record.md +53 -53
  37. package/assets/templates/public-release-record.md +65 -65
  38. package/assets/templates/public-repository-settings-record.md +59 -59
  39. package/assets/templates/public-security-contact-record.md +45 -45
  40. package/assets/templates/release-trust-record.md +32 -32
  41. package/assets/templates/review.md +18 -18
  42. package/assets/templates/role-fallback-packet.md +49 -0
  43. package/assets/templates/spec.md +16 -16
  44. package/assets/templates/target-adoption-evidence.md +29 -29
  45. package/assets/templates/tasks.md +17 -17
  46. package/assets/templates/update-release-acceptance-record.md +58 -58
  47. package/examples/README.md +22 -22
  48. package/examples/agent-runtime-host/.claude/gse-adapter.md +6 -6
  49. package/examples/agent-runtime-host/.codex/gse-adapter.md +7 -7
  50. package/examples/agent-runtime-host/.gse/goal-map.md +7 -7
  51. package/examples/agent-runtime-host/.gse/project-profile.md +27 -27
  52. package/examples/agent-runtime-host/.gse/tooling.md +5 -5
  53. package/examples/agent-runtime-host/.mcp.json +9 -9
  54. package/examples/agent-runtime-host/AGENTS.md +5 -5
  55. package/examples/agent-runtime-host/README.md +10 -10
  56. package/examples/agent-runtime-host/docs/model-routing.md +5 -5
  57. package/examples/cli-tool/.gse/project-profile.md +21 -21
  58. package/examples/cli-tool/AGENTS.md +5 -5
  59. package/examples/cli-tool/README.md +12 -12
  60. package/examples/cli-tool/package.json +21 -21
  61. package/examples/small-app/.env.example +2 -2
  62. package/examples/small-app/.github/workflows/ci.yml +8 -8
  63. package/examples/small-app/AGENTS.md +5 -5
  64. package/examples/small-app/README.md +12 -12
  65. package/examples/small-app/package.json +26 -26
  66. package/examples/small-app/playwright.config.ts +4 -4
  67. package/package.json +52 -44
  68. package/references/adoption-recipes.md +171 -171
  69. package/references/agent-roles.md +42 -21
  70. package/references/architecture-health.md +101 -101
  71. package/references/benchmark-audit.md +73 -73
  72. package/references/commands.md +74 -45
  73. package/references/community-channels.md +46 -46
  74. package/references/compatibility.md +70 -70
  75. package/references/design-basis.md +38 -38
  76. package/references/domain-model.md +129 -129
  77. package/references/domain-quality-gates.md +75 -75
  78. package/references/drift-audit.md +81 -80
  79. package/references/evidence-taxonomy.md +145 -108
  80. package/references/file-ownership.md +97 -93
  81. package/references/final-form-roadmap.md +201 -0
  82. package/references/final-readiness.md +84 -84
  83. package/references/forward-test.md +133 -133
  84. package/references/goal-map.md +36 -36
  85. package/references/host-adapters.md +129 -101
  86. package/references/learning-system.md +42 -26
  87. package/references/maintenance-cadence.md +51 -0
  88. package/references/marketplace-discovery.md +46 -46
  89. package/references/model-routing.md +103 -103
  90. package/references/open-source-defaults.md +39 -39
  91. package/references/operating-model.md +52 -52
  92. package/references/packaging.md +277 -277
  93. package/references/project-agent-workspace.md +89 -89
  94. package/references/project-bootstrap.md +122 -122
  95. package/references/project-guards.md +52 -0
  96. package/references/project-profile.md +57 -57
  97. package/references/public-release.md +174 -174
  98. package/references/quality-gates.md +96 -89
  99. package/references/recovery.md +176 -176
  100. package/references/release-trust.md +43 -43
  101. package/references/release.md +126 -126
  102. package/references/review.md +141 -141
  103. package/references/role-dispatch-fallback.md +54 -0
  104. package/references/router.md +90 -90
  105. package/references/spec-workflow.md +66 -66
  106. package/references/task-levels.md +81 -81
  107. package/references/tool-adapters.md +80 -70
  108. package/scripts/audit-acceptance-execution-packet.mjs +133 -133
  109. package/scripts/audit-adoption-recipes.mjs +99 -99
  110. package/scripts/audit-change-lifecycle.mjs +77 -77
  111. package/scripts/audit-change-system.mjs +134 -134
  112. package/scripts/audit-ci-readiness.mjs +107 -107
  113. package/scripts/audit-close-gate-hardening.mjs +188 -0
  114. package/scripts/audit-close-gate.mjs +448 -262
  115. package/scripts/audit-command-adapters.mjs +91 -91
  116. package/scripts/audit-command-execution.mjs +212 -199
  117. package/scripts/audit-commands.mjs +7 -5
  118. package/scripts/audit-compatibility.mjs +149 -149
  119. package/scripts/audit-completion-plan-drill.mjs +230 -0
  120. package/scripts/audit-completion-readiness.mjs +290 -287
  121. package/scripts/audit-continue-preflight.mjs +234 -0
  122. package/scripts/audit-distribution.mjs +251 -251
  123. package/scripts/audit-domain-quality-gates.mjs +108 -108
  124. package/scripts/audit-evidence-levels.mjs +217 -0
  125. package/scripts/audit-evidence-placeholders.mjs +126 -126
  126. package/scripts/audit-evidence-review-queue.mjs +206 -0
  127. package/scripts/audit-final-acceptance-packet.mjs +9 -9
  128. package/scripts/audit-final-form-progress-report.mjs +19 -18
  129. package/scripts/audit-final-form-roadmap.mjs +157 -0
  130. package/scripts/audit-final-form-stale-copy.mjs +2 -2
  131. package/scripts/audit-final-readiness-promotion.mjs +255 -255
  132. package/scripts/audit-final-readiness.mjs +226 -226
  133. package/scripts/audit-fixtures.mjs +154 -154
  134. package/scripts/audit-fresh-session-readiness.mjs +177 -177
  135. package/scripts/audit-host-capabilities.mjs +237 -0
  136. package/scripts/audit-host-runtime-evidence-handoff.mjs +159 -159
  137. package/scripts/audit-host-runtime-invocation-drill.mjs +240 -240
  138. package/scripts/audit-host-runtime-invocations.mjs +254 -254
  139. package/scripts/audit-host-ui-invocation.mjs +132 -132
  140. package/scripts/audit-installed-sync.mjs +203 -0
  141. package/scripts/audit-learning-drift.mjs +292 -0
  142. package/scripts/audit-learning-promotion.mjs +351 -0
  143. package/scripts/audit-learning-system.mjs +24 -14
  144. package/scripts/audit-local-final-form-completion.mjs +11 -10
  145. package/scripts/audit-maintenance-cadence.mjs +139 -0
  146. package/scripts/audit-maintenance-snapshot.mjs +181 -0
  147. package/scripts/audit-marketplace-discovery.mjs +122 -122
  148. package/scripts/audit-npm-package-metadata.mjs +160 -154
  149. package/scripts/audit-npm-publish-dry-run.mjs +194 -166
  150. package/scripts/audit-npm-tarball-install.mjs +195 -189
  151. package/scripts/audit-open-source-defaults.mjs +92 -92
  152. package/scripts/audit-open-source-readiness.mjs +97 -97
  153. package/scripts/audit-owner-external-gate-kit.mjs +12 -11
  154. package/scripts/audit-project-guards.mjs +189 -0
  155. package/scripts/audit-project.mjs +144 -141
  156. package/scripts/audit-public-acceptance-command-dry-run-drill.mjs +203 -203
  157. package/scripts/audit-public-acceptance-handoff.mjs +10 -10
  158. package/scripts/audit-public-acceptance-readiness.mjs +222 -222
  159. package/scripts/audit-public-channel-publication.mjs +248 -248
  160. package/scripts/audit-public-ci-run.mjs +184 -184
  161. package/scripts/audit-public-collaboration-templates.mjs +98 -98
  162. package/scripts/audit-public-external-gate-probe.mjs +206 -206
  163. package/scripts/audit-public-release-checklist.mjs +17 -15
  164. package/scripts/audit-public-release-decision.mjs +201 -201
  165. package/scripts/audit-public-release-metadata.mjs +176 -176
  166. package/scripts/audit-public-repository-settings.mjs +237 -237
  167. package/scripts/audit-public-security-contact.mjs +171 -171
  168. package/scripts/audit-readme-docs.mjs +6 -4
  169. package/scripts/audit-recovery-readiness.mjs +98 -98
  170. package/scripts/audit-release-bundle.mjs +13 -11
  171. package/scripts/audit-release-owner-action-plan-drill.mjs +5 -4
  172. package/scripts/audit-release-owner-action-plan.mjs +10 -10
  173. package/scripts/audit-release-readiness.mjs +106 -106
  174. package/scripts/audit-release-status-manifest.mjs +4 -4
  175. package/scripts/audit-release-trust.mjs +62 -62
  176. package/scripts/audit-remote-distribution.mjs +266 -266
  177. package/scripts/audit-roadmap-consistency.mjs +234 -230
  178. package/scripts/audit-role-dispatch-fallback.mjs +212 -0
  179. package/scripts/audit-session-sync.mjs +127 -0
  180. package/scripts/audit-signing.mjs +147 -147
  181. package/scripts/audit-state-freshness.mjs +20 -14
  182. package/scripts/audit-state-repair.mjs +360 -0
  183. package/scripts/audit-target-adoption-evidence.mjs +117 -117
  184. package/scripts/audit-target-hardening-drills.mjs +267 -0
  185. package/scripts/audit-target-project.mjs +507 -507
  186. package/scripts/audit-tool-fallback-policy.mjs +180 -0
  187. package/scripts/audit-ui-browser-evidence-policy.mjs +191 -0
  188. package/scripts/audit-update-release-acceptance.mjs +136 -136
  189. package/scripts/audit-v1-target-validation.mjs +269 -269
  190. package/scripts/audit-validation-profiles.mjs +125 -123
  191. package/scripts/backfill-evidence-levels.mjs +98 -0
  192. package/scripts/check-encoding.mjs +72 -0
  193. package/scripts/close-change.mjs +116 -116
  194. package/scripts/discover-project-profile.mjs +307 -307
  195. package/scripts/generate-command-adapter.mjs +231 -231
  196. package/scripts/generate-continue-packet.mjs +1214 -0
  197. package/scripts/generate-final-acceptance-packet.mjs +188 -173
  198. package/scripts/generate-final-form-progress-report.mjs +191 -189
  199. package/scripts/generate-host-runtime-evidence-handoff.mjs +198 -191
  200. package/scripts/generate-maintenance-snapshot.mjs +216 -0
  201. package/scripts/generate-owner-external-gate-kit.mjs +295 -295
  202. package/scripts/generate-public-acceptance-handoff.mjs +168 -156
  203. package/scripts/generate-public-release-checklist.mjs +207 -207
  204. package/scripts/generate-release-bundle.mjs +505 -505
  205. package/scripts/generate-release-owner-action-plan.mjs +172 -171
  206. package/scripts/generate-release-status-manifest.mjs +199 -198
  207. package/scripts/generate-session-prompt.mjs +188 -188
  208. package/scripts/gse.mjs +67 -67
  209. package/scripts/init-change.mjs +265 -265
  210. package/scripts/init-project.mjs +793 -712
  211. package/scripts/install-gse.mjs +234 -234
  212. package/scripts/lib/evidence-placeholders.mjs +28 -28
  213. package/scripts/package-gse.mjs +174 -174
  214. package/scripts/probe-public-external-gates.mjs +167 -167
  215. package/scripts/record-host-invocation.mjs +151 -151
  216. package/scripts/record-learning.mjs +37 -8
  217. package/scripts/record-public-channel-publication.mjs +178 -178
  218. package/scripts/record-public-ci-run.mjs +180 -180
  219. package/scripts/record-public-release.mjs +175 -175
  220. package/scripts/record-public-repository-settings.mjs +209 -209
  221. package/scripts/record-public-security-contact.mjs +157 -157
  222. package/scripts/record-session-sync.mjs +87 -0
  223. package/scripts/run-gse-command.mjs +79 -47
  224. package/scripts/run-validation-profile.mjs +24 -5
  225. package/scripts/sign-gse-package.mjs +83 -83
  226. package/scripts/update-project-state.mjs +223 -223
  227. package/scripts/validate-gse.mjs +216 -0
  228. package/scripts/verify-gse-package.mjs +85 -85
@@ -0,0 +1,212 @@
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 { fileURLToPath } from 'node:url'
6
+ import { spawnSync } from 'node:child_process'
7
+
8
+ const args = process.argv.slice(2)
9
+
10
+ function readArg(name, fallback = null) {
11
+ const index = args.indexOf(name)
12
+ if (index === -1) return fallback
13
+ return args[index + 1] ?? fallback
14
+ }
15
+
16
+ const root = path.resolve(readArg('--root', path.join(import.meta.dirname, '..')))
17
+ const targetArg = readArg('--target')
18
+ const jsonOnly = args.includes('--json')
19
+
20
+ const requiredRoles = ['Planner', 'Locator', 'Implementer', 'Verifier', 'Reviewer', 'Docs/Evidence', 'Release']
21
+ const allowedModes = new Set(['real-subagent', 'sequential-role', 'handoff-session'])
22
+ const allowedDelegation = new Set(['yes', 'no'])
23
+ const allowedToolStatuses = new Set(['verified', 'documented', 'unknown', 'unavailable'])
24
+
25
+ function readText(filePath) {
26
+ return fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8').replace(/^\uFEFF/, '') : ''
27
+ }
28
+
29
+ function parseRoleTable(text) {
30
+ const rows = []
31
+ for (const line of text.split(/\r?\n/)) {
32
+ const trimmed = line.trim()
33
+ if (!trimmed.startsWith('|')) continue
34
+ if (/^\|\s*-+/.test(trimmed)) continue
35
+ if (/^\|\s*Role\s*\|/i.test(trimmed)) continue
36
+ const cells = trimmed
37
+ .slice(1, trimmed.endsWith('|') ? -1 : undefined)
38
+ .split('|')
39
+ .map((cell) => cell.trim())
40
+ if (cells.length < 8) continue
41
+ rows.push({
42
+ role: cells[0],
43
+ mode: cells[1],
44
+ realDelegationUsed: cells[2],
45
+ toolStatus: cells[3],
46
+ fallbackOutput: cells[4],
47
+ evidence: cells[5],
48
+ stopCondition: cells[6],
49
+ writeAccess: cells[7],
50
+ })
51
+ }
52
+ return rows
53
+ }
54
+
55
+ export function readRoleDispatchFallback(target) {
56
+ const filePath = path.join(target, '.gse', 'agents', 'role-fallback-packets.md')
57
+ const exists = fs.existsSync(filePath)
58
+ const text = exists ? readText(filePath) : ''
59
+ const packets = parseRoleTable(text)
60
+ const roleSet = new Set(packets.map((packet) => packet.role))
61
+ const missingRoles = requiredRoles.filter((role) => !roleSet.has(role))
62
+ const invalidMode = packets.filter((packet) => !allowedModes.has(packet.mode))
63
+ const invalidDelegation = packets.filter((packet) => !allowedDelegation.has(packet.realDelegationUsed))
64
+ const invalidToolStatus = packets.filter((packet) => !allowedToolStatuses.has(packet.toolStatus))
65
+ const missingEvidence = packets.filter((packet) => !packet.fallbackOutput || !packet.evidence || !packet.stopCondition || !packet.writeAccess)
66
+ const fakeDelegationRisk = packets.filter((packet) => packet.realDelegationUsed === 'yes' && packet.toolStatus !== 'verified')
67
+ const status = !exists
68
+ ? 'warning'
69
+ : missingRoles.length > 0 || invalidMode.length > 0 || invalidDelegation.length > 0 || invalidToolStatus.length > 0 || missingEvidence.length > 0 || fakeDelegationRisk.length > 0
70
+ ? 'failed'
71
+ : 'passed'
72
+ return {
73
+ path: '.gse/agents/role-fallback-packets.md',
74
+ exists,
75
+ status,
76
+ packets,
77
+ summary: {
78
+ requiredRoles,
79
+ total: packets.length,
80
+ missingRoles,
81
+ invalidMode: invalidMode.map((packet) => packet.role),
82
+ invalidDelegation: invalidDelegation.map((packet) => packet.role),
83
+ invalidToolStatus: invalidToolStatus.map((packet) => packet.role),
84
+ missingEvidence: missingEvidence.map((packet) => packet.role),
85
+ fakeDelegationRisk: fakeDelegationRisk.map((packet) => packet.role),
86
+ sequentialFallbackRoles: packets.filter((packet) => packet.mode === 'sequential-role').map((packet) => packet.role),
87
+ },
88
+ }
89
+ }
90
+
91
+ function run(script, commandArgs) {
92
+ const result = spawnSync(process.execPath, [path.join(root, 'scripts', script), ...commandArgs], {
93
+ cwd: root,
94
+ encoding: 'utf8',
95
+ windowsHide: true,
96
+ })
97
+ return {
98
+ command: [process.execPath, path.join(root, 'scripts', script), ...commandArgs].join(' '),
99
+ status: result.status ?? 1,
100
+ stdout: (result.stdout ?? '').trim(),
101
+ stderr: (result.stderr ?? '').trim(),
102
+ }
103
+ }
104
+
105
+ function check(id, label, ok, evidence, risk = '') {
106
+ return { id, label, status: ok ? 'passed' : 'failed', evidence, risk }
107
+ }
108
+
109
+ function createFixture() {
110
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gse-role-fallback-'))
111
+ const init = run('init-project.mjs', ['--target', dir, '--mode', 'standard', '--json'])
112
+ return { dir, init }
113
+ }
114
+
115
+ function audit(target) {
116
+ const resolvedTarget = path.resolve(target)
117
+ const reference = readText(path.join(root, 'references', 'role-dispatch-fallback.md'))
118
+ const agentRoles = readText(path.join(root, 'references', 'agent-roles.md'))
119
+ const dispatchTemplate = readText(path.join(root, 'assets', 'templates', 'dispatch-packet.md'))
120
+ const fallbackTemplate = readText(path.join(root, 'assets', 'templates', 'role-fallback-packet.md'))
121
+ const initProject = readText(path.join(root, 'scripts', 'init-project.mjs'))
122
+ const continuePacket = readText(path.join(root, 'scripts', 'generate-continue-packet.mjs'))
123
+ const validationProfile = readText(path.join(root, 'scripts', 'run-validation-profile.mjs'))
124
+ const validator = readText(path.join(root, 'scripts', 'validate-gse.mjs'))
125
+ const fallback = readRoleDispatchFallback(resolvedTarget)
126
+ const roleDocsCoverRequired = requiredRoles.every((role) => reference.includes(role)) && agentRoles.includes('Planner') && agentRoles.includes('Release')
127
+ const templatesCoverEvidence =
128
+ dispatchTemplate.includes('Real delegation used') &&
129
+ dispatchTemplate.includes('Role output evidence') &&
130
+ dispatchTemplate.includes('Claim boundary') &&
131
+ fallbackTemplate.includes('Execution mode') &&
132
+ fallbackTemplate.includes('Evidence level')
133
+ const checks = [
134
+ check('RDF01', 'role dispatch fallback reference defines required roles and claim boundary', roleDocsCoverRequired && reference.includes('real-subagent') && reference.includes('sequential-role') && reference.includes('handoff-session'), 'references/role-dispatch-fallback.md and references/agent-roles.md'),
135
+ check('RDF02', 'dispatch templates expose auditable fallback fields', templatesCoverEvidence, 'assets/templates/dispatch-packet.md and assets/templates/role-fallback-packet.md'),
136
+ check('RDF03', 'init-project scaffolds role fallback packets for standard or enterprise projects', initProject.includes('agents/role-fallback-packets.md') && initProject.includes('Planner') && initProject.includes('Release'), 'scripts/init-project.mjs'),
137
+ check('RDF04', 'target role fallback packet is present or reported as warning', fallback.exists || fallback.status === 'warning', fallback.exists ? fallback.path : 'missing role fallback packet warning'),
138
+ check('RDF05', 'target role fallback packets cover all required roles when present', !fallback.exists || fallback.summary.missingRoles.length === 0, fallback.summary.missingRoles.join(', ') || 'required roles present'),
139
+ check('RDF06', 'target role fallback packets use valid mode/delegation/tool status vocabulary', !fallback.exists || (fallback.summary.invalidMode.length === 0 && fallback.summary.invalidDelegation.length === 0 && fallback.summary.invalidToolStatus.length === 0), 'mode/delegation/tool status vocabulary'),
140
+ check('RDF07', 'target role fallback packets include output evidence and stop conditions', !fallback.exists || fallback.summary.missingEvidence.length === 0, fallback.summary.missingEvidence.join(', ') || 'fallback evidence fields present'),
141
+ check('RDF08', 'target role fallback packets do not claim real delegation without verified tool status', !fallback.exists || fallback.summary.fakeDelegationRisk.length === 0, fallback.summary.fakeDelegationRisk.join(', ') || 'no fake delegation risk'),
142
+ check('RDF09', 'continue packet surfaces role fallback readiness', continuePacket.includes('readRoleDispatchFallback') && continuePacket.includes('roleFallback'), 'scripts/generate-continue-packet.mjs'),
143
+ check('RDF10', 'validation routes include role dispatch fallback audit', validationProfile.includes('audit-role-dispatch-fallback.mjs') && validator.includes('audit-role-dispatch-fallback.mjs'), 'validation profile and validate-gse'),
144
+ ]
145
+ const passed = checks.filter((item) => item.status === 'passed').length
146
+ const failed = checks.length - passed
147
+ return {
148
+ target: resolvedTarget,
149
+ generatedAt: new Date().toISOString(),
150
+ summary: { status: failed === 0 ? fallback.status : 'failed', passed, failed, total: checks.length },
151
+ workflows: {
152
+ roleDispatchFallback: failed === 0 && fallback.status !== 'failed' ? 'verified' : 'failed',
153
+ requiredRoles,
154
+ packets: fallback.summary.total,
155
+ sequentialFallbackRoles: fallback.summary.sequentialFallbackRoles.length,
156
+ },
157
+ roleFallback: fallback,
158
+ checks,
159
+ limits: [
160
+ 'Role fallback packets prove auditable role boundaries, not real subagent execution.',
161
+ 'Real subagent support still requires current host dispatch evidence.',
162
+ 'Missing role fallback packets are a warning for target projects until the scaffold is adopted.',
163
+ ],
164
+ }
165
+ }
166
+
167
+ function selfTestReport() {
168
+ const fixture = createFixture()
169
+ const fixtureReport = audit(fixture.dir)
170
+ const missingDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gse-role-fallback-missing-'))
171
+ fs.mkdirSync(path.join(missingDir, '.gse'), { recursive: true })
172
+ const missingReport = audit(missingDir)
173
+ fs.rmSync(fixture.dir, { recursive: true, force: true })
174
+ fs.rmSync(missingDir, { recursive: true, force: true })
175
+ const checks = [
176
+ check('RDFA01', 'init-project creates role fallback packet scaffold', fixture.init.status === 0 && fixtureReport.roleFallback.exists, 'scripts/init-project.mjs'),
177
+ check('RDFA02', 'scaffold covers every required role', fixtureReport.roleFallback.summary.missingRoles.length === 0, fixtureReport.roleFallback.packets.map((packet) => packet.role).join(', ')),
178
+ check('RDFA03', 'scaffold uses sequential fallback without fake real delegation', fixtureReport.roleFallback.summary.fakeDelegationRisk.length === 0 && fixtureReport.roleFallback.packets.every((packet) => packet.realDelegationUsed === 'no'), 'sequential fallback packets'),
179
+ check('RDFA04', 'missing role fallback file is warning, not hard failure', missingReport.roleFallback.status === 'warning', 'missing fixture'),
180
+ check('RDFA05', 'audit source is wired to continue packet and validation scripts', fixtureReport.checks.find((item) => item.id === 'RDF09')?.status === 'passed' && fixtureReport.checks.find((item) => item.id === 'RDF10')?.status === 'passed', 'continue/validation wiring'),
181
+ ]
182
+ const passed = checks.filter((item) => item.status === 'passed').length
183
+ const failed = checks.length - passed
184
+ return {
185
+ root,
186
+ generatedAt: new Date().toISOString(),
187
+ summary: { status: failed === 0 ? 'passed' : 'failed', passed, failed, total: checks.length },
188
+ workflows: {
189
+ roleDispatchFallback: failed === 0 ? 'verified' : 'failed',
190
+ initProjectRoleFallbackScaffold: failed === 0 ? 'verified' : 'failed',
191
+ },
192
+ fixture: {
193
+ scaffoldStatus: fixtureReport.roleFallback.status,
194
+ missingStatus: missingReport.roleFallback.status,
195
+ roles: fixtureReport.roleFallback.packets.map((packet) => packet.role),
196
+ },
197
+ checks,
198
+ limits: [
199
+ 'This self-test verifies scaffold and audit mechanics.',
200
+ 'It does not prove a real host spawned subagents.',
201
+ ],
202
+ }
203
+ }
204
+
205
+ const isCli = process.argv[1] && fileURLToPath(import.meta.url) === path.resolve(process.argv[1])
206
+
207
+ if (isCli) {
208
+ const report = targetArg ? audit(targetArg) : selfTestReport()
209
+ if (jsonOnly) console.log(JSON.stringify(report, null, 2))
210
+ else console.log(JSON.stringify(report, null, 2))
211
+ if (report.summary.status === 'failed') process.exit(1)
212
+ }
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs'
3
+ import path from 'node:path'
4
+
5
+ const args = process.argv.slice(2)
6
+
7
+ function readArg(name, fallback = null) {
8
+ const index = args.indexOf(name)
9
+ if (index === -1) return fallback
10
+ return args[index + 1] ?? fallback
11
+ }
12
+
13
+ function readArgs(name) {
14
+ const values = []
15
+ for (let index = 0; index < args.length; index += 1) {
16
+ if (args[index] === name && args[index + 1]) values.push(args[index + 1])
17
+ }
18
+ return values
19
+ }
20
+
21
+ const root = path.resolve(readArg('--root', path.join(import.meta.dirname, '..')))
22
+ const jsonOnly = args.includes('--json')
23
+ const requireInstalled = args.includes('--require-installed')
24
+ const requiredThreads = readArgs('--require-thread')
25
+ const recordPath = path.join(root, '.gse', 'session-sync.jsonl')
26
+
27
+ function check(id, label, ok, evidence, risk = '') {
28
+ return { id, label, status: ok ? 'passed' : 'failed', evidence, risk }
29
+ }
30
+
31
+ function parseJsonl(filePath) {
32
+ if (!fs.existsSync(filePath)) return { records: [], errors: [] }
33
+ const lines = fs.readFileSync(filePath, 'utf8').replace(/^\uFEFF/, '').split(/\r?\n/)
34
+ const records = []
35
+ const errors = []
36
+ lines.forEach((line, index) => {
37
+ if (!line.trim()) return
38
+ try {
39
+ records.push(JSON.parse(line))
40
+ } catch (error) {
41
+ errors.push({ line: index + 1, error: error.message })
42
+ }
43
+ })
44
+ return { records, errors }
45
+ }
46
+
47
+ function validRecord(record) {
48
+ const validStatuses = new Set(['sent', 'installed-sync', 'skipped', 'failed', 'archived', 'unavailable'])
49
+ return record?.schemaVersion === 1 &&
50
+ typeof record.recordedAt === 'string' &&
51
+ validStatuses.has(record.status) &&
52
+ typeof record.method === 'string' &&
53
+ record.method.length > 0 &&
54
+ typeof record.evidence === 'string' &&
55
+ record.evidence.length > 0
56
+ }
57
+
58
+ const { records, errors } = parseJsonl(recordPath)
59
+ const invalidRecords = records.filter((record) => !validRecord(record))
60
+ const installedRecords = records.filter((record) => record.status === 'installed-sync')
61
+ const threadResults = requiredThreads.map((threadId) => {
62
+ const matches = records.filter((record) => record.threadId === threadId)
63
+ const latest = matches.at(-1) ?? null
64
+ const accepted = latest && ['sent', 'archived', 'unavailable', 'failed', 'skipped'].includes(latest.status)
65
+ return { threadId, latest, accepted }
66
+ })
67
+
68
+ const checks = [
69
+ check(
70
+ 'SS01',
71
+ 'session sync record file is optional but parseable when present',
72
+ errors.length === 0,
73
+ fs.existsSync(recordPath) ? recordPath : 'no .gse/session-sync.jsonl yet',
74
+ errors.map((item) => `line ${item.line}: ${item.error}`).join('; '),
75
+ ),
76
+ check(
77
+ 'SS02',
78
+ 'all session sync records follow schema',
79
+ invalidRecords.length === 0,
80
+ `records=${records.length}, invalid=${invalidRecords.length}`,
81
+ ),
82
+ requireInstalled
83
+ ? check(
84
+ 'SS03',
85
+ 'installed-copy sync/parity evidence is recorded',
86
+ installedRecords.length > 0,
87
+ installedRecords.at(-1)?.evidence ?? 'missing installed-sync record',
88
+ )
89
+ : check('SS03', 'installed-copy sync record is optional unless required', true, 'no --require-installed supplied'),
90
+ check(
91
+ 'SS04',
92
+ 'required thread sync outcomes are recorded honestly',
93
+ threadResults.every((item) => item.accepted),
94
+ threadResults.map((item) => `${item.threadId}:${item.latest?.status ?? 'missing'}`).join(', ') || 'no --require-thread supplied',
95
+ 'Allowed outcomes are sent, archived, unavailable, failed, or skipped; adoption must not be inferred from the record alone.',
96
+ ),
97
+ ]
98
+
99
+ const passed = checks.filter((item) => item.status === 'passed').length
100
+ const failed = checks.length - passed
101
+ const report = {
102
+ root,
103
+ recordPath,
104
+ generatedAt: new Date().toISOString(),
105
+ summary: { status: failed === 0 ? 'passed' : 'failed', passed, failed, total: checks.length },
106
+ workflows: {
107
+ sessionSyncRecords: failed === 0 ? 'verified' : 'failed',
108
+ installedSyncRecorded: installedRecords.length > 0 ? 'recorded' : 'not-recorded',
109
+ requiredThreadCount: requiredThreads.length,
110
+ },
111
+ records: {
112
+ total: records.length,
113
+ installedSync: installedRecords.length,
114
+ requiredThreads: threadResults,
115
+ },
116
+ limits: [
117
+ 'This audit verifies sync records and honest outcomes; it does not prove a target session adopted the new capability.',
118
+ 'Use --require-thread for owner-requested active sessions and --require-installed for installed-copy capability sync closure.',
119
+ 'Archived or unavailable sessions should be recorded as such instead of treated as successful syncs.',
120
+ ],
121
+ checks,
122
+ }
123
+
124
+ if (jsonOnly) console.log(JSON.stringify(report, null, 2))
125
+ else console.log(JSON.stringify(report, null, 2))
126
+
127
+ if (failed > 0) process.exit(1)
@@ -1,147 +1,147 @@
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'
6
-
7
- const args = process.argv.slice(2)
8
-
9
- function readArg(name, fallback = null) {
10
- const index = args.indexOf(name)
11
- if (index === -1) return fallback
12
- return args[index + 1] ?? fallback
13
- }
14
-
15
- const root = path.resolve(readArg('--root', path.join(import.meta.dirname, '..')))
16
- const jsonOnly = args.includes('--json')
17
-
18
- function run(command, commandArgs, cwd = root) {
19
- const result = spawnSync(command, commandArgs, { cwd, encoding: 'utf8', windowsHide: true })
20
- return {
21
- command: [command, ...commandArgs].join(' '),
22
- status: result.status ?? 1,
23
- stdout: (result.stdout ?? '').trim(),
24
- stderr: (result.stderr ?? '').trim(),
25
- }
26
- }
27
-
28
- function parseJson(text) {
29
- try { return JSON.parse(text) } catch { return null }
30
- }
31
-
32
- function check(id, label, ok, evidence, risk = '') {
33
- return { id, label, status: ok ? 'passed' : 'failed', evidence, risk }
34
- }
35
-
36
- const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gse-signing-'))
37
- const packageOut = path.join(tempRoot, 'package')
38
- const installTarget = path.join(tempRoot, 'installed-gse')
39
- const tamperedPackage = path.join(tempRoot, 'tampered-package')
40
- const tamperedTarget = path.join(tempRoot, 'tampered-installed')
41
- const privateKey = path.join(tempRoot, 'gse-private.pem')
42
- const publicKey = path.join(tempRoot, 'gse-public.pem')
43
-
44
- const packageRun = run(process.execPath, [
45
- path.join(root, 'scripts', 'package-gse.mjs'),
46
- '--root',
47
- root,
48
- '--out',
49
- packageOut,
50
- '--label',
51
- 'gse-signing-audit',
52
- '--json',
53
- ])
54
- const signRun = run(process.execPath, [
55
- path.join(root, 'scripts', 'sign-gse-package.mjs'),
56
- '--package',
57
- packageOut,
58
- '--private-key',
59
- privateKey,
60
- '--public-key',
61
- publicKey,
62
- '--generate-key',
63
- '--json',
64
- ])
65
- const verifyRun = run(process.execPath, [
66
- path.join(root, 'scripts', 'verify-gse-package.mjs'),
67
- '--package',
68
- packageOut,
69
- '--public-key',
70
- publicKey,
71
- '--json',
72
- ])
73
- const installRun = run(process.execPath, [
74
- path.join(root, 'scripts', 'install-gse.mjs'),
75
- '--source',
76
- packageOut,
77
- '--target',
78
- installTarget,
79
- '--public-key',
80
- publicKey,
81
- '--json',
82
- ])
83
-
84
- fs.cpSync(packageOut, tamperedPackage, { recursive: true })
85
- fs.appendFileSync(path.join(tamperedPackage, 'SKILL.md'), '\nTampered for signature audit.\n', 'utf8')
86
- const tamperedVerify = run(process.execPath, [
87
- path.join(root, 'scripts', 'verify-gse-package.mjs'),
88
- '--package',
89
- tamperedPackage,
90
- '--public-key',
91
- publicKey,
92
- '--json',
93
- ])
94
- const tamperedInstall = run(process.execPath, [
95
- path.join(root, 'scripts', 'install-gse.mjs'),
96
- '--source',
97
- tamperedPackage,
98
- '--target',
99
- tamperedTarget,
100
- '--public-key',
101
- publicKey,
102
- '--json',
103
- ])
104
-
105
- const signData = parseJson(signRun.stdout)
106
- const verifyData = parseJson(verifyRun.stdout)
107
- const installData = parseJson(installRun.stdout)
108
- const tamperedVerifyData = parseJson(tamperedVerify.stdout)
109
- const tamperedInstallData = parseJson(tamperedInstall.stdout)
110
-
111
- const checks = [
112
- check('SIGN01', 'package command succeeds', packageRun.status === 0, packageRun.command),
113
- check('SIGN02', 'sign command creates signature and keys', signRun.status === 0 && fs.existsSync(path.join(packageOut, 'gse-package-signature.json')) && fs.existsSync(publicKey), signRun.command),
114
- check('SIGN03', 'verify command accepts signed package', verifyRun.status === 0 && verifyData?.summary?.failed === 0, verifyRun.command),
115
- check('SIGN04', 'install command accepts signed package with public key', installRun.status === 0 && installData?.signature?.status === 'verified', installRun.command),
116
- check('SIGN05', 'tampered package fails signature/hash verification', tamperedVerify.status === 1 && tamperedVerifyData?.summary?.failed > 0, tamperedVerify.command),
117
- check('SIGN06', 'install command rejects tampered signed package', tamperedInstall.status === 1 && (tamperedInstallData?.signature?.status === 'failed' || tamperedInstallData?.summary?.integrityFailed > 0), tamperedInstall.command),
118
- check('SIGN07', 'signature uses Ed25519', signData?.algorithm === 'ed25519', 'sign-gse-package output'),
119
- ]
120
-
121
- const passed = checks.filter((item) => item.status === 'passed').length
122
- const failed = checks.length - passed
123
- const report = {
124
- root,
125
- generatedAt: new Date().toISOString(),
126
- tempRoot,
127
- summary: { status: failed === 0 ? 'passed' : 'failed', passed, failed, total: checks.length },
128
- workflows: {
129
- packageSigning: signRun.status === 0 ? 'verified' : 'failed',
130
- signatureVerification: verifyRun.status === 0 ? 'verified' : 'failed',
131
- signedInstall: installRun.status === 0 ? 'verified' : 'failed',
132
- tamperRejection: tamperedVerify.status === 1 && tamperedInstall.status === 1 ? 'verified' : 'failed',
133
- },
134
- commands: [packageRun.command, signRun.command, verifyRun.command, installRun.command, tamperedVerify.command, tamperedInstall.command],
135
- limits: [
136
- 'This audit verifies local Ed25519 signing and verification mechanics.',
137
- 'It does not prove public key custody, maintainer identity, transparency logs, or marketplace trust.',
138
- ],
139
- checks,
140
- }
141
-
142
- fs.rmSync(tempRoot, { recursive: true, force: true })
143
-
144
- if (jsonOnly) console.log(JSON.stringify(report, null, 2))
145
- else console.log(JSON.stringify(report, null, 2))
146
-
147
- if (failed > 0) process.exit(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'
6
+
7
+ const args = process.argv.slice(2)
8
+
9
+ function readArg(name, fallback = null) {
10
+ const index = args.indexOf(name)
11
+ if (index === -1) return fallback
12
+ return args[index + 1] ?? fallback
13
+ }
14
+
15
+ const root = path.resolve(readArg('--root', path.join(import.meta.dirname, '..')))
16
+ const jsonOnly = args.includes('--json')
17
+
18
+ function run(command, commandArgs, cwd = root) {
19
+ const result = spawnSync(command, commandArgs, { cwd, encoding: 'utf8', windowsHide: true })
20
+ return {
21
+ command: [command, ...commandArgs].join(' '),
22
+ status: result.status ?? 1,
23
+ stdout: (result.stdout ?? '').trim(),
24
+ stderr: (result.stderr ?? '').trim(),
25
+ }
26
+ }
27
+
28
+ function parseJson(text) {
29
+ try { return JSON.parse(text) } catch { return null }
30
+ }
31
+
32
+ function check(id, label, ok, evidence, risk = '') {
33
+ return { id, label, status: ok ? 'passed' : 'failed', evidence, risk }
34
+ }
35
+
36
+ const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'gse-signing-'))
37
+ const packageOut = path.join(tempRoot, 'package')
38
+ const installTarget = path.join(tempRoot, 'installed-gse')
39
+ const tamperedPackage = path.join(tempRoot, 'tampered-package')
40
+ const tamperedTarget = path.join(tempRoot, 'tampered-installed')
41
+ const privateKey = path.join(tempRoot, 'gse-private.pem')
42
+ const publicKey = path.join(tempRoot, 'gse-public.pem')
43
+
44
+ const packageRun = run(process.execPath, [
45
+ path.join(root, 'scripts', 'package-gse.mjs'),
46
+ '--root',
47
+ root,
48
+ '--out',
49
+ packageOut,
50
+ '--label',
51
+ 'gse-signing-audit',
52
+ '--json',
53
+ ])
54
+ const signRun = run(process.execPath, [
55
+ path.join(root, 'scripts', 'sign-gse-package.mjs'),
56
+ '--package',
57
+ packageOut,
58
+ '--private-key',
59
+ privateKey,
60
+ '--public-key',
61
+ publicKey,
62
+ '--generate-key',
63
+ '--json',
64
+ ])
65
+ const verifyRun = run(process.execPath, [
66
+ path.join(root, 'scripts', 'verify-gse-package.mjs'),
67
+ '--package',
68
+ packageOut,
69
+ '--public-key',
70
+ publicKey,
71
+ '--json',
72
+ ])
73
+ const installRun = run(process.execPath, [
74
+ path.join(root, 'scripts', 'install-gse.mjs'),
75
+ '--source',
76
+ packageOut,
77
+ '--target',
78
+ installTarget,
79
+ '--public-key',
80
+ publicKey,
81
+ '--json',
82
+ ])
83
+
84
+ fs.cpSync(packageOut, tamperedPackage, { recursive: true })
85
+ fs.appendFileSync(path.join(tamperedPackage, 'SKILL.md'), '\nTampered for signature audit.\n', 'utf8')
86
+ const tamperedVerify = run(process.execPath, [
87
+ path.join(root, 'scripts', 'verify-gse-package.mjs'),
88
+ '--package',
89
+ tamperedPackage,
90
+ '--public-key',
91
+ publicKey,
92
+ '--json',
93
+ ])
94
+ const tamperedInstall = run(process.execPath, [
95
+ path.join(root, 'scripts', 'install-gse.mjs'),
96
+ '--source',
97
+ tamperedPackage,
98
+ '--target',
99
+ tamperedTarget,
100
+ '--public-key',
101
+ publicKey,
102
+ '--json',
103
+ ])
104
+
105
+ const signData = parseJson(signRun.stdout)
106
+ const verifyData = parseJson(verifyRun.stdout)
107
+ const installData = parseJson(installRun.stdout)
108
+ const tamperedVerifyData = parseJson(tamperedVerify.stdout)
109
+ const tamperedInstallData = parseJson(tamperedInstall.stdout)
110
+
111
+ const checks = [
112
+ check('SIGN01', 'package command succeeds', packageRun.status === 0, packageRun.command),
113
+ check('SIGN02', 'sign command creates signature and keys', signRun.status === 0 && fs.existsSync(path.join(packageOut, 'gse-package-signature.json')) && fs.existsSync(publicKey), signRun.command),
114
+ check('SIGN03', 'verify command accepts signed package', verifyRun.status === 0 && verifyData?.summary?.failed === 0, verifyRun.command),
115
+ check('SIGN04', 'install command accepts signed package with public key', installRun.status === 0 && installData?.signature?.status === 'verified', installRun.command),
116
+ check('SIGN05', 'tampered package fails signature/hash verification', tamperedVerify.status === 1 && tamperedVerifyData?.summary?.failed > 0, tamperedVerify.command),
117
+ check('SIGN06', 'install command rejects tampered signed package', tamperedInstall.status === 1 && (tamperedInstallData?.signature?.status === 'failed' || tamperedInstallData?.summary?.integrityFailed > 0), tamperedInstall.command),
118
+ check('SIGN07', 'signature uses Ed25519', signData?.algorithm === 'ed25519', 'sign-gse-package output'),
119
+ ]
120
+
121
+ const passed = checks.filter((item) => item.status === 'passed').length
122
+ const failed = checks.length - passed
123
+ const report = {
124
+ root,
125
+ generatedAt: new Date().toISOString(),
126
+ tempRoot,
127
+ summary: { status: failed === 0 ? 'passed' : 'failed', passed, failed, total: checks.length },
128
+ workflows: {
129
+ packageSigning: signRun.status === 0 ? 'verified' : 'failed',
130
+ signatureVerification: verifyRun.status === 0 ? 'verified' : 'failed',
131
+ signedInstall: installRun.status === 0 ? 'verified' : 'failed',
132
+ tamperRejection: tamperedVerify.status === 1 && tamperedInstall.status === 1 ? 'verified' : 'failed',
133
+ },
134
+ commands: [packageRun.command, signRun.command, verifyRun.command, installRun.command, tamperedVerify.command, tamperedInstall.command],
135
+ limits: [
136
+ 'This audit verifies local Ed25519 signing and verification mechanics.',
137
+ 'It does not prove public key custody, maintainer identity, transparency logs, or marketplace trust.',
138
+ ],
139
+ checks,
140
+ }
141
+
142
+ fs.rmSync(tempRoot, { recursive: true, force: true })
143
+
144
+ if (jsonOnly) console.log(JSON.stringify(report, null, 2))
145
+ else console.log(JSON.stringify(report, null, 2))
146
+
147
+ if (failed > 0) process.exit(1)