@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,126 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { isPlaceholderEvidence, placeholderEvidenceError } from './lib/evidence-placeholders.mjs'
|
|
5
|
+
|
|
6
|
+
const args = process.argv.slice(2)
|
|
7
|
+
|
|
8
|
+
function readArg(name, fallback = null) {
|
|
9
|
+
const index = args.indexOf(name)
|
|
10
|
+
if (index === -1) return fallback
|
|
11
|
+
return args[index + 1] ?? fallback
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const root = path.resolve(readArg('--root', path.join(import.meta.dirname, '..')))
|
|
15
|
+
const jsonOnly = args.includes('--json')
|
|
16
|
+
|
|
17
|
+
function read(relativePath) {
|
|
18
|
+
const fullPath = path.join(root, relativePath)
|
|
19
|
+
return fs.existsSync(fullPath) ? fs.readFileSync(fullPath, 'utf8') : ''
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function exists(relativePath) {
|
|
23
|
+
return fs.existsSync(path.join(root, relativePath))
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function check(id, label, ok, evidence, risk = '') {
|
|
27
|
+
return { id, label, status: ok ? 'passed' : 'failed', evidence, risk }
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const placeholderCases = [
|
|
31
|
+
'__PLACEHOLDER__',
|
|
32
|
+
'<placeholder>',
|
|
33
|
+
'fixture-owner',
|
|
34
|
+
'todo',
|
|
35
|
+
'https://example.com/security',
|
|
36
|
+
'https://github.com/example/gse/actions/runs/123',
|
|
37
|
+
'https://registry.example/gse',
|
|
38
|
+
'http://localhost:3000/result',
|
|
39
|
+
'http://127.0.0.1/result',
|
|
40
|
+
'security@example.com',
|
|
41
|
+
'https://release.local/gse',
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
const realCases = [
|
|
45
|
+
'gse-maintainer',
|
|
46
|
+
'security@gse.dev',
|
|
47
|
+
'https://github.com/gse-org/gse/actions/runs/123',
|
|
48
|
+
'https://registry.npmjs.org/@t275005746/gse',
|
|
49
|
+
'https://marketplace.openai.com/gse',
|
|
50
|
+
'sha256:0123456789abcdef',
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
const recordScripts = [
|
|
54
|
+
'scripts/record-public-ci-run.mjs',
|
|
55
|
+
'scripts/record-public-repository-settings.mjs',
|
|
56
|
+
'scripts/record-public-security-contact.mjs',
|
|
57
|
+
'scripts/record-public-channel-publication.mjs',
|
|
58
|
+
]
|
|
59
|
+
|
|
60
|
+
const localFunctionRegex = /function\s+(isPlaceholderEvidence|rejectPlaceholderEvidence)\s*\(/
|
|
61
|
+
const importNeedle = "from './lib/evidence-placeholders.mjs'"
|
|
62
|
+
|
|
63
|
+
const helperSource = read('scripts/lib/evidence-placeholders.mjs')
|
|
64
|
+
const scriptSources = recordScripts.map((relativePath) => ({ relativePath, source: read(relativePath) }))
|
|
65
|
+
const duplicateDefinitions = scriptSources
|
|
66
|
+
.filter((item) => localFunctionRegex.test(item.source))
|
|
67
|
+
.map((item) => item.relativePath)
|
|
68
|
+
const missingImports = scriptSources
|
|
69
|
+
.filter((item) => !item.source.includes(importNeedle))
|
|
70
|
+
.map((item) => item.relativePath)
|
|
71
|
+
|
|
72
|
+
const checks = [
|
|
73
|
+
check('EPH01', 'shared placeholder helper exists', exists('scripts/lib/evidence-placeholders.mjs') && helperSource.includes('export function isPlaceholderEvidence'), 'scripts/lib/evidence-placeholders.mjs'),
|
|
74
|
+
check('EPH02', 'placeholder cases are rejected', placeholderCases.every((value) => isPlaceholderEvidence(value)), placeholderCases.map((value) => `${value}:${isPlaceholderEvidence(value)}`).join(', ')),
|
|
75
|
+
check('EPH03', 'real-looking public evidence cases are allowed', realCases.every((value) => !isPlaceholderEvidence(value)), realCases.map((value) => `${value}:${isPlaceholderEvidence(value)}`).join(', ')),
|
|
76
|
+
check('EPH04', 'placeholder error message stays operator-readable', placeholderEvidenceError('--evidence-url').includes('real public evidence') && placeholderEvidenceError('--evidence-url').includes('--evidence-url'), placeholderEvidenceError('--evidence-url')),
|
|
77
|
+
check('EPH05', 'public record scripts import shared helper', missingImports.length === 0, missingImports.length ? missingImports.join(', ') : recordScripts.join(', ')),
|
|
78
|
+
check('EPH06', 'public record scripts do not carry duplicate placeholder functions', duplicateDefinitions.length === 0, duplicateDefinitions.length ? duplicateDefinitions.join(', ') : 'no duplicate definitions'),
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
const passed = checks.filter((item) => item.status === 'passed').length
|
|
82
|
+
const failed = checks.length - passed
|
|
83
|
+
const report = {
|
|
84
|
+
root,
|
|
85
|
+
generatedAt: new Date().toISOString(),
|
|
86
|
+
summary: { status: failed === 0 ? 'passed' : 'failed', passed, failed, total: checks.length },
|
|
87
|
+
workflows: {
|
|
88
|
+
evidencePlaceholderHelper: failed === 0 ? 'verified' : 'failed',
|
|
89
|
+
},
|
|
90
|
+
limits: [
|
|
91
|
+
'This audit verifies local placeholder detection and script wiring.',
|
|
92
|
+
'It does not decide whether a real owner/external URL is valid or published.',
|
|
93
|
+
],
|
|
94
|
+
checks,
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function renderMarkdown(data) {
|
|
98
|
+
const lines = []
|
|
99
|
+
lines.push('# GSE Evidence Placeholder Audit')
|
|
100
|
+
lines.push('')
|
|
101
|
+
lines.push('Generated: ' + data.generatedAt)
|
|
102
|
+
lines.push('Root: ' + data.root)
|
|
103
|
+
lines.push('')
|
|
104
|
+
lines.push('## Summary')
|
|
105
|
+
lines.push('')
|
|
106
|
+
lines.push('- Status: ' + data.summary.status)
|
|
107
|
+
lines.push('- Checks: ' + data.summary.passed + '/' + data.summary.total)
|
|
108
|
+
lines.push('- Evidence placeholder helper: ' + data.workflows.evidencePlaceholderHelper)
|
|
109
|
+
lines.push('')
|
|
110
|
+
lines.push('## Checks')
|
|
111
|
+
lines.push('')
|
|
112
|
+
for (const item of data.checks) {
|
|
113
|
+
const marker = item.status === 'passed' ? '[x]' : '[ ]'
|
|
114
|
+
lines.push('- ' + marker + ' ' + item.id + ' ' + item.label + ': ' + item.evidence)
|
|
115
|
+
}
|
|
116
|
+
lines.push('')
|
|
117
|
+
lines.push('## Limits')
|
|
118
|
+
lines.push('')
|
|
119
|
+
for (const item of data.limits) lines.push('- ' + item)
|
|
120
|
+
return lines.join('\n') + '\n'
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (jsonOnly) console.log(JSON.stringify(report, null, 2))
|
|
124
|
+
else console.log(renderMarkdown(report))
|
|
125
|
+
|
|
126
|
+
if (failed > 0) process.exit(1)
|
|
@@ -0,0 +1,148 @@
|
|
|
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
|
+
|
|
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 jsonOnly = args.includes('--json')
|
|
18
|
+
|
|
19
|
+
function read(relativePath) {
|
|
20
|
+
const fullPath = path.join(root, relativePath)
|
|
21
|
+
return fs.existsSync(fullPath) ? fs.readFileSync(fullPath, 'utf8') : ''
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function exists(relativePath) {
|
|
25
|
+
return fs.existsSync(path.join(root, relativePath))
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function run(command, commandArgs) {
|
|
29
|
+
const result = spawnSync(command, commandArgs, {
|
|
30
|
+
cwd: root,
|
|
31
|
+
encoding: 'utf8',
|
|
32
|
+
windowsHide: true,
|
|
33
|
+
})
|
|
34
|
+
return {
|
|
35
|
+
status: result.status ?? 1,
|
|
36
|
+
stdout: result.stdout?.trim() ?? '',
|
|
37
|
+
stderr: result.stderr?.trim() ?? '',
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function check(id, label, ok, evidence, risk = '') {
|
|
42
|
+
return { id, label, status: ok ? 'passed' : 'failed', evidence, risk }
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const generator = read('scripts/generate-final-acceptance-packet.mjs')
|
|
46
|
+
const finalReadiness = read('references/final-readiness.md')
|
|
47
|
+
const validate = read('scripts/validate-gse.mjs')
|
|
48
|
+
const tmp = mkdtempSync(path.join(tmpdir(), 'gse-final-acceptance-'))
|
|
49
|
+
const out = path.join(tmp, 'final-acceptance-packet.md')
|
|
50
|
+
const generated = run(process.execPath, [path.join(root, 'scripts', 'generate-final-acceptance-packet.mjs'), '--root', root, '--out', out, '--force', '--json'])
|
|
51
|
+
let generatedJson = null
|
|
52
|
+
try {
|
|
53
|
+
generatedJson = JSON.parse(generated.stdout)
|
|
54
|
+
} catch {}
|
|
55
|
+
const packet = fs.existsSync(out) ? fs.readFileSync(out, 'utf8') : ''
|
|
56
|
+
rmSync(tmp, { recursive: true, force: true })
|
|
57
|
+
const publicAcceptance = run(process.execPath, [path.join(root, 'scripts', 'audit-public-acceptance-readiness.mjs'), '--root', root, '--json'])
|
|
58
|
+
let publicAcceptanceJson = null
|
|
59
|
+
try {
|
|
60
|
+
publicAcceptanceJson = JSON.parse(publicAcceptance.stdout)
|
|
61
|
+
} catch {}
|
|
62
|
+
|
|
63
|
+
const requiredSections = [
|
|
64
|
+
'# GSE Final Acceptance Packet',
|
|
65
|
+
'## Current Claim Boundary',
|
|
66
|
+
'## Verified Local Capabilities',
|
|
67
|
+
'## Pending Acceptance Gates',
|
|
68
|
+
'## Re-Verification Commands',
|
|
69
|
+
'## Anti-Overclaim Rules',
|
|
70
|
+
'## Next Action',
|
|
71
|
+
]
|
|
72
|
+
const pendingTerms = [
|
|
73
|
+
...(publicAcceptanceJson?.pendingGates ?? []).map((gate) => gate.area),
|
|
74
|
+
'audit-public-acceptance-readiness.mjs',
|
|
75
|
+
...(publicAcceptanceJson?.pendingGates ?? []).map((gate) => String(gate.recordCommand ?? '').match(/scripts\/([\w-]+\.mjs)/)?.[1]).filter(Boolean),
|
|
76
|
+
]
|
|
77
|
+
const antiOverclaimTerms = [
|
|
78
|
+
'Do not claim public release acceptance',
|
|
79
|
+
'Do not claim marketplace availability',
|
|
80
|
+
'Do not claim native slash-command support',
|
|
81
|
+
'Do not claim support for a host',
|
|
82
|
+
]
|
|
83
|
+
const packetUsesCompleteRecordCommandTemplates = !packet.includes('--invocation-status') &&
|
|
84
|
+
!/record-[a-z-]+\.mjs[\s\S]*\.\.\./.test(packet) &&
|
|
85
|
+
!/record-[a-z-]+\.mjs[^\n`]*[<>]/.test(packet) &&
|
|
86
|
+
packet.includes('--status accepted') &&
|
|
87
|
+
packet.includes('--dry-run --json')
|
|
88
|
+
|
|
89
|
+
const checks = [
|
|
90
|
+
check('FAP01', 'final acceptance packet generator exists', exists('scripts/generate-final-acceptance-packet.mjs'), 'scripts/generate-final-acceptance-packet.mjs'),
|
|
91
|
+
check('FAP02', 'generator is based on final readiness audit', generator.includes('audit-final-readiness.mjs') && generator.includes('Pending Acceptance Gates'), 'generator calls final readiness and renders pending gates'),
|
|
92
|
+
check('FAP03', 'generator produces a packet in a temporary output path', generated.status === 0 && generatedJson?.status === 'ready' && packet.length > 0, generated.stderr || out),
|
|
93
|
+
check('FAP04', 'packet contains required owner/external gate sections', requiredSections.every((term) => packet.includes(term)), requiredSections.join(', ')),
|
|
94
|
+
check('FAP05', 'packet enumerates all current pending final-form gates', pendingTerms.length > 1 && pendingTerms.every((term) => packet.includes(term)), pendingTerms.join(', ')),
|
|
95
|
+
check('FAP06', 'packet keeps anti-overclaim boundaries explicit', antiOverclaimTerms.every((term) => packet.includes(term)), antiOverclaimTerms.join(', ')),
|
|
96
|
+
check('FAP07', 'final readiness docs route users to the packet generator', finalReadiness.includes('generate-final-acceptance-packet.mjs'), 'references/final-readiness.md'),
|
|
97
|
+
check('FAP08', 'consolidated validator includes this audit', validate.includes('audit-final-acceptance-packet.mjs'), 'scripts/validate-gse.mjs'),
|
|
98
|
+
check('FAP09', 'packet uses complete record command templates', packetUsesCompleteRecordCommandTemplates, 'no ellipsis, no stale host invocation flag, includes preflight commands'),
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
const passed = checks.filter((item) => item.status === 'passed').length
|
|
102
|
+
const failed = checks.length - passed
|
|
103
|
+
const report = {
|
|
104
|
+
root,
|
|
105
|
+
generatedAt: new Date().toISOString(),
|
|
106
|
+
summary: { status: failed === 0 ? 'passed' : 'failed', passed, failed, total: checks.length },
|
|
107
|
+
workflows: {
|
|
108
|
+
finalAcceptancePacket: failed === 0 ? 'verified' : 'failed',
|
|
109
|
+
publicAccepted: generatedJson?.summary?.publicAccepted ?? 'unknown',
|
|
110
|
+
},
|
|
111
|
+
limits: [
|
|
112
|
+
'This audit verifies packet generation and claim boundaries only.',
|
|
113
|
+
'It does not choose a license, approve a security contact, publish a marketplace listing, or prove host-native slash commands.',
|
|
114
|
+
],
|
|
115
|
+
checks,
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function renderMarkdown(data) {
|
|
119
|
+
const lines = []
|
|
120
|
+
lines.push('# GSE Final Acceptance Packet Audit')
|
|
121
|
+
lines.push('')
|
|
122
|
+
lines.push('Generated: ' + data.generatedAt)
|
|
123
|
+
lines.push('Root: ' + data.root)
|
|
124
|
+
lines.push('')
|
|
125
|
+
lines.push('## Summary')
|
|
126
|
+
lines.push('')
|
|
127
|
+
lines.push('- Status: ' + data.summary.status)
|
|
128
|
+
lines.push('- Checks: ' + data.summary.passed + '/' + data.summary.total)
|
|
129
|
+
lines.push('- Final acceptance packet: ' + data.workflows.finalAcceptancePacket)
|
|
130
|
+
lines.push('- Public accepted: ' + data.workflows.publicAccepted)
|
|
131
|
+
lines.push('')
|
|
132
|
+
lines.push('## Checks')
|
|
133
|
+
lines.push('')
|
|
134
|
+
for (const item of data.checks) {
|
|
135
|
+
const marker = item.status === 'passed' ? '[x]' : '[ ]'
|
|
136
|
+
lines.push('- ' + marker + ' ' + item.id + ' ' + item.label + ': ' + item.evidence)
|
|
137
|
+
}
|
|
138
|
+
lines.push('')
|
|
139
|
+
lines.push('## Limits')
|
|
140
|
+
lines.push('')
|
|
141
|
+
for (const item of data.limits) lines.push('- ' + item)
|
|
142
|
+
return lines.join('\n') + '\n'
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (jsonOnly) console.log(JSON.stringify(report, null, 2))
|
|
146
|
+
else console.log(renderMarkdown(report))
|
|
147
|
+
|
|
148
|
+
if (failed > 0) process.exit(1)
|
|
@@ -0,0 +1,161 @@
|
|
|
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
|
+
|
|
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 jsonOnly = args.includes('--json')
|
|
18
|
+
|
|
19
|
+
function read(relativePath) {
|
|
20
|
+
const fullPath = path.join(root, relativePath)
|
|
21
|
+
return fs.existsSync(fullPath) ? fs.readFileSync(fullPath, 'utf8').replace(/^\uFEFF/, '') : ''
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function run(command, commandArgs) {
|
|
25
|
+
const result = spawnSync(command, commandArgs, {
|
|
26
|
+
cwd: root,
|
|
27
|
+
encoding: 'utf8',
|
|
28
|
+
windowsHide: true,
|
|
29
|
+
})
|
|
30
|
+
return {
|
|
31
|
+
status: result.status ?? 1,
|
|
32
|
+
stdout: (result.stdout ?? '').trim(),
|
|
33
|
+
stderr: (result.stderr ?? '').trim(),
|
|
34
|
+
command: [command, ...commandArgs].join(' '),
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function parseJson(text) {
|
|
39
|
+
try {
|
|
40
|
+
return JSON.parse(text)
|
|
41
|
+
} catch {
|
|
42
|
+
return null
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function check(id, label, ok, evidence, risk = '') {
|
|
47
|
+
return { id, label, status: ok ? 'passed' : 'failed', evidence, risk }
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const tmp = mkdtempSync(path.join(tmpdir(), 'gse-final-form-progress-'))
|
|
51
|
+
const out = path.join(tmp, 'final-form-progress-report.md')
|
|
52
|
+
const jsonOut = path.join(tmp, 'final-form-progress-report.json')
|
|
53
|
+
const generated = run(process.execPath, [
|
|
54
|
+
path.join(root, 'scripts', 'generate-final-form-progress-report.mjs'),
|
|
55
|
+
'--root', root,
|
|
56
|
+
'--out', out,
|
|
57
|
+
'--json-out', jsonOut,
|
|
58
|
+
'--force',
|
|
59
|
+
'--json',
|
|
60
|
+
])
|
|
61
|
+
const generatedData = parseJson(generated.stdout)
|
|
62
|
+
const report = fs.existsSync(jsonOut) ? parseJson(fs.readFileSync(jsonOut, 'utf8')) : null
|
|
63
|
+
const markdown = fs.existsSync(out) ? fs.readFileSync(out, 'utf8') : ''
|
|
64
|
+
rmSync(tmp, { recursive: true, force: true })
|
|
65
|
+
|
|
66
|
+
const skill = read('SKILL.md')
|
|
67
|
+
const validate = read('scripts/validate-gse.mjs')
|
|
68
|
+
const generator = read('scripts/generate-final-form-progress-report.mjs')
|
|
69
|
+
const canonicalMarkdown = read('.gse/acceptance/final-form-progress-report.md')
|
|
70
|
+
const canonicalReport = parseJson(read('.gse/acceptance/final-form-progress-report.json'))
|
|
71
|
+
|
|
72
|
+
const blockerNames = new Set((report?.blockers ?? []).map((item) => item.area))
|
|
73
|
+
const blockerSetMatchesReportCount = (report?.blockers?.length ?? -1) === report?.readiness?.pendingGateCount &&
|
|
74
|
+
(report?.blockers?.length ?? 0) > 0 &&
|
|
75
|
+
[...blockerNames].every(Boolean)
|
|
76
|
+
const verifiedNames = new Set((report?.verifiedCapabilities ?? []).map((item) => item.area))
|
|
77
|
+
const licenseDecisionResolved = verifiedNames.has('License decision')
|
|
78
|
+
const blockerCommandsAreCompleteTemplates = (report?.blockers?.length ?? 0) > 0 &&
|
|
79
|
+
report.blockers.every((blocker) =>
|
|
80
|
+
!String(blocker.recordCommand ?? '').includes('...') &&
|
|
81
|
+
!String(blocker.preflightCommand ?? '').includes('...') &&
|
|
82
|
+
!/[<>]/.test(String(blocker.recordCommand ?? '')) &&
|
|
83
|
+
!/[<>]/.test(String(blocker.preflightCommand ?? '')) &&
|
|
84
|
+
!String(blocker.recordCommand ?? '').includes('--invocation-status') &&
|
|
85
|
+
!String(blocker.preflightCommand ?? '').includes('--invocation-status') &&
|
|
86
|
+
(String(blocker.recordCommand ?? '').includes('record-host-invocation.mjs') ? String(blocker.recordCommand).includes('--status accepted') : true),
|
|
87
|
+
) &&
|
|
88
|
+
!markdown.includes('--invocation-status') &&
|
|
89
|
+
!/record-[a-z-]+\.mjs[^\n`]*[<>]/.test(markdown) &&
|
|
90
|
+
!/record-[a-z-]+\.mjs[\s\S]*\.\.\./.test(markdown)
|
|
91
|
+
|
|
92
|
+
const checks = [
|
|
93
|
+
check('FFP01', 'progress report generator exists and writes markdown/json', generated.status === 0 && generatedData?.status === 'written' && Boolean(report) && markdown.includes('# GSE Final-Form Progress Report'), generated.stderr || `${out}, ${jsonOut}`),
|
|
94
|
+
check('FFP02', 'report derives status from final readiness, public acceptance, and command dry-run drill audits', generator.includes('audit-final-readiness.mjs') && generator.includes('audit-public-acceptance-readiness.mjs') && generator.includes('audit-public-acceptance-command-dry-run-drill.mjs'), 'generator source audits'),
|
|
95
|
+
check('FFP03', 'report separates local engineering and full final-form readiness', report?.scores?.scoringBasis?.includes('local engineering excludes owner-required and external-required') && report?.scores?.localEngineeringReadiness === 100 && report?.scores?.fullFinalFormReadiness < 100 && report?.claimBoundary?.mayClaimPublicAcceptedFinalForm === false, `local=${report?.scores?.localEngineeringReadiness}; full=${report?.scores?.fullFinalFormReadiness}; publicAccepted=${report?.readiness?.publicAccepted}`),
|
|
96
|
+
check('FFP04', 'report lists current owner/external blockers', blockerSetMatchesReportCount && licenseDecisionResolved && !blockerNames.has('License decision'), [...blockerNames].join(', ') + '; License decision resolved'),
|
|
97
|
+
check('FFP05', 'report lists verified local capabilities', ['Local install', 'npm tarball install', 'URL install', 'Signing', 'Portable command execution', 'Host adapters'].every((name) => verifiedNames.has(name)), 'local engineering capabilities'),
|
|
98
|
+
check('FFP06', 'report preserves native slash-command boundary', report?.hostRuntime?.nativeSlashCommandRecords === 0 && report?.claimBoundary?.cannotClaim?.some((item) => item.includes('native slash-command')), `native=${report?.hostRuntime?.nativeSlashCommandRecords}`),
|
|
99
|
+
check('FFP06b', 'report does not list resolved license decision as a cannot-claim item', licenseDecisionResolved && !report?.claimBoundary?.cannotClaim?.some((item) => item.includes('license acceptance')), 'License decision verified; cannotClaim excludes license acceptance'),
|
|
100
|
+
check('FFP07', 'human report includes claim boundary and verification commands', markdown.includes('## Claim Boundary') && markdown.includes('## Verification Commands') && markdown.includes('May claim public accepted final form: false') && markdown.includes('audit-public-acceptance-command-dry-run-drill.mjs'), 'markdown sections'),
|
|
101
|
+
check('FFP07b', 'report exposes dry-run preflight commands for blockers', (report?.blockers?.length ?? 0) > 0 && report.blockers.every((blocker) => blocker.preflightCommand?.includes('--dry-run --json')) && markdown.includes('Preflight command'), 'blocker preflight commands'),
|
|
102
|
+
check('FFP07c', 'report uses complete record command templates', blockerCommandsAreCompleteTemplates, 'no ellipsis, no stale host invocation flag, host records use --status accepted'),
|
|
103
|
+
check('FFP07d', 'canonical progress report carries command dry-run drill evidence', canonicalMarkdown.includes('audit-public-acceptance-command-dry-run-drill.mjs') && canonicalReport?.commandEvidence?.publicAcceptanceCommandDryRunDrill?.command?.includes('audit-public-acceptance-command-dry-run-drill.mjs'), '.gse/acceptance/final-form-progress-report.md, .gse/acceptance/final-form-progress-report.json'),
|
|
104
|
+
check('FFP08', 'skill routes users to final-form progress report', skill.includes('generate-final-form-progress-report.mjs'), 'SKILL.md'),
|
|
105
|
+
check('FFP09', 'validator includes final-form progress report audit', validate.includes('audit-final-form-progress-report.mjs'), 'scripts/validate-gse.mjs'),
|
|
106
|
+
]
|
|
107
|
+
|
|
108
|
+
const passed = checks.filter((item) => item.status === 'passed').length
|
|
109
|
+
const failed = checks.length - passed
|
|
110
|
+
const audit = {
|
|
111
|
+
root,
|
|
112
|
+
generatedAt: new Date().toISOString(),
|
|
113
|
+
summary: { status: failed === 0 ? 'passed' : 'failed', passed, failed, total: checks.length },
|
|
114
|
+
workflows: {
|
|
115
|
+
finalFormProgressReport: failed === 0 ? 'verified' : 'failed',
|
|
116
|
+
localEngineeringReadiness: report?.scores?.localEngineeringReadiness ?? 'unknown',
|
|
117
|
+
fullFinalFormReadiness: report?.scores?.fullFinalFormReadiness ?? 'unknown',
|
|
118
|
+
pendingGates: report?.readiness?.pendingGateCount ?? 'unknown',
|
|
119
|
+
publicAccepted: report?.readiness?.publicAccepted ?? 'unknown',
|
|
120
|
+
},
|
|
121
|
+
limits: [
|
|
122
|
+
'This audit verifies honest progress reporting from local evidence.',
|
|
123
|
+
'It does not create owner decisions, public CI, public repository settings, registry publication, marketplace approval, or native host evidence.',
|
|
124
|
+
],
|
|
125
|
+
checks,
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function renderMarkdown(data) {
|
|
129
|
+
const lines = []
|
|
130
|
+
lines.push('# GSE Final-Form Progress Report Audit')
|
|
131
|
+
lines.push('')
|
|
132
|
+
lines.push('Generated: ' + data.generatedAt)
|
|
133
|
+
lines.push('Root: ' + data.root)
|
|
134
|
+
lines.push('')
|
|
135
|
+
lines.push('## Summary')
|
|
136
|
+
lines.push('')
|
|
137
|
+
lines.push('- Status: ' + data.summary.status)
|
|
138
|
+
lines.push('- Checks: ' + data.summary.passed + '/' + data.summary.total)
|
|
139
|
+
lines.push('- Final-form progress report: ' + data.workflows.finalFormProgressReport)
|
|
140
|
+
lines.push('- Local engineering readiness: ' + data.workflows.localEngineeringReadiness + '%')
|
|
141
|
+
lines.push('- Full final-form readiness: ' + data.workflows.fullFinalFormReadiness + '%')
|
|
142
|
+
lines.push('- Pending gates: ' + data.workflows.pendingGates)
|
|
143
|
+
lines.push('- Public accepted: ' + data.workflows.publicAccepted)
|
|
144
|
+
lines.push('')
|
|
145
|
+
lines.push('## Checks')
|
|
146
|
+
lines.push('')
|
|
147
|
+
for (const item of data.checks) {
|
|
148
|
+
const marker = item.status === 'passed' ? '[x]' : '[ ]'
|
|
149
|
+
lines.push('- ' + marker + ' ' + item.id + ' ' + item.label + ': ' + item.evidence)
|
|
150
|
+
}
|
|
151
|
+
lines.push('')
|
|
152
|
+
lines.push('## Limits')
|
|
153
|
+
lines.push('')
|
|
154
|
+
for (const item of data.limits) lines.push('- ' + item)
|
|
155
|
+
return lines.join('\n') + '\n'
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (jsonOnly) console.log(JSON.stringify(audit, null, 2))
|
|
159
|
+
else console.log(renderMarkdown(audit))
|
|
160
|
+
|
|
161
|
+
if (failed > 0) process.exit(1)
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { spawnSync } from 'node:child_process'
|
|
5
|
+
|
|
6
|
+
const args = process.argv.slice(2)
|
|
7
|
+
|
|
8
|
+
function readArg(name, fallback = null) {
|
|
9
|
+
const index = args.indexOf(name)
|
|
10
|
+
if (index === -1) return fallback
|
|
11
|
+
return args[index + 1] ?? fallback
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const root = path.resolve(readArg('--root', path.join(import.meta.dirname, '..')))
|
|
15
|
+
const jsonOnly = args.includes('--json')
|
|
16
|
+
|
|
17
|
+
function read(relativePath) {
|
|
18
|
+
const fullPath = path.join(root, relativePath)
|
|
19
|
+
return fs.existsSync(fullPath) ? fs.readFileSync(fullPath, 'utf8').replace(/^\uFEFF/, '') : ''
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function run(command, commandArgs) {
|
|
23
|
+
const result = spawnSync(command, commandArgs, {
|
|
24
|
+
cwd: root,
|
|
25
|
+
encoding: 'utf8',
|
|
26
|
+
windowsHide: true,
|
|
27
|
+
})
|
|
28
|
+
return {
|
|
29
|
+
status: result.status ?? 1,
|
|
30
|
+
stdout: (result.stdout ?? '').trim(),
|
|
31
|
+
stderr: (result.stderr ?? '').trim(),
|
|
32
|
+
command: [command, ...commandArgs].join(' '),
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function parseJson(text) {
|
|
37
|
+
try {
|
|
38
|
+
return JSON.parse(text)
|
|
39
|
+
} catch {
|
|
40
|
+
return null
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function check(id, label, ok, evidence, risk = '') {
|
|
45
|
+
return { id, label, status: ok ? 'passed' : 'failed', evidence, risk }
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const finalReadiness = run(process.execPath, [path.join(root, 'scripts', 'audit-final-readiness.mjs'), '--root', root, '--json'])
|
|
49
|
+
const publicAcceptance = run(process.execPath, [path.join(root, 'scripts', 'audit-public-acceptance-readiness.mjs'), '--root', root, '--json'])
|
|
50
|
+
const finalReadinessData = parseJson(finalReadiness.stdout)
|
|
51
|
+
const publicAcceptanceData = parseJson(publicAcceptance.stdout)
|
|
52
|
+
|
|
53
|
+
const matrix = finalReadinessData?.matrix ?? []
|
|
54
|
+
const licenseRow = matrix.find((row) => row.area === 'License decision')
|
|
55
|
+
const licenseVerified = licenseRow?.status === 'verified' || licenseRow?.status === 'accepted'
|
|
56
|
+
const pendingGates = publicAcceptanceData?.pendingGates ?? []
|
|
57
|
+
const pendingAreas = pendingGates.map((gate) => gate.area).sort()
|
|
58
|
+
const expectedPendingAreas = pendingAreas
|
|
59
|
+
|
|
60
|
+
const currentFiles = [
|
|
61
|
+
'.gse/gse-design-master-plan.md',
|
|
62
|
+
'.gse/goal-map.md',
|
|
63
|
+
'.gse/acceptance/final-acceptance-packet.md',
|
|
64
|
+
'.gse/acceptance/final-form-progress-report.md',
|
|
65
|
+
'.gse/acceptance/final-form-progress-report.json',
|
|
66
|
+
'.gse/acceptance/public-acceptance-handoff.md',
|
|
67
|
+
'.gse/acceptance/host-runtime-evidence-handoff.md',
|
|
68
|
+
'.gse/acceptance/release-status-manifest.json',
|
|
69
|
+
'.gse/acceptance/owner-external-gate-kit/README.md',
|
|
70
|
+
'.gse/acceptance/owner-external-gate-kit/final-acceptance-packet.md',
|
|
71
|
+
'.gse/acceptance/owner-external-gate-kit/public-acceptance-handoff.md',
|
|
72
|
+
'.gse/acceptance/owner-external-gate-kit/host-runtime-evidence-handoff.md',
|
|
73
|
+
'.gse/acceptance/owner-external-gate-kit/action-packet.md',
|
|
74
|
+
'.gse/acceptance/owner-external-gate-kit/release-status-manifest.json',
|
|
75
|
+
'.gse/acceptance/owner-external-gate-kit/kit-manifest.json',
|
|
76
|
+
'.gse/releases/public-release-owner-required.md',
|
|
77
|
+
'.gse/release-bundles/gse-release-bundle-audit/release-summary.md',
|
|
78
|
+
'.gse/release-bundles/gse-release-bundle-audit/public-release-record.md',
|
|
79
|
+
'.gse/release-bundles/gse-release-bundle-audit/public-acceptance-handoff.md',
|
|
80
|
+
'.gse/release-bundles/gse-release-bundle-audit/host-runtime-evidence-handoff.md',
|
|
81
|
+
'.gse/release-bundles/gse-release-bundle-audit/release-status-manifest.json',
|
|
82
|
+
'.gse/release-bundles/gse-release-bundle-audit/owner-external-gate-kit/README.md',
|
|
83
|
+
'.gse/release-bundles/gse-release-bundle-audit/owner-external-gate-kit/final-acceptance-packet.md',
|
|
84
|
+
'.gse/release-bundles/gse-release-bundle-audit/owner-external-gate-kit/public-acceptance-handoff.md',
|
|
85
|
+
'.gse/release-bundles/gse-release-bundle-audit/owner-external-gate-kit/host-runtime-evidence-handoff.md',
|
|
86
|
+
'.gse/release-bundles/gse-release-bundle-audit/owner-external-gate-kit/action-packet.md',
|
|
87
|
+
'.gse/release-bundles/gse-release-bundle-audit/owner-external-gate-kit/release-status-manifest.json',
|
|
88
|
+
'.gse/release-bundles/gse-release-bundle-audit/owner-external-gate-kit/kit-manifest.json',
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
const staleLicensePendingPhrases = [
|
|
92
|
+
'owner license selection remains required',
|
|
93
|
+
'Remaining final-form work is owner-selected license acceptance',
|
|
94
|
+
'Record owner-selected license acceptance',
|
|
95
|
+
'record owner-selected license acceptance',
|
|
96
|
+
'owner license decision before public release acceptance',
|
|
97
|
+
'owner license decision, accepted public security contact',
|
|
98
|
+
'owner-selected license acceptance, public security contact',
|
|
99
|
+
'owner-selected license acceptance or not-public decision',
|
|
100
|
+
'license decision and public security contact',
|
|
101
|
+
'Owner license decision and validation evidence required',
|
|
102
|
+
'owner-selected license acceptance unless accepted owner evidence exists',
|
|
103
|
+
]
|
|
104
|
+
|
|
105
|
+
function staleMatches() {
|
|
106
|
+
if (!licenseVerified) return []
|
|
107
|
+
const matches = []
|
|
108
|
+
for (const file of currentFiles) {
|
|
109
|
+
const content = read(file)
|
|
110
|
+
for (const phrase of staleLicensePendingPhrases) {
|
|
111
|
+
if (content.includes(phrase)) matches.push(`${file}: ${phrase}`)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return matches
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function localPathMatches() {
|
|
118
|
+
const patterns = [
|
|
119
|
+
/C:[\\/]+Users[\\/]+Admin/gi,
|
|
120
|
+
/C:\/Users\/Admin/gi,
|
|
121
|
+
]
|
|
122
|
+
const matches = []
|
|
123
|
+
for (const file of currentFiles) {
|
|
124
|
+
const content = read(file)
|
|
125
|
+
for (const pattern of patterns) {
|
|
126
|
+
if (pattern.test(content)) matches.push(file)
|
|
127
|
+
pattern.lastIndex = 0
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return [...new Set(matches)]
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function sameList(a, b) {
|
|
134
|
+
return a.length === b.length && a.every((item, index) => item === b[index])
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function jsonFile(relativePath) {
|
|
138
|
+
return parseJson(read(relativePath))
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const finalProgress = jsonFile('.gse/acceptance/final-form-progress-report.json')
|
|
142
|
+
const releaseStatus = jsonFile('.gse/acceptance/release-status-manifest.json')
|
|
143
|
+
const ownerKit = jsonFile('.gse/acceptance/owner-external-gate-kit/kit-manifest.json')
|
|
144
|
+
const bundleReleaseStatus = jsonFile('.gse/release-bundles/gse-release-bundle-audit/release-status-manifest.json')
|
|
145
|
+
const bundleOwnerKit = jsonFile('.gse/release-bundles/gse-release-bundle-audit/owner-external-gate-kit/kit-manifest.json')
|
|
146
|
+
|
|
147
|
+
const artifactPendingSets = [
|
|
148
|
+
['final progress', (finalProgress?.blockers ?? []).map((gate) => gate.area).sort()],
|
|
149
|
+
['release status', (releaseStatus?.publicAcceptance?.pendingGates ?? []).map((gate) => gate.area).sort()],
|
|
150
|
+
['owner kit', (ownerKit?.gates ?? []).map((gate) => gate.area).sort()],
|
|
151
|
+
['bundle release status', (bundleReleaseStatus?.publicAcceptance?.pendingGates ?? []).map((gate) => gate.area).sort()],
|
|
152
|
+
['bundle owner kit', (bundleOwnerKit?.gates ?? []).map((gate) => gate.area).sort()],
|
|
153
|
+
]
|
|
154
|
+
|
|
155
|
+
const publicReleaseRecord = read('.gse/releases/public-release-owner-required.md')
|
|
156
|
+
const finalProgressCannotClaim = finalProgress?.claimBoundary?.cannotClaim ?? []
|
|
157
|
+
const matches = staleMatches()
|
|
158
|
+
const pathMatches = localPathMatches()
|
|
159
|
+
|
|
160
|
+
const checks = [
|
|
161
|
+
check('FFSC01', 'live final readiness resolves the MIT license decision', finalReadiness.status === 0 && licenseVerified && publicReleaseRecord.includes('License status: selected') && publicReleaseRecord.includes('SPDX identifier: MIT') && publicReleaseRecord.includes('Evidence status: accepted'), 'audit-final-readiness.mjs, .gse/releases/public-release-owner-required.md'),
|
|
162
|
+
check('FFSC02', 'live public acceptance pending gates are current non-license gates', publicAcceptance.status === 0 && pendingAreas.length > 0 && sameList(pendingAreas, expectedPendingAreas) && !pendingAreas.includes('License decision'), pendingAreas.join(', ')),
|
|
163
|
+
check('FFSC03', 'current control and handoff docs do not describe resolved license decision as pending', matches.length === 0, matches.length ? matches.join('; ') : currentFiles.join(', ')),
|
|
164
|
+
check('FFSC04', 'canonical and bundled artifacts expose the same current pending gates', artifactPendingSets.every(([, areas]) => sameList(areas, expectedPendingAreas)), artifactPendingSets.map(([name, areas]) => `${name}: ${areas.join(', ')}`).join(' | ')),
|
|
165
|
+
check('FFSC05', 'public release record accepted license wording is not contradictory', publicReleaseRecord.includes('Owner license decision accepted') && !publicReleaseRecord.includes('Owner license decision and validation evidence required'), '.gse/releases/public-release-owner-required.md'),
|
|
166
|
+
check('FFSC06', 'final-form progress claim boundary excludes resolved license acceptance', finalProgress?.verifiedCapabilities?.some((item) => item.area === 'License decision' && item.status === 'verified') && !finalProgressCannotClaim.some((item) => item.includes('license acceptance')), '.gse/acceptance/final-form-progress-report.json'),
|
|
167
|
+
check('FFSC07', 'current public handoff artifacts do not expose local Windows user paths', pathMatches.length === 0, pathMatches.length ? pathMatches.join(', ') : currentFiles.join(', ')),
|
|
168
|
+
]
|
|
169
|
+
|
|
170
|
+
const passed = checks.filter((item) => item.status === 'passed').length
|
|
171
|
+
const failed = checks.length - passed
|
|
172
|
+
const report = {
|
|
173
|
+
root,
|
|
174
|
+
generatedAt: new Date().toISOString(),
|
|
175
|
+
summary: { status: failed === 0 ? 'passed' : 'failed', passed, failed, total: checks.length },
|
|
176
|
+
workflows: {
|
|
177
|
+
finalFormStaleCopy: failed === 0 ? 'verified' : 'failed',
|
|
178
|
+
licenseDecision: licenseVerified ? 'verified' : licenseRow?.status ?? 'unknown',
|
|
179
|
+
pendingGates: pendingAreas.length,
|
|
180
|
+
publicAccepted: publicAcceptanceData?.summary?.publicAccepted ?? 'unknown',
|
|
181
|
+
},
|
|
182
|
+
limits: [
|
|
183
|
+
'This audit checks current control docs and generated handoff artifacts for stale final-form status copy.',
|
|
184
|
+
'It does not scan historical evidence logs, because older evidence may truthfully describe earlier states.',
|
|
185
|
+
'It does not create public security contact, public repository, CI, registry, marketplace, or host-runtime evidence.',
|
|
186
|
+
],
|
|
187
|
+
checks,
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function renderMarkdown(data) {
|
|
191
|
+
const lines = []
|
|
192
|
+
lines.push('# GSE Final-Form Stale Copy Audit')
|
|
193
|
+
lines.push('')
|
|
194
|
+
lines.push('Generated: ' + data.generatedAt)
|
|
195
|
+
lines.push('Root: ' + data.root)
|
|
196
|
+
lines.push('')
|
|
197
|
+
lines.push('## Summary')
|
|
198
|
+
lines.push('')
|
|
199
|
+
lines.push('- Status: ' + data.summary.status)
|
|
200
|
+
lines.push('- Checks: ' + data.summary.passed + '/' + data.summary.total)
|
|
201
|
+
lines.push('- License decision: ' + data.workflows.licenseDecision)
|
|
202
|
+
lines.push('- Pending gates: ' + data.workflows.pendingGates)
|
|
203
|
+
lines.push('- Public accepted: ' + data.workflows.publicAccepted)
|
|
204
|
+
lines.push('')
|
|
205
|
+
lines.push('## Checks')
|
|
206
|
+
lines.push('')
|
|
207
|
+
for (const item of data.checks) {
|
|
208
|
+
const marker = item.status === 'passed' ? '[x]' : '[ ]'
|
|
209
|
+
lines.push('- ' + marker + ' ' + item.id + ' ' + item.label + ': ' + item.evidence)
|
|
210
|
+
}
|
|
211
|
+
lines.push('')
|
|
212
|
+
lines.push('## Limits')
|
|
213
|
+
lines.push('')
|
|
214
|
+
for (const item of data.limits) lines.push('- ' + item)
|
|
215
|
+
return lines.join('\n') + '\n'
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (jsonOnly) console.log(JSON.stringify(report, null, 2))
|
|
219
|
+
else console.log(renderMarkdown(report))
|
|
220
|
+
|
|
221
|
+
if (failed > 0) process.exit(1)
|