@t275005746/gse 0.1.0
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.
- package/.github/ISSUE_TEMPLATE/bug_report.yml +42 -0
- package/.github/ISSUE_TEMPLATE/change_request.yml +50 -0
- package/.github/ISSUE_TEMPLATE/config.yml +5 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +38 -0
- package/.github/workflows/validate-gse.yml +33 -0
- package/.gse/README.md +18 -0
- package/.gse/gse-development-protocol.md +50 -0
- package/.gse/project-profile.md +29 -0
- package/.gse/quality-gates.md +25 -0
- package/.gse/releases/public-channel-publication-pending.md +49 -0
- package/.gse/releases/public-ci-run-pending.md +53 -0
- package/.gse/releases/public-release-owner-required.md +65 -0
- package/.gse/releases/public-repository-settings-owner-required.md +59 -0
- package/.gse/releases/public-security-contact-owner-required.md +45 -0
- package/.gse/state.json +27 -0
- package/CHANGELOG.md +24 -0
- package/CONTRIBUTING.md +64 -0
- package/LICENSE +21 -0
- package/README.md +236 -0
- package/README.zh-CN.md +236 -0
- package/SECURITY.md +41 -0
- package/SKILL.md +148 -0
- package/SUPPORT.md +38 -0
- package/agents/openai.yaml +4 -0
- package/assets/marketplace/README.md +7 -0
- package/assets/marketplace/gse-listing.json +75 -0
- package/assets/templates/acceptance-execution-packet.md +73 -0
- package/assets/templates/adr.md +14 -0
- package/assets/templates/change-brief.md +16 -0
- package/assets/templates/design.md +18 -0
- package/assets/templates/dispatch-packet.md +104 -0
- package/assets/templates/evidence.md +14 -0
- package/assets/templates/execution-quality-pack.md +65 -0
- package/assets/templates/goal-map.md +21 -0
- package/assets/templates/host-adapter.md +48 -0
- package/assets/templates/host-ui-invocation-record.md +42 -0
- package/assets/templates/incident-review.md +60 -0
- package/assets/templates/public-channel-publication-record.md +49 -0
- package/assets/templates/public-ci-run-record.md +53 -0
- package/assets/templates/public-release-record.md +65 -0
- package/assets/templates/public-repository-settings-record.md +59 -0
- package/assets/templates/public-security-contact-record.md +45 -0
- package/assets/templates/release-trust-record.md +32 -0
- package/assets/templates/review.md +18 -0
- package/assets/templates/spec.md +16 -0
- package/assets/templates/target-adoption-evidence.md +29 -0
- package/assets/templates/tasks.md +17 -0
- package/assets/templates/update-release-acceptance-record.md +58 -0
- package/examples/README.md +22 -0
- package/examples/agent-runtime-host/.claude/gse-adapter.md +6 -0
- package/examples/agent-runtime-host/.codex/gse-adapter.md +7 -0
- package/examples/agent-runtime-host/.gse/goal-map.md +7 -0
- package/examples/agent-runtime-host/.gse/project-profile.md +27 -0
- package/examples/agent-runtime-host/.gse/tooling.md +5 -0
- package/examples/agent-runtime-host/.mcp.json +9 -0
- package/examples/agent-runtime-host/AGENTS.md +5 -0
- package/examples/agent-runtime-host/README.md +10 -0
- package/examples/agent-runtime-host/docs/model-routing.md +5 -0
- package/examples/cli-tool/.gse/project-profile.md +21 -0
- package/examples/cli-tool/AGENTS.md +5 -0
- package/examples/cli-tool/README.md +12 -0
- package/examples/cli-tool/package.json +21 -0
- package/examples/small-app/.env.example +2 -0
- package/examples/small-app/.github/workflows/ci.yml +8 -0
- package/examples/small-app/AGENTS.md +5 -0
- package/examples/small-app/README.md +12 -0
- package/examples/small-app/package.json +26 -0
- package/examples/small-app/playwright.config.ts +4 -0
- package/package.json +53 -0
- package/references/adoption-recipes.md +171 -0
- package/references/agent-roles.md +49 -0
- package/references/architecture-health.md +101 -0
- package/references/benchmark-audit.md +73 -0
- package/references/commands.md +295 -0
- package/references/community-channels.md +46 -0
- package/references/compatibility.md +70 -0
- package/references/design-basis.md +38 -0
- package/references/domain-model.md +129 -0
- package/references/domain-quality-gates.md +75 -0
- package/references/drift-audit.md +81 -0
- package/references/evidence-taxonomy.md +123 -0
- package/references/file-ownership.md +107 -0
- package/references/final-readiness.md +84 -0
- package/references/forward-test.md +133 -0
- package/references/goal-map.md +36 -0
- package/references/host-adapters.md +119 -0
- package/references/learning-system.md +35 -0
- package/references/marketplace-discovery.md +46 -0
- package/references/model-routing.md +103 -0
- package/references/open-source-defaults.md +39 -0
- package/references/operating-model.md +52 -0
- package/references/packaging.md +277 -0
- package/references/project-agent-workspace.md +89 -0
- package/references/project-bootstrap.md +122 -0
- package/references/project-profile.md +57 -0
- package/references/public-release.md +174 -0
- package/references/quality-gates.md +100 -0
- package/references/recovery.md +176 -0
- package/references/release-trust.md +43 -0
- package/references/release.md +126 -0
- package/references/review.md +141 -0
- package/references/router.md +90 -0
- package/references/spec-workflow.md +66 -0
- package/references/task-levels.md +81 -0
- package/references/tool-adapters.md +73 -0
- package/scripts/audit-acceptance-execution-packet.mjs +133 -0
- package/scripts/audit-adoption-recipes.mjs +99 -0
- package/scripts/audit-adoption.mjs +154 -0
- package/scripts/audit-change-lifecycle.mjs +77 -0
- package/scripts/audit-change-system.mjs +134 -0
- package/scripts/audit-ci-readiness.mjs +107 -0
- package/scripts/audit-close-gate.mjs +323 -0
- package/scripts/audit-command-adapters.mjs +91 -0
- package/scripts/audit-command-execution.mjs +210 -0
- package/scripts/audit-commands.mjs +117 -0
- package/scripts/audit-compatibility.mjs +149 -0
- package/scripts/audit-completion-readiness.mjs +292 -0
- package/scripts/audit-distribution.mjs +251 -0
- package/scripts/audit-domain-quality-gates.mjs +108 -0
- package/scripts/audit-evidence-placeholders.mjs +126 -0
- package/scripts/audit-final-acceptance-packet.mjs +148 -0
- package/scripts/audit-final-form-progress-report.mjs +161 -0
- package/scripts/audit-final-form-stale-copy.mjs +221 -0
- package/scripts/audit-final-readiness-promotion.mjs +255 -0
- package/scripts/audit-final-readiness.mjs +226 -0
- package/scripts/audit-fixtures.mjs +154 -0
- package/scripts/audit-fresh-session-readiness.mjs +177 -0
- package/scripts/audit-gse.mjs +275 -0
- package/scripts/audit-host-adapters.mjs +119 -0
- package/scripts/audit-host-runtime-evidence-handoff.mjs +159 -0
- package/scripts/audit-host-runtime-invocation-drill.mjs +240 -0
- package/scripts/audit-host-runtime-invocations.mjs +254 -0
- package/scripts/audit-host-ui-invocation.mjs +132 -0
- package/scripts/audit-learning-system.mjs +103 -0
- package/scripts/audit-local-final-form-completion.mjs +131 -0
- package/scripts/audit-marketplace-discovery.mjs +122 -0
- package/scripts/audit-npm-package-metadata.mjs +155 -0
- package/scripts/audit-npm-publish-dry-run.mjs +169 -0
- package/scripts/audit-npm-tarball-install.mjs +191 -0
- package/scripts/audit-open-source-defaults.mjs +92 -0
- package/scripts/audit-open-source-readiness.mjs +97 -0
- package/scripts/audit-owner-external-gate-kit.mjs +218 -0
- package/scripts/audit-project.mjs +323 -0
- package/scripts/audit-public-acceptance-command-dry-run-drill.mjs +203 -0
- package/scripts/audit-public-acceptance-handoff.mjs +142 -0
- package/scripts/audit-public-acceptance-readiness.mjs +224 -0
- package/scripts/audit-public-channel-publication.mjs +248 -0
- package/scripts/audit-public-ci-run.mjs +184 -0
- package/scripts/audit-public-collaboration-templates.mjs +98 -0
- package/scripts/audit-public-external-gate-probe.mjs +206 -0
- package/scripts/audit-public-release-checklist.mjs +133 -0
- package/scripts/audit-public-release-decision.mjs +201 -0
- package/scripts/audit-public-release-metadata.mjs +176 -0
- package/scripts/audit-public-repository-settings.mjs +237 -0
- package/scripts/audit-public-security-contact.mjs +171 -0
- package/scripts/audit-readme-docs.mjs +185 -0
- package/scripts/audit-recovery-readiness.mjs +98 -0
- package/scripts/audit-release-bundle.mjs +251 -0
- package/scripts/audit-release-owner-action-plan-drill.mjs +277 -0
- package/scripts/audit-release-owner-action-plan.mjs +164 -0
- package/scripts/audit-release-readiness.mjs +106 -0
- package/scripts/audit-release-status-manifest.mjs +165 -0
- package/scripts/audit-release-trust.mjs +62 -0
- package/scripts/audit-remote-distribution.mjs +266 -0
- package/scripts/audit-roadmap-consistency.mjs +235 -0
- package/scripts/audit-signing.mjs +147 -0
- package/scripts/audit-state-freshness.mjs +113 -0
- package/scripts/audit-target-adoption-evidence.mjs +117 -0
- package/scripts/audit-target-project.mjs +507 -0
- package/scripts/audit-update-release-acceptance.mjs +136 -0
- package/scripts/audit-v1-target-validation.mjs +269 -0
- package/scripts/audit-validation-profiles.mjs +125 -0
- package/scripts/close-change.mjs +116 -0
- package/scripts/discover-project-profile.mjs +307 -0
- package/scripts/forward-test-gse.mjs +146 -0
- package/scripts/generate-command-adapter.mjs +231 -0
- package/scripts/generate-final-acceptance-packet.mjs +181 -0
- package/scripts/generate-final-form-progress-report.mjs +205 -0
- package/scripts/generate-host-adapter.mjs +73 -0
- package/scripts/generate-host-runtime-evidence-handoff.mjs +206 -0
- package/scripts/generate-owner-external-gate-kit.mjs +295 -0
- package/scripts/generate-public-acceptance-handoff.mjs +168 -0
- package/scripts/generate-public-release-checklist.mjs +207 -0
- package/scripts/generate-release-bundle.mjs +505 -0
- package/scripts/generate-release-owner-action-plan.mjs +172 -0
- package/scripts/generate-release-status-manifest.mjs +200 -0
- package/scripts/generate-session-prompt.mjs +188 -0
- package/scripts/gse.mjs +67 -0
- package/scripts/init-change.mjs +265 -0
- package/scripts/init-project.mjs +785 -0
- package/scripts/install-gse.mjs +234 -0
- package/scripts/lib/evidence-placeholders.mjs +28 -0
- package/scripts/package-gse.mjs +174 -0
- package/scripts/probe-public-external-gates.mjs +167 -0
- package/scripts/record-host-invocation.mjs +151 -0
- package/scripts/record-learning.mjs +137 -0
- package/scripts/record-public-channel-publication.mjs +178 -0
- package/scripts/record-public-ci-run.mjs +180 -0
- package/scripts/record-public-release.mjs +175 -0
- package/scripts/record-public-repository-settings.mjs +209 -0
- package/scripts/record-public-security-contact.mjs +157 -0
- package/scripts/run-gse-command.mjs +538 -0
- package/scripts/run-validation-profile.mjs +146 -0
- package/scripts/sign-gse-package.mjs +83 -0
- package/scripts/update-project-state.mjs +223 -0
- package/scripts/validate-gse.mjs +1168 -0
- package/scripts/verify-gse-package.mjs +85 -0
|
@@ -0,0 +1,185 @@
|
|
|
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
|
+
const root = path.resolve(readArg('--root', path.join(import.meta.dirname, '..')))
|
|
14
|
+
const jsonOnly = args.includes('--json')
|
|
15
|
+
|
|
16
|
+
function read(relativePath) {
|
|
17
|
+
const fullPath = path.join(root, relativePath)
|
|
18
|
+
return fs.existsSync(fullPath) ? fs.readFileSync(fullPath, 'utf8') : ''
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function exists(relativePath) {
|
|
22
|
+
return fs.existsSync(path.join(root, relativePath))
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function check(id, label, ok, evidence, risk = '') {
|
|
26
|
+
return { id, label, status: ok ? 'passed' : 'failed', evidence, risk }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function section(text, heading) {
|
|
30
|
+
const start = text.indexOf(heading)
|
|
31
|
+
if (start === -1) return ''
|
|
32
|
+
const next = text.indexOf('\n## ', start + heading.length)
|
|
33
|
+
return next === -1 ? text.slice(start) : text.slice(start, next)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function z(codes) {
|
|
37
|
+
return String.fromCodePoint(...codes)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function hasBom(relativePath) {
|
|
41
|
+
const fullPath = path.join(root, relativePath)
|
|
42
|
+
if (!fs.existsSync(fullPath)) return false
|
|
43
|
+
const bytes = fs.readFileSync(fullPath)
|
|
44
|
+
return bytes.length >= 3 && bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function hasEncodingDamage(text) {
|
|
48
|
+
const mojibakeSentinels = [
|
|
49
|
+
'\uFFFD',
|
|
50
|
+
String.fromCodePoint(0x951b),
|
|
51
|
+
String.fromCodePoint(0x9286),
|
|
52
|
+
String.fromCodePoint(0x9428),
|
|
53
|
+
String.fromCodePoint(0x95c8),
|
|
54
|
+
String.fromCodePoint(0x6d93),
|
|
55
|
+
String.fromCodePoint(0x7ecb),
|
|
56
|
+
String.fromCodePoint(0x6d60),
|
|
57
|
+
]
|
|
58
|
+
return mojibakeSentinels.some((item) => text.includes(item))
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const english = read('README.md')
|
|
62
|
+
const chinese = read('README.zh-CN.md')
|
|
63
|
+
const skill = read('SKILL.md')
|
|
64
|
+
const validate = read('scripts/validate-gse.mjs')
|
|
65
|
+
const packaging = read('references/packaging.md')
|
|
66
|
+
const englishCommunity = section(english, '## Community')
|
|
67
|
+
const zh = {
|
|
68
|
+
community: z([0x23, 0x23, 0x20, 0x793e, 0x533a]),
|
|
69
|
+
noPretend: z([0x4e0d, 0x8981, 0x5047, 0x88c5]),
|
|
70
|
+
noGroundlessClaim: z([0x4e0d, 0x80fd, 0x51ed, 0x7a7a, 0x5ba3, 0x79f0]),
|
|
71
|
+
noGateHubDependency: z([0x4e0d, 0x4f9d, 0x8d56, 0x20, 0x47, 0x61, 0x74, 0x65, 0x48, 0x75, 0x62]),
|
|
72
|
+
notRegistryCenter: z([0x4e0d, 0x662f, 0x20, 0x47, 0x53, 0x45, 0x20, 0x7684, 0x5305, 0x6ce8, 0x518c, 0x4e2d, 0x5fc3]),
|
|
73
|
+
securityDisclosureChannel: z([0x5b89, 0x5168, 0x6f0f, 0x6d1e, 0x62ab, 0x9732, 0x6e20, 0x9053]),
|
|
74
|
+
hostRuntimeCertification: 'host runtime ' + z([0x8ba4, 0x8bc1]),
|
|
75
|
+
highlights: z([0x23, 0x23, 0x20, 0x4eae, 0x70b9]),
|
|
76
|
+
quickStart: z([0x23, 0x23, 0x20, 0x5feb, 0x901f, 0x5f00, 0x59cb]),
|
|
77
|
+
longRunningAgentProjects: z([0x9762, 0x5411, 0x957f, 0x671f, 0x20, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x20, 0x8f85, 0x52a9, 0x8f6f, 0x4ef6, 0x9879, 0x76ee]),
|
|
78
|
+
whenToUse: z([0x23, 0x23, 0x20, 0x4ec0, 0x4e48, 0x65f6, 0x5019, 0x7528, 0x20, 0x47, 0x53, 0x45]),
|
|
79
|
+
manyAgentSessions: z([0x9879, 0x76ee, 0x4f1a, 0x8de8, 0x5f88, 0x591a, 0x20, 0x61, 0x67, 0x65, 0x6e, 0x74, 0x20, 0x4f1a, 0x8bdd, 0x6301, 0x7eed, 0x63a8, 0x8fdb]),
|
|
80
|
+
whatItCreates: z([0x23, 0x23, 0x20, 0x4f1a, 0x521b, 0x5efa, 0x4ec0, 0x4e48]),
|
|
81
|
+
projectLayout: z([0x23, 0x23, 0x20, 0x9879, 0x76ee, 0x7ed3, 0x6784]),
|
|
82
|
+
commands: z([0x23, 0x23, 0x20, 0x547d, 0x4ee4]),
|
|
83
|
+
documentation: z([0x23, 0x23, 0x20, 0x6587, 0x6863]),
|
|
84
|
+
recommendedTopics: z([0x63a8, 0x8350, 0x20, 0x47, 0x69, 0x74, 0x48, 0x75, 0x62, 0x20, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x73]),
|
|
85
|
+
recommendedDescription: z([0x63a8, 0x8350, 0x4ed3, 0x5e93, 0x20, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e]),
|
|
86
|
+
largeProjectFirstAdoption: z([0x5927, 0x9879, 0x76ee, 0x7b2c, 0x4e00, 0x6b21, 0x63a5, 0x5165, 0x4e5f, 0x53ef, 0x4ee5, 0x76f4, 0x63a5, 0x4f7f, 0x7528]),
|
|
87
|
+
gsePosition: z([0x23, 0x23, 0x20, 0x47, 0x53, 0x45, 0x20, 0x7684, 0x4f4d, 0x7f6e]),
|
|
88
|
+
searchTerms: z([0x641c, 0x7d22, 0x8bcd]),
|
|
89
|
+
gatehubSupports: 'GateHub' + z([0xff08]) + '[gatehub.top](https://gatehub.top/)' + z([0xff09, 0x652f, 0x6301, 0x20, 0x47, 0x53, 0x45, 0x20, 0x7684, 0x5f00, 0x53d1]),
|
|
90
|
+
installation: z([0x23, 0x23, 0x20, 0x5b89, 0x88c5]),
|
|
91
|
+
}
|
|
92
|
+
const chineseCommunity = section(chinese, zh.community)
|
|
93
|
+
|
|
94
|
+
const sharedTerms = [
|
|
95
|
+
'Goal -> Spec -> Execute -> Evidence -> Learn',
|
|
96
|
+
'/gse continue',
|
|
97
|
+
'/gse status',
|
|
98
|
+
'result -> verified -> accepted',
|
|
99
|
+
'validate-gse.mjs',
|
|
100
|
+
'.gse/',
|
|
101
|
+
'spec-driven development',
|
|
102
|
+
]
|
|
103
|
+
|
|
104
|
+
const defensivePhrases = [
|
|
105
|
+
'not the package registry',
|
|
106
|
+
'security disclosure channel',
|
|
107
|
+
'host-runtime certification',
|
|
108
|
+
'does not require GateHub',
|
|
109
|
+
'not required to use GSE',
|
|
110
|
+
'Do not claim',
|
|
111
|
+
'Do not fake',
|
|
112
|
+
zh.noPretend,
|
|
113
|
+
zh.noGroundlessClaim,
|
|
114
|
+
zh.noGateHubDependency,
|
|
115
|
+
zh.notRegistryCenter,
|
|
116
|
+
zh.securityDisclosureChannel,
|
|
117
|
+
zh.hostRuntimeCertification,
|
|
118
|
+
]
|
|
119
|
+
|
|
120
|
+
const checks = [
|
|
121
|
+
check('RD01', 'English README exists', exists('README.md'), 'README.md'),
|
|
122
|
+
check('RD02', 'Chinese README exists', exists('README.zh-CN.md'), 'README.zh-CN.md'),
|
|
123
|
+
check('RD02A', 'READMEs link to each other for language switching', english.includes('[简体中文](README.zh-CN.md)') && chinese.includes('[English](README.md)'), 'README.md, README.zh-CN.md'),
|
|
124
|
+
check('RD03', 'READMEs share core concepts and commands', sharedTerms.every((term) => english.includes(term) && chinese.includes(term)), sharedTerms.join(', ')),
|
|
125
|
+
check('RD04', 'READMEs open with value, quick start, and highlights', english.includes('## Highlights') && english.includes('## Quick Start') && english.includes('Goal-Spec-Evidence Engineering for long-running') && chinese.includes(zh.highlights) && chinese.includes(zh.quickStart) && chinese.includes(zh.longRunningAgentProjects), 'README.md, README.zh-CN.md'),
|
|
126
|
+
check('RD05', 'READMEs explain when to use GSE', english.includes('## When To Use GSE') && english.includes('the project will continue across many agent sessions') && chinese.includes(zh.whenToUse) && chinese.includes(zh.manyAgentSessions), 'README.md, README.zh-CN.md'),
|
|
127
|
+
check('RD06', 'SKILL links bilingual README docs', skill.includes('README.md') && skill.includes('README.zh-CN.md'), 'SKILL.md'),
|
|
128
|
+
check('RD07', 'validate-gse runs README docs audit', validate.includes('audit-readme-docs.mjs'), 'scripts/validate-gse.mjs'),
|
|
129
|
+
check('RD08', 'READMEs include open-source style usage sections', english.includes('## What It Creates') && english.includes('## Project Layout') && english.includes('## Commands') && english.includes('## Documentation') && chinese.includes(zh.whatItCreates) && chinese.includes(zh.projectLayout) && chinese.includes(zh.commands) && chinese.includes(zh.documentation), 'README.md, README.zh-CN.md'),
|
|
130
|
+
check('RD09', 'READMEs avoid machine-local absolute paths', !english.includes('C:\\Users\\Admin') && !chinese.includes('C:\\Users\\Admin') && english.includes('node scripts/validate-gse.mjs --root . --json') && chinese.includes('node scripts/validate-gse.mjs --root . --json'), 'README.md, README.zh-CN.md'),
|
|
131
|
+
check('RD10', 'READMEs avoid private project examples', !english.includes('AION') && !chinese.includes('AION') && !english.includes('aion-productization') && !chinese.includes('aion-productization'), 'README.md, README.zh-CN.md'),
|
|
132
|
+
check('RD11', 'READMEs avoid maintainer-only GitHub metadata blocks', !english.includes('Recommended GitHub topics') && !chinese.includes(zh.recommendedTopics) && !english.includes('Recommended repository description') && !chinese.includes(zh.recommendedDescription), 'README.md, README.zh-CN.md'),
|
|
133
|
+
check('RD12', 'READMEs avoid defensive caveat bloat', defensivePhrases.every((phrase) => !english.includes(phrase) && !chinese.includes(phrase)), 'README.md, README.zh-CN.md'),
|
|
134
|
+
check('RD13', 'READMEs explain scaffold modes and project workspace', english.includes('lite') && english.includes('standard') && english.includes('enterprise') && english.includes('Large projects can start directly') && chinese.includes('lite') && chinese.includes('standard') && chinese.includes('enterprise') && chinese.includes(zh.largeProjectFirstAdoption), 'README.md, README.zh-CN.md'),
|
|
135
|
+
check('RD14', 'READMEs explain goal hierarchy and ecosystem positioning naturally', english.includes('.gse/goals/') && english.includes('agentic engineering') && english.includes('spec-driven development') && english.includes('SDD') && !english.includes('## How GSE Fits') && chinese.includes('.gse/goals/') && chinese.includes('agentic engineering') && chinese.includes('spec-driven development') && chinese.includes('SDD') && !chinese.includes(zh.gsePosition) && !english.includes('Search Terms') && !chinese.includes(zh.searchTerms), 'README.md, README.zh-CN.md'),
|
|
136
|
+
check('RD15', 'README community sections are concise and non-defensive', englishCommunity.includes('GateHub ([gatehub.top](https://gatehub.top/)) supports GSE development') && !englishCommunity.includes('SUPPORT.md') && !englishCommunity.includes('CONTRIBUTING.md') && englishCommunity.length < 240 && chineseCommunity.includes(zh.gatehubSupports) && !chineseCommunity.includes('SUPPORT.md') && !chineseCommunity.includes('CONTRIBUTING.md') && chineseCommunity.length < 140, 'README.md, README.zh-CN.md'),
|
|
137
|
+
check('RD16', 'READMEs keep release and security caveats in deeper docs', english.includes('## License') && chinese.includes('## License') && read('references/community-channels.md').includes('vulnerability disclosure channel') && read('references/public-release.md').includes('references/community-channels.md'), 'README.md, README.zh-CN.md, references/community-channels.md, references/public-release.md'),
|
|
138
|
+
check('RD17', 'READMEs include package, install, and npm metadata commands', english.includes('## Installation') && chinese.includes(zh.installation) && english.includes('node scripts/package-gse.mjs --root . --out <package-dir> --label <release-label>') && chinese.includes('node scripts/package-gse.mjs --root . --out <package-dir> --label <release-label>') && english.includes('node scripts/install-gse.mjs --source <package-dir> --target <install-skill-dir>') && chinese.includes('node scripts/install-gse.mjs --source <package-dir> --target <install-skill-dir>') && english.includes('node scripts/install-gse.mjs --source-url <file-or-http-package-url> --target <install-skill-dir>') && chinese.includes('node scripts/install-gse.mjs --source-url <file-or-http-package-url> --target <install-skill-dir>') && english.includes('node scripts/audit-npm-package-metadata.mjs --root . --json') && chinese.includes('node scripts/audit-npm-package-metadata.mjs --root . --json') && english.includes('node scripts/audit-npm-tarball-install.mjs --root . --json') && chinese.includes('node scripts/audit-npm-tarball-install.mjs --root . --json') && english.includes('node scripts/audit-npm-publish-dry-run.mjs --root . --json') && chinese.includes('node scripts/audit-npm-publish-dry-run.mjs --root . --json') && packaging.includes('audit-npm-package-metadata.mjs') && packaging.includes('audit-npm-tarball-install.mjs') && packaging.includes('audit-npm-publish-dry-run.mjs') && packaging.includes('npm pack --dry-run --json') && english.includes('references/packaging.md') && chinese.includes('references/packaging.md'), 'README.md, README.zh-CN.md, references/packaging.md'),
|
|
139
|
+
check('RD18', 'READMEs have no BOM, replacement characters, or common mojibake sentinels', !hasBom('README.md') && !hasBom('README.zh-CN.md') && !hasEncodingDamage(english) && !hasEncodingDamage(chinese), 'README.md, README.zh-CN.md'),
|
|
140
|
+
]
|
|
141
|
+
|
|
142
|
+
const passed = checks.filter((item) => item.status === 'passed').length
|
|
143
|
+
const failed = checks.length - passed
|
|
144
|
+
const report = {
|
|
145
|
+
root,
|
|
146
|
+
generatedAt: new Date().toISOString(),
|
|
147
|
+
summary: { status: failed === 0 ? 'passed' : 'failed', passed, failed, total: checks.length },
|
|
148
|
+
workflows: { readmeDocs: failed === 0 ? 'verified' : 'failed' },
|
|
149
|
+
limits: [
|
|
150
|
+
'This audit checks bilingual README coverage, public-facing clarity, and avoidance of defensive caveat bloat.',
|
|
151
|
+
'Release, marketplace, security, and host-runtime boundaries belong in deeper reference docs and dedicated audits.',
|
|
152
|
+
],
|
|
153
|
+
checks,
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function renderMarkdown(data) {
|
|
157
|
+
const lines = []
|
|
158
|
+
lines.push('# GSE README Docs Audit')
|
|
159
|
+
lines.push('')
|
|
160
|
+
lines.push('Generated: ' + data.generatedAt)
|
|
161
|
+
lines.push('Root: ' + data.root)
|
|
162
|
+
lines.push('')
|
|
163
|
+
lines.push('## Summary')
|
|
164
|
+
lines.push('')
|
|
165
|
+
lines.push('- Status: ' + data.summary.status)
|
|
166
|
+
lines.push('- Checks: ' + data.summary.passed + '/' + data.summary.total)
|
|
167
|
+
lines.push('- README docs: ' + data.workflows.readmeDocs)
|
|
168
|
+
lines.push('')
|
|
169
|
+
lines.push('## Checks')
|
|
170
|
+
lines.push('')
|
|
171
|
+
for (const item of data.checks) {
|
|
172
|
+
const marker = item.status === 'passed' ? '[x]' : '[ ]'
|
|
173
|
+
lines.push('- ' + marker + ' ' + item.id + ' ' + item.label + ': ' + item.evidence)
|
|
174
|
+
}
|
|
175
|
+
lines.push('')
|
|
176
|
+
lines.push('## Limits')
|
|
177
|
+
lines.push('')
|
|
178
|
+
for (const item of data.limits) lines.push('- ' + item)
|
|
179
|
+
return lines.join('\n') + '\n'
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (jsonOnly) console.log(JSON.stringify(report, null, 2))
|
|
183
|
+
else console.log(renderMarkdown(report))
|
|
184
|
+
|
|
185
|
+
if (failed > 0) process.exit(1)
|
|
@@ -0,0 +1,98 @@
|
|
|
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
|
+
const root = path.resolve(readArg('--root', path.join(import.meta.dirname, '..')))
|
|
14
|
+
const jsonOnly = args.includes('--json')
|
|
15
|
+
|
|
16
|
+
function read(relativePath) {
|
|
17
|
+
const fullPath = path.join(root, relativePath)
|
|
18
|
+
return fs.existsSync(fullPath) ? fs.readFileSync(fullPath, 'utf8') : ''
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function exists(relativePath) {
|
|
22
|
+
return fs.existsSync(path.join(root, relativePath))
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function check(id, label, ok, evidence, risk = '') {
|
|
26
|
+
return { id, label, status: ok ? 'passed' : 'failed', evidence, risk }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const recovery = read('references/recovery.md')
|
|
30
|
+
const release = read('references/release.md')
|
|
31
|
+
const incident = read('assets/templates/incident-review.md')
|
|
32
|
+
const quality = read('references/quality-gates.md')
|
|
33
|
+
|
|
34
|
+
const recoveryClasses = ['recoverable failure', 'blocked work', 'rollback required', 'handoff required', 'incident recovery']
|
|
35
|
+
const failedVerificationSteps = ['Preserve the failed command', 'Decide whether the failure invalidates the result', 'Fix the smallest reusable artifact', 'Re-run the same focused verification', 'mark readiness `not ready` or `result`']
|
|
36
|
+
const releaseRecoveryFields = ['Release scope:', 'Failed gate:', 'Release level:', 'Readiness after failure:', 'Rollback or resume decision:', 'Verification to rerun:', 'Next action:']
|
|
37
|
+
const continuationFields = ['Goal or spec:', 'Active slice:', 'Current evidence status:', 'Changed files:', 'Failed or unavailable tools:', 'Next verification command:', 'Next action:']
|
|
38
|
+
const incidentFields = ['Incident title:', 'Evidence status:', 'Affected users/systems:', 'Data/security/privacy impact:', 'Verification that stabilization worked:', 'Action Items', 'Follow-Up Evidence']
|
|
39
|
+
|
|
40
|
+
const checks = [
|
|
41
|
+
check('RC01', 'recovery reference exists', exists('references/recovery.md'), 'references/recovery.md'),
|
|
42
|
+
check('RC02', 'failure classes cover core recovery cases', recoveryClasses.every((item) => recovery.includes(item)), recoveryClasses.join(', ')),
|
|
43
|
+
check('RC03', 'failed verification path prevents lowering acceptance', recovery.includes('do not reduce the acceptance bar') && failedVerificationSteps.every((item) => recovery.includes(item)), 'Failed Verification Path'),
|
|
44
|
+
check('RC04', 'release recovery record has required fields', releaseRecoveryFields.every((item) => recovery.includes(item)), releaseRecoveryFields.join(', ')),
|
|
45
|
+
check('RC05', 'release recovery routes through release gates', recovery.includes('references/release.md') && release.includes('Use references/recovery.md') && quality.includes('Use `references/recovery.md`'), 'release, recovery, and quality integration'),
|
|
46
|
+
check('RC06', 'rollback rules protect user and unrelated work', recovery.includes('Do not revert user work or unrelated dirty files') && recovery.includes('references/file-ownership.md'), 'rollback ownership rules'),
|
|
47
|
+
check('RC07', 'incident template includes commercial follow-up fields', incidentFields.every((item) => incident.includes(item)), incidentFields.join(', ')),
|
|
48
|
+
check('RC08', 'incident follow-up distinguishes signal, stabilization, prevention, and evidence', incident.includes('Detection gap') && incident.includes('Stabilization') && incident.includes('What Prevents Recurrence') && incident.includes('Remaining risk'), 'incident-review.md sections'),
|
|
49
|
+
check('RC09', 'future-agent continuation packet is defined', continuationFields.every((item) => recovery.includes(item)), continuationFields.join(', ')),
|
|
50
|
+
check('RC10', 'fresh-session acceptance is not faked during handoff', recovery.includes('accepted by: fresh-session') && recovery.includes('only after a separate session actually follows the packet'), 'future-agent continuation rule'),
|
|
51
|
+
check('RC11', 'recovery evidence status routes through taxonomy', recovery.includes('references/evidence-taxonomy.md') && recovery.includes('result, verified, accepted, blocked, rollback required, or not ready'), 'handoff evidence status'),
|
|
52
|
+
check('RC12', 'incident review is scoped, not mandatory for every failure', recovery.includes('Do not turn every failed command into an incident'), 'incident scope rule'),
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
const passed = checks.filter((item) => item.status === 'passed').length
|
|
56
|
+
const failed = checks.length - passed
|
|
57
|
+
const report = {
|
|
58
|
+
root,
|
|
59
|
+
generatedAt: new Date().toISOString(),
|
|
60
|
+
summary: { status: failed === 0 ? 'passed' : 'failed', passed, failed, total: checks.length },
|
|
61
|
+
workflows: { recoveryReadiness: failed === 0 ? 'verified' : 'failed' },
|
|
62
|
+
limits: [
|
|
63
|
+
'Recovery readiness audit verifies reusable recovery and incident workflow coverage; it does not exercise a live incident or release rollback.',
|
|
64
|
+
'Production readiness still depends on project-specific monitors, release tooling, ownership, and environment evidence.',
|
|
65
|
+
],
|
|
66
|
+
checks,
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function renderMarkdown(data) {
|
|
70
|
+
const lines = []
|
|
71
|
+
lines.push('# GSE Recovery Readiness Audit')
|
|
72
|
+
lines.push('')
|
|
73
|
+
lines.push('Generated: ' + data.generatedAt)
|
|
74
|
+
lines.push('Root: ' + data.root)
|
|
75
|
+
lines.push('')
|
|
76
|
+
lines.push('## Summary')
|
|
77
|
+
lines.push('')
|
|
78
|
+
lines.push('- Status: ' + data.summary.status)
|
|
79
|
+
lines.push('- Checks: ' + data.summary.passed + '/' + data.summary.total)
|
|
80
|
+
lines.push('- Recovery readiness: ' + data.workflows.recoveryReadiness)
|
|
81
|
+
lines.push('')
|
|
82
|
+
lines.push('## Checks')
|
|
83
|
+
lines.push('')
|
|
84
|
+
for (const item of data.checks) {
|
|
85
|
+
const marker = item.status === 'passed' ? '[x]' : '[ ]'
|
|
86
|
+
lines.push('- ' + marker + ' ' + item.id + ' ' + item.label + ': ' + item.evidence)
|
|
87
|
+
}
|
|
88
|
+
lines.push('')
|
|
89
|
+
lines.push('## Limits')
|
|
90
|
+
lines.push('')
|
|
91
|
+
for (const item of data.limits) lines.push('- ' + item)
|
|
92
|
+
return lines.join('\n') + '\n'
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (jsonOnly) console.log(JSON.stringify(report, null, 2))
|
|
96
|
+
else console.log(renderMarkdown(report))
|
|
97
|
+
|
|
98
|
+
if (failed > 0) process.exit(1)
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { mkdtempSync, rmSync } from 'node:fs'
|
|
5
|
+
import { tmpdir } from 'node:os'
|
|
6
|
+
import { spawnSync } from 'node:child_process'
|
|
7
|
+
import crypto from 'node:crypto'
|
|
8
|
+
|
|
9
|
+
const args = process.argv.slice(2)
|
|
10
|
+
|
|
11
|
+
function readArg(name, fallback = null) {
|
|
12
|
+
const index = args.indexOf(name)
|
|
13
|
+
if (index === -1) return fallback
|
|
14
|
+
return args[index + 1] ?? fallback
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const root = path.resolve(readArg('--root', path.join(import.meta.dirname, '..')))
|
|
18
|
+
const jsonOnly = args.includes('--json')
|
|
19
|
+
|
|
20
|
+
function exists(relativePath) {
|
|
21
|
+
return fs.existsSync(path.join(root, relativePath))
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function read(relativePath) {
|
|
25
|
+
const fullPath = path.join(root, relativePath)
|
|
26
|
+
return fs.existsSync(fullPath) ? fs.readFileSync(fullPath, 'utf8') : ''
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function run(command, commandArgs) {
|
|
30
|
+
const result = spawnSync(command, commandArgs, {
|
|
31
|
+
cwd: root,
|
|
32
|
+
encoding: 'utf8',
|
|
33
|
+
windowsHide: true,
|
|
34
|
+
})
|
|
35
|
+
return {
|
|
36
|
+
status: result.status ?? 1,
|
|
37
|
+
stdout: (result.stdout ?? '').trim(),
|
|
38
|
+
stderr: (result.stderr ?? '').trim(),
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function parseJson(stdout) {
|
|
43
|
+
try {
|
|
44
|
+
return JSON.parse(stdout)
|
|
45
|
+
} catch {
|
|
46
|
+
return null
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function check(id, label, ok, evidence, risk = '') {
|
|
51
|
+
return { id, label, status: ok ? 'passed' : 'failed', evidence, risk }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function sha256File(filePath) {
|
|
55
|
+
return crypto.createHash('sha256').update(fs.readFileSync(filePath)).digest('hex')
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const tempRoot = mkdtempSync(path.join(tmpdir(), 'gse-release-bundle-audit-'))
|
|
59
|
+
const bundlePath = path.join(tempRoot, 'bundle')
|
|
60
|
+
const bundle = run(process.execPath, [
|
|
61
|
+
path.join(root, 'scripts', 'generate-release-bundle.mjs'),
|
|
62
|
+
'--root', root,
|
|
63
|
+
'--label', 'gse-release-bundle-audit',
|
|
64
|
+
'--out', bundlePath,
|
|
65
|
+
'--force',
|
|
66
|
+
'--json',
|
|
67
|
+
])
|
|
68
|
+
const bundleData = parseJson(bundle.stdout)
|
|
69
|
+
function readBundle(fileName) {
|
|
70
|
+
const fullPath = path.join(bundlePath, fileName)
|
|
71
|
+
return fs.existsSync(fullPath) ? fs.readFileSync(fullPath, 'utf8') : ''
|
|
72
|
+
}
|
|
73
|
+
const summary = readBundle('release-summary.md')
|
|
74
|
+
const manifest = readBundle('bundle-manifest.json')
|
|
75
|
+
const checklist = readBundle('validation-checklist.md')
|
|
76
|
+
const record = readBundle('public-release-record.md')
|
|
77
|
+
const handoff = readBundle('public-acceptance-handoff.md')
|
|
78
|
+
const hostHandoff = readBundle('host-runtime-evidence-handoff.md')
|
|
79
|
+
const releaseStatusManifest = readBundle('release-status-manifest.json')
|
|
80
|
+
const releaseOwnerActionPlan = readBundle('release-owner-action-plan.md')
|
|
81
|
+
const publicReleaseChecklist = readBundle('public-release-checklist.md')
|
|
82
|
+
const ownerExternalGateKitReadme = readBundle('owner-external-gate-kit/README.md')
|
|
83
|
+
const ownerExternalGateKitManifest = readBundle('owner-external-gate-kit/kit-manifest.json')
|
|
84
|
+
const ownerExternalGateKitRecordCommands = readBundle('owner-external-gate-kit/record-commands.md')
|
|
85
|
+
const ownerExternalGateKitVerificationCommands = readBundle('owner-external-gate-kit/verification-commands.md')
|
|
86
|
+
const installablePackageManifest = readBundle('installable-package/gse-package-manifest.json')
|
|
87
|
+
const provenance = readBundle('provenance.json')
|
|
88
|
+
const checksums = readBundle('checksums.sha256')
|
|
89
|
+
const releaseStatusData = parseJson(releaseStatusManifest)
|
|
90
|
+
const pendingGates = releaseStatusData?.publicAcceptance?.pendingGates ?? []
|
|
91
|
+
const pendingAreas = pendingGates.map((gate) => gate.area)
|
|
92
|
+
const hasPendingRegistryPublication = pendingAreas.includes('Public registry publication')
|
|
93
|
+
const bundleManifestData = parseJson(manifest)
|
|
94
|
+
const ownerExternalGateKitData = parseJson(ownerExternalGateKitManifest)
|
|
95
|
+
const installablePackageManifestData = parseJson(installablePackageManifest)
|
|
96
|
+
const provenanceData = parseJson(provenance)
|
|
97
|
+
const validate = read('scripts/validate-gse.mjs')
|
|
98
|
+
const packaging = read('references/packaging.md')
|
|
99
|
+
const canonicalReleaseStatusManifest = parseJson(read('.gse/acceptance/release-status-manifest.json'))
|
|
100
|
+
const canonicalBundleReleaseOwnerActionPlan = read('.gse/release-bundles/gse-release-bundle-audit/release-owner-action-plan.md')
|
|
101
|
+
const canonicalBundlePublicReleaseChecklist = read('.gse/release-bundles/gse-release-bundle-audit/public-release-checklist.md')
|
|
102
|
+
const canonicalBundleKitReleaseOwnerActionPlan = read('.gse/release-bundles/gse-release-bundle-audit/owner-external-gate-kit/release-owner-action-plan.md')
|
|
103
|
+
const canonicalBundleManifestData = parseJson(read('.gse/release-bundles/gse-release-bundle-audit/bundle-manifest.json'))
|
|
104
|
+
const installedFromBundlePath = path.join(tempRoot, 'installed-from-bundle')
|
|
105
|
+
const installFromBundle = fs.existsSync(path.join(bundlePath, 'installable-package', 'gse-package-manifest.json'))
|
|
106
|
+
? run(process.execPath, [
|
|
107
|
+
path.join(root, 'scripts', 'install-gse.mjs'),
|
|
108
|
+
'--source',
|
|
109
|
+
path.join(bundlePath, 'installable-package'),
|
|
110
|
+
'--target',
|
|
111
|
+
installedFromBundlePath,
|
|
112
|
+
'--json',
|
|
113
|
+
])
|
|
114
|
+
: { status: 1, stdout: '', stderr: 'installable package missing' }
|
|
115
|
+
const installFromBundleData = parseJson(installFromBundle.stdout)
|
|
116
|
+
const installedFromBundleCli = fs.existsSync(path.join(installedFromBundlePath, 'scripts', 'gse.mjs'))
|
|
117
|
+
? run(process.execPath, [
|
|
118
|
+
path.join(installedFromBundlePath, 'scripts', 'gse.mjs'),
|
|
119
|
+
'status',
|
|
120
|
+
'--target',
|
|
121
|
+
installedFromBundlePath,
|
|
122
|
+
'--json',
|
|
123
|
+
])
|
|
124
|
+
: { status: 1, stdout: '', stderr: 'installed gse cli missing' }
|
|
125
|
+
const installedFromBundleCliData = parseJson(installedFromBundleCli.stdout)
|
|
126
|
+
|
|
127
|
+
function planMatchesManifestCounts(plan, data) {
|
|
128
|
+
if (!plan || !data) return false
|
|
129
|
+
const verifiedRows = data.readiness?.verified?.length ?? 0
|
|
130
|
+
const ownerRequiredRows = data.readiness?.ownerRequired?.length ?? 0
|
|
131
|
+
const externalRequiredRows = data.readiness?.externalRequired?.length ?? 0
|
|
132
|
+
const publicAccepted = data.publicAcceptance?.publicAccepted ?? data.claimBoundary?.publicAccepted ?? 'unknown'
|
|
133
|
+
const pendingGates = data.publicAcceptance?.pendingGates ?? []
|
|
134
|
+
return plan.includes('- Public accepted: ' + publicAccepted) &&
|
|
135
|
+
plan.includes('- Verified rows: ' + verifiedRows) &&
|
|
136
|
+
plan.includes('- Owner-required rows: ' + ownerRequiredRows) &&
|
|
137
|
+
plan.includes('- External-required rows: ' + externalRequiredRows) &&
|
|
138
|
+
pendingGates.every((gate) => plan.includes('#### ' + gate.area)) &&
|
|
139
|
+
!plan.includes('#### License decision')
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function hasLocalPathLeak(text) {
|
|
143
|
+
const escapedRoot = root.replace(/\\/g, '\\\\')
|
|
144
|
+
const forwardRoot = root.replace(/\\/g, '/')
|
|
145
|
+
const escapedForwardRoot = forwardRoot.replace(/\//g, '\\/')
|
|
146
|
+
return text.includes(root) ||
|
|
147
|
+
text.includes(escapedRoot) ||
|
|
148
|
+
text.includes(forwardRoot) ||
|
|
149
|
+
text.includes(escapedForwardRoot) ||
|
|
150
|
+
/C:\\\\Program Files\\\\nodejs\\\\node\.exe/i.test(text) ||
|
|
151
|
+
/C:\\Program Files\\nodejs\\node\.exe/i.test(text)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function verifyChecksumFile() {
|
|
155
|
+
if (!checksums.trim()) return false
|
|
156
|
+
const rows = checksums.trim().split(/\r?\n/)
|
|
157
|
+
return rows.every((row) => {
|
|
158
|
+
const match = row.match(/^([a-f0-9]{64}) (.+)$/)
|
|
159
|
+
if (!match) return false
|
|
160
|
+
const [, hash, relativePath] = match
|
|
161
|
+
const fullPath = path.join(bundlePath, relativePath)
|
|
162
|
+
if (!fs.existsSync(fullPath) || !fs.statSync(fullPath).isFile()) return false
|
|
163
|
+
return sha256File(fullPath) === hash
|
|
164
|
+
}) && rows.some((row) => row.endsWith('installable-package/gse-package-manifest.json'))
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const checks = [
|
|
168
|
+
check('RB01', 'release bundle generator exists', exists('scripts/generate-release-bundle.mjs'), 'scripts/generate-release-bundle.mjs'),
|
|
169
|
+
check('RB02', 'release bundle generation succeeds', bundle.status === 0 && bundleData?.status === 'written', 'generate-release-bundle audit run'),
|
|
170
|
+
check('RB03', 'bundle includes required handoff files', ['release-summary.md', 'install-commands.md', 'validation-checklist.md', 'public-release-record.md', 'public-acceptance-handoff.md', 'host-runtime-evidence-handoff.md', 'release-status-manifest.json', 'release-owner-action-plan.md', 'public-release-checklist.md', 'owner-external-gate-kit/README.md', 'owner-external-gate-kit/kit-manifest.json', 'installable-package/gse-package-manifest.json', 'provenance.json', 'checksums.sha256', 'bundle-manifest.json'].every((item) => fs.existsSync(path.join(bundlePath, item))), bundlePath),
|
|
171
|
+
check('RB04', 'bundle preserves license decision and public release boundary', summary.includes('Accepted public release: not accepted') && record.includes('License status: selected') && record.includes('SPDX identifier: MIT') && record.includes('Evidence status: accepted'), 'release-summary.md, public-release-record.md'),
|
|
172
|
+
check('RB05', 'bundle includes install, validation, and installed CLI commands', summary.includes('installable-package/') && summary.includes('install-gse.mjs') && summary.includes('validate-gse.mjs') && summary.includes('scripts/gse.mjs status') && checklist.includes('audit-remote-distribution.mjs'), 'release-summary.md, validation-checklist.md'),
|
|
173
|
+
check('RB05b', 'bundle includes host runtime drill before cross-host handoff', summary.includes('audit-host-runtime-invocation-drill.mjs') && checklist.includes('audit-host-runtime-invocation-drill.mjs'), 'release-summary.md, validation-checklist.md'),
|
|
174
|
+
check('RB06', 'bundle manifest records validation and limits', manifest.includes('"validation"') && manifest.includes('Bundle does not choose a license') && bundleManifestData?.publicReleaseAcceptance === 'not-accepted' && bundleManifestData?.licenseDecision === 'accepted' && manifest.includes('"publicAcceptanceHandoff": "included"') && manifest.includes('"hostRuntimeEvidenceHandoff": "included"') && manifest.includes('"releaseStatusManifest": "included"') && manifest.includes('"releaseOwnerActionPlan": "included"'), 'bundle-manifest.json'),
|
|
175
|
+
check('RB06v', 'bundle manifest records compact validation check details', Array.isArray(bundleManifestData?.validationChecks) && bundleManifestData.validationChecks.some((item) => item.id === 'validate-09o' && item.label === 'public evidence placeholder helper audit' && item.command?.includes('audit-evidence-placeholders.mjs')) && summary.includes('Validation manifest detail'), 'bundle-manifest.json, release-summary.md'),
|
|
176
|
+
check('RB06v2', 'canonical bundle manifest records current validation check details', Array.isArray(canonicalBundleManifestData?.validationChecks) && canonicalBundleManifestData.validationChecks.some((item) => item.id === 'validate-09o' && item.label === 'public evidence placeholder helper audit' && item.command?.includes('audit-evidence-placeholders.mjs')), '.gse/release-bundles/gse-release-bundle-audit/bundle-manifest.json'),
|
|
177
|
+
check('RB06v3', 'bundle manifest records installable package snapshot', bundleManifestData?.installablePackage?.path === 'installable-package/' && bundleManifestData?.installablePackage?.manifest === 'installable-package/gse-package-manifest.json' && bundleManifestData?.installablePackage?.algorithm === 'sha256' && bundleManifestData?.installablePackage?.cli === 'scripts/gse.mjs', 'bundle-manifest.json installablePackage'),
|
|
178
|
+
check('RB06v4', 'bundle installable package manifest is valid', installablePackageManifestData?.integrity?.algorithm === 'sha256' && installablePackageManifestData?.entrypoints?.cli === 'scripts/gse.mjs' && installablePackageManifestData?.fileHashes?.['SKILL.md'], 'installable-package/gse-package-manifest.json'),
|
|
179
|
+
check('RB06v5', 'bundle installable package installs and installed CLI runs', installFromBundle.status === 0 && installFromBundleData?.status === 'passed' && installFromBundleData?.summary?.integrityFailed === 0 && installedFromBundleCli.status === 0 && installedFromBundleCliData?.command === '/gse status' && installedFromBundleCliData?.project?.stateValid === true, 'installable-package install and gse status'),
|
|
180
|
+
check('RB06v6', 'bundle provenance records local generation boundaries', provenanceData?.generator === 'scripts/generate-release-bundle.mjs' && provenanceData?.installablePackage?.packageDigest === installablePackageManifestData?.integrity?.packageDigest && provenanceData?.publicAcceptance?.status === 'not-accepted' && Array.isArray(provenanceData?.claimBoundaries) && provenanceData.claimBoundaries.some((item) => item.includes('not a registry attestation')), 'provenance.json'),
|
|
181
|
+
check('RB06v7', 'bundle checksums verify installable package files', verifyChecksumFile(), 'checksums.sha256'),
|
|
182
|
+
check('RB06v8', 'bundle provenance and checksums do not expose local paths', !hasLocalPathLeak(provenance) && !hasLocalPathLeak(checksums), 'provenance.json, checksums.sha256'),
|
|
183
|
+
check('RB06a', 'bundle manifest does not expose local source root or Node install path', bundleManifestData && !Object.hasOwn(bundleManifestData, 'sourceRoot') && !hasLocalPathLeak(manifest), 'bundle-manifest.json'),
|
|
184
|
+
check('RB06b', 'bundle includes public acceptance handoff boundaries', handoff.includes('GSE Public Acceptance Handoff') && handoff.includes('Public accepted: not-accepted') && handoff.includes('Do not claim public release acceptance'), 'public-acceptance-handoff.md'),
|
|
185
|
+
check('RB06b2', 'bundle public acceptance handoff uses current registry publication CLI flag when pending', !hasPendingRegistryPublication || (handoff.includes('--proves-registry-publication true') && !handoff.includes('--proves-public-registry-publication')), 'public-acceptance-handoff.md'),
|
|
186
|
+
check('RB06b3', 'bundle public acceptance handoff includes dry-run preflight commands', handoff.includes('Preflight command') && handoff.includes('--dry-run --json'), 'public-acceptance-handoff.md'),
|
|
187
|
+
check('RB06c', 'bundle includes host runtime evidence handoff boundaries', hostHandoff.includes('GSE Host Runtime Evidence Handoff') && hostHandoff.includes('Do not claim native slash-command support') && hostHandoff.includes('record-host-invocation.mjs'), 'host-runtime-evidence-handoff.md'),
|
|
188
|
+
check('RB06d', 'bundle includes machine-readable release status manifest', releaseStatusData?.claimBoundary?.publicAccepted === 'not-accepted' && releaseStatusManifest.includes('"releaseStatusManifest"') && releaseStatusManifest.includes('"localInstalledCli": "verified"') && releaseStatusManifest.includes('"remoteInstalledCli": "verified"') && releaseStatusManifest.includes('"nativeSlashCommandRecords": 0') && releaseStatusManifest.includes('"fixtureDrill": "verified"') && releaseStatusManifest.includes('"fixtureEvidenceIsPersistent": false'), 'release-status-manifest.json'),
|
|
189
|
+
check('RB06d2', 'bundle release status manifest includes dry-run preflight commands', (releaseStatusData?.publicAcceptance?.nextPreflightCommands?.length ?? 0) === (releaseStatusData?.publicAcceptance?.pendingGates?.length ?? -1) && releaseStatusData.publicAcceptance.nextPreflightCommands.every((command) => command.includes('--dry-run --json')), 'release-status-manifest.json'),
|
|
190
|
+
check('RB06d3', 'bundle release status manifest includes preflight command drill', releaseStatusData?.verificationCommands?.some((command) => command.includes('audit-public-acceptance-command-dry-run-drill.mjs')), 'release-status-manifest.json'),
|
|
191
|
+
check('RB06e', 'bundle includes owner-facing release action plan', releaseOwnerActionPlan.includes('GSE Release Owner Action Plan') && pendingGates.every((gate) => releaseOwnerActionPlan.includes('#### ' + gate.area)) && !releaseOwnerActionPlan.includes('record-public-release.mjs') && releaseOwnerActionPlan.includes('Local validation does not mean public acceptance'), 'release-owner-action-plan.md'),
|
|
192
|
+
check('RB06e2', 'bundle owner action plan includes dry-run preflight commands and drill', releaseOwnerActionPlan.includes('Preflight command') && releaseOwnerActionPlan.includes('--dry-run --json') && releaseOwnerActionPlan.includes('audit-public-acceptance-command-dry-run-drill.mjs'), 'release-owner-action-plan.md'),
|
|
193
|
+
check('RB06e3', 'canonical release bundle owner action plans match current manifest counts and pending gates', planMatchesManifestCounts(canonicalBundleReleaseOwnerActionPlan, canonicalReleaseStatusManifest) && planMatchesManifestCounts(canonicalBundleKitReleaseOwnerActionPlan, canonicalReleaseStatusManifest), '.gse/release-bundles/gse-release-bundle-audit/release-owner-action-plan.md'),
|
|
194
|
+
check('RB06e4', 'bundle includes linear public release checklist', publicReleaseChecklist.includes('GSE Public Release Checklist') && publicReleaseChecklist.includes('01. Prepare the release bundle') && publicReleaseChecklist.includes('08. Record other host runtime invocation evidence') && publicReleaseChecklist.includes('Public accepted: not-accepted') && publicReleaseChecklist.includes('/gse release --execute --out <bundle>'), 'public-release-checklist.md'),
|
|
195
|
+
check('RB06e5', 'canonical release bundle checklist matches pending gate count', canonicalBundlePublicReleaseChecklist.includes('GSE Public Release Checklist') && canonicalBundlePublicReleaseChecklist.includes('Pending owner/external gates: ' + (canonicalReleaseStatusManifest?.publicAcceptance?.pendingGates?.length ?? 0)), '.gse/release-bundles/gse-release-bundle-audit/public-release-checklist.md'),
|
|
196
|
+
check('RB06f', 'bundle includes owner/external gate execution kit', ownerExternalGateKitReadme.includes('GSE Owner / External Gate Kit') && ownerExternalGateKitData?.pendingGateCount === releaseStatusData?.publicAcceptance?.pendingGates?.length && ownerExternalGateKitData?.generatedFresh?.finalAcceptancePacket === true && !ownerExternalGateKitRecordCommands.includes('record-public-release.mjs'), 'owner-external-gate-kit/'),
|
|
197
|
+
check('RB06f2', 'bundle owner/external gate kit includes dry-run preflight commands', ownerExternalGateKitRecordCommands.includes('Preflight command') && ownerExternalGateKitRecordCommands.includes('--dry-run --json'), 'owner-external-gate-kit/record-commands.md'),
|
|
198
|
+
check('RB06f3', 'bundle owner/external gate kit includes preflight command drill', ownerExternalGateKitVerificationCommands.includes('audit-public-acceptance-command-dry-run-drill.mjs'), 'owner-external-gate-kit/verification-commands.md'),
|
|
199
|
+
check('RB07', 'validator includes release bundle audit', validate.includes('audit-release-bundle.mjs'), 'scripts/validate-gse.mjs'),
|
|
200
|
+
check('RB08', 'packaging docs route release bundle command', packaging.includes('generate-release-bundle.mjs') && packaging.includes('audit-release-bundle.mjs'), 'references/packaging.md'),
|
|
201
|
+
]
|
|
202
|
+
|
|
203
|
+
const passed = checks.filter((item) => item.status === 'passed').length
|
|
204
|
+
const failed = checks.length - passed
|
|
205
|
+
const report = {
|
|
206
|
+
root,
|
|
207
|
+
generatedAt: new Date().toISOString(),
|
|
208
|
+
summary: { status: failed === 0 ? 'passed' : 'failed', passed, failed, total: checks.length },
|
|
209
|
+
workflows: { releaseBundle: failed === 0 ? 'verified' : 'failed' },
|
|
210
|
+
bundle: bundleData,
|
|
211
|
+
tempRoot,
|
|
212
|
+
limits: [
|
|
213
|
+
'This audit verifies release bundle generation and contents.',
|
|
214
|
+
'This audit writes to an isolated temporary directory and does not mutate the canonical release bundle path.',
|
|
215
|
+
'It does not publish a package, approve a marketplace listing, choose a license, or prove host-native slash-command support.',
|
|
216
|
+
],
|
|
217
|
+
checks,
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function renderMarkdown(data) {
|
|
221
|
+
const lines = []
|
|
222
|
+
lines.push('# GSE Release Bundle Audit')
|
|
223
|
+
lines.push('')
|
|
224
|
+
lines.push('Generated: ' + data.generatedAt)
|
|
225
|
+
lines.push('Root: ' + data.root)
|
|
226
|
+
lines.push('')
|
|
227
|
+
lines.push('## Summary')
|
|
228
|
+
lines.push('')
|
|
229
|
+
lines.push('- Status: ' + data.summary.status)
|
|
230
|
+
lines.push('- Checks: ' + data.summary.passed + '/' + data.summary.total)
|
|
231
|
+
lines.push('- Release bundle: ' + data.workflows.releaseBundle)
|
|
232
|
+
lines.push('')
|
|
233
|
+
lines.push('## Checks')
|
|
234
|
+
lines.push('')
|
|
235
|
+
for (const item of data.checks) {
|
|
236
|
+
const marker = item.status === 'passed' ? '[x]' : '[ ]'
|
|
237
|
+
lines.push('- ' + marker + ' ' + item.id + ' ' + item.label + ': ' + item.evidence)
|
|
238
|
+
}
|
|
239
|
+
lines.push('')
|
|
240
|
+
lines.push('## Limits')
|
|
241
|
+
lines.push('')
|
|
242
|
+
for (const item of data.limits) lines.push('- ' + item)
|
|
243
|
+
return lines.join('\n') + '\n'
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (jsonOnly) console.log(JSON.stringify(report, null, 2))
|
|
247
|
+
else console.log(renderMarkdown(report))
|
|
248
|
+
|
|
249
|
+
rmSync(tempRoot, { recursive: true, force: true })
|
|
250
|
+
|
|
251
|
+
if (failed > 0) process.exit(1)
|