@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,218 @@
|
|
|
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 exists(relativePath) {
|
|
20
|
+
return fs.existsSync(path.join(root, relativePath))
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function read(relativePath) {
|
|
24
|
+
const fullPath = path.join(root, relativePath)
|
|
25
|
+
return fs.existsSync(fullPath) ? fs.readFileSync(fullPath, 'utf8') : ''
|
|
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
|
+
command: [command, ...commandArgs].join(' '),
|
|
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
|
+
const tmp = mkdtempSync(path.join(tmpdir(), 'gse-owner-external-gate-kit-'))
|
|
55
|
+
const out = path.join(tmp, 'kit')
|
|
56
|
+
const generated = exists('scripts/generate-owner-external-gate-kit.mjs')
|
|
57
|
+
? run(process.execPath, [path.join(root, 'scripts', 'generate-owner-external-gate-kit.mjs'), '--root', root, '--out', out, '--force', '--json'])
|
|
58
|
+
: null
|
|
59
|
+
const generatedData = generated ? parseJson(generated.stdout) : null
|
|
60
|
+
|
|
61
|
+
function readKit(fileName) {
|
|
62
|
+
const fullPath = path.join(out, fileName)
|
|
63
|
+
return fs.existsSync(fullPath) ? fs.readFileSync(fullPath, 'utf8') : ''
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function readCanonicalKit(fileName) {
|
|
67
|
+
return read(path.join('.gse', 'acceptance', 'owner-external-gate-kit', fileName))
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function stableGateSnapshot(data) {
|
|
71
|
+
return (data?.gates ?? []).map((gate) => ({
|
|
72
|
+
stage: gate.stage,
|
|
73
|
+
area: gate.area,
|
|
74
|
+
status: gate.status,
|
|
75
|
+
owner: gate.owner,
|
|
76
|
+
requiredEvidence: gate.requiredEvidence,
|
|
77
|
+
recordCommand: gate.recordCommand,
|
|
78
|
+
preflightCommand: gate.preflightCommand,
|
|
79
|
+
}))
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function sameJson(a, b) {
|
|
83
|
+
return JSON.stringify(a) === JSON.stringify(b)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const readme = readKit('README.md')
|
|
87
|
+
const actionPacket = readKit('action-packet.md')
|
|
88
|
+
const finalPacket = readKit('final-acceptance-packet.md')
|
|
89
|
+
const publicHandoff = readKit('public-acceptance-handoff.md')
|
|
90
|
+
const hostHandoff = readKit('host-runtime-evidence-handoff.md')
|
|
91
|
+
const releaseStatusManifest = readKit('release-status-manifest.json')
|
|
92
|
+
const releaseOwnerActionPlan = readKit('release-owner-action-plan.md')
|
|
93
|
+
const commands = readKit('record-commands.md')
|
|
94
|
+
const verificationCommands = readKit('verification-commands.md')
|
|
95
|
+
const manifestText = readKit('kit-manifest.json')
|
|
96
|
+
const manifest = parseJson(manifestText)
|
|
97
|
+
const canonicalReadme = readCanonicalKit('README.md')
|
|
98
|
+
const canonicalActionPacket = readCanonicalKit('action-packet.md')
|
|
99
|
+
const canonicalCommands = readCanonicalKit('record-commands.md')
|
|
100
|
+
const canonicalVerificationCommands = readCanonicalKit('verification-commands.md')
|
|
101
|
+
const canonicalManifestText = readCanonicalKit('kit-manifest.json')
|
|
102
|
+
const canonicalManifest = parseJson(canonicalManifestText)
|
|
103
|
+
const requiredKitFiles = [
|
|
104
|
+
'README.md',
|
|
105
|
+
'action-packet.md',
|
|
106
|
+
'final-acceptance-packet.md',
|
|
107
|
+
'public-acceptance-handoff.md',
|
|
108
|
+
'host-runtime-evidence-handoff.md',
|
|
109
|
+
'release-status-manifest.json',
|
|
110
|
+
'release-owner-action-plan.md',
|
|
111
|
+
'record-commands.md',
|
|
112
|
+
'verification-commands.md',
|
|
113
|
+
'kit-manifest.json',
|
|
114
|
+
]
|
|
115
|
+
const requiredKitFilesPresent = requiredKitFiles.every((item) => readKit(item).length > 0)
|
|
116
|
+
const canonicalRequiredKitFilesPresent = requiredKitFiles.every((item) => readCanonicalKit(item).length > 0)
|
|
117
|
+
rmSync(tmp, { recursive: true, force: true })
|
|
118
|
+
|
|
119
|
+
const skill = read('SKILL.md')
|
|
120
|
+
const validate = read('scripts/validate-gse.mjs')
|
|
121
|
+
const bundleGenerator = read('scripts/generate-release-bundle.mjs')
|
|
122
|
+
const bundleAudit = read('scripts/audit-release-bundle.mjs')
|
|
123
|
+
const expectedAreas = (manifest?.gates ?? []).map((gate) => gate.area)
|
|
124
|
+
const expectedScripts = [...new Set((manifest?.gates ?? [])
|
|
125
|
+
.map((gate) => String(gate.recordCommand ?? '').match(/node scripts\/([^ ]+)/)?.[1] ?? '')
|
|
126
|
+
.filter(Boolean))]
|
|
127
|
+
const kitRecordTemplatesAreComplete = !commands.includes('--invocation-status') &&
|
|
128
|
+
!finalPacket.includes('--invocation-status') &&
|
|
129
|
+
!publicHandoff.includes('--invocation-status') &&
|
|
130
|
+
!releaseOwnerActionPlan.includes('--invocation-status') &&
|
|
131
|
+
!hostHandoff.includes('--invocation-status') &&
|
|
132
|
+
!/record-[a-z-]+\.mjs[\s\S]*\.\.\./.test(commands) &&
|
|
133
|
+
!/record-[a-z-]+\.mjs[\s\S]*\.\.\./.test(finalPacket) &&
|
|
134
|
+
!/record-[a-z-]+\.mjs[\s\S]*\.\.\./.test(publicHandoff) &&
|
|
135
|
+
!/record-[a-z-]+\.mjs[\s\S]*\.\.\./.test(releaseOwnerActionPlan) &&
|
|
136
|
+
!/record-[a-z-]+\.mjs[^\n`]*[<>]/.test(commands) &&
|
|
137
|
+
!/record-[a-z-]+\.mjs[^\n`]*[<>]/.test(finalPacket) &&
|
|
138
|
+
!/record-[a-z-]+\.mjs[^\n`]*[<>]/.test(publicHandoff) &&
|
|
139
|
+
!/record-[a-z-]+\.mjs[^\n`]*[<>]/.test(releaseOwnerActionPlan) &&
|
|
140
|
+
!/record-[a-z-]+\.mjs[^\n`]*[<>]/.test(hostHandoff) &&
|
|
141
|
+
commands.includes('--status accepted')
|
|
142
|
+
const kitVerificationCommandsAreShellSafe = !/(audit-[a-z-]+|validate-gse|generate-release-status-manifest)\.mjs[^\n`]*[<>]/.test(verificationCommands) &&
|
|
143
|
+
!/(audit-[a-z-]+|validate-gse)\.mjs[^\n`]*[<>]/.test(releaseStatusManifest) &&
|
|
144
|
+
!/(audit-[a-z-]+|validate-gse|generate-release-status-manifest)\.mjs[^\n`]*[<>]/.test(releaseOwnerActionPlan) &&
|
|
145
|
+
verificationCommands.includes('__GSE__')
|
|
146
|
+
|
|
147
|
+
const checks = [
|
|
148
|
+
check('OEG01', 'owner/external gate kit generator exists', exists('scripts/generate-owner-external-gate-kit.mjs'), 'scripts/generate-owner-external-gate-kit.mjs'),
|
|
149
|
+
check('OEG02', 'generator produces the kit directory', generated?.status === 0 && generatedData?.status === 'written', generated?.stderr || out),
|
|
150
|
+
check('OEG03', 'kit includes required files', requiredKitFilesPresent, requiredKitFiles.join(', ')),
|
|
151
|
+
check('OEG04', 'machine-readable pending gates cover current final owner/external gates', expectedAreas.length > 0 && expectedAreas.every((area) => manifest?.gates?.some((gate) => gate.area === area)) && !manifest?.gates?.some((gate) => gate.area === 'License decision'), expectedAreas.join(', ') + '; License decision resolved'),
|
|
152
|
+
check('OEG05', 'record commands map to real scripts', expectedScripts.length > 0 && expectedScripts.every((script) => commands.includes(script)) && !commands.includes('proves-public-registry-publication') && (!commands.includes('record-public-channel-publication.mjs') || commands.includes('--proves-registry-publication true') || commands.includes('--proves-marketplace-approval true')), expectedScripts.join(', ')),
|
|
153
|
+
check('OEG05b', 'record commands include dry-run preflight commands for accepted evidence', commands.includes('Preflight command') && commands.includes('--dry-run --json'), 'record-commands.md'),
|
|
154
|
+
check('OEG05c', 'kit uses complete record command templates', kitRecordTemplatesAreComplete, 'no ellipsis, no stale host invocation flag, host records use --status accepted'),
|
|
155
|
+
check('OEG06', 'kit preserves anti-overclaim boundaries', readme.includes('does not choose a license') && readme.includes('Do not claim native slash-command support') && verificationCommands.includes('Local fixture drills, pointer adapters, and generated handoff files do not count as external acceptance'), 'README.md, verification-commands.md'),
|
|
156
|
+
check('OEG07', 'kit verification commands include portable probe, final readiness, preflight drill, and close gate', verificationCommands.includes('run-gse-command.mjs') && verificationCommands.includes('/gse probe') && !verificationCommands.includes('node scripts/probe-public-external-gates.mjs') && verificationCommands.includes('audit-final-readiness.mjs') && verificationCommands.includes('audit-public-acceptance-readiness.mjs') && verificationCommands.includes('audit-public-acceptance-command-dry-run-drill.mjs') && verificationCommands.includes('audit-close-gate.mjs'), 'verification-commands.md'),
|
|
157
|
+
check('OEG08', 'kit contains fresh final acceptance, public handoff, host handoff, manifest, and owner action plan', finalPacket.includes('GSE Final Acceptance Packet') && publicHandoff.includes('GSE Public Acceptance Handoff') && hostHandoff.includes('GSE Host Runtime Evidence Handoff') && releaseStatusManifest.includes('"publicAccepted": "not-accepted"') && releaseOwnerActionPlan.includes('GSE Release Owner Action Plan'), 'generated kit artifacts'),
|
|
158
|
+
check('OEG09', 'kit manifest marks generated handoff artifacts as fresh', manifest?.generatedFresh?.finalAcceptancePacket === true && manifest?.generatedFresh?.publicAcceptanceHandoff === true && manifest?.generatedFresh?.hostRuntimeEvidenceHandoff === true && manifest?.generatedFresh?.releaseStatusManifest === true && manifest?.generatedFresh?.releaseOwnerActionPlan === true, 'kit-manifest.json'),
|
|
159
|
+
check('OEG09b', 'kit manifest carries preflight commands for every pending gate', (manifest?.gates?.length ?? 0) > 0 && manifest.gates.every((gate) => gate.preflightCommand?.includes('--dry-run --json')), 'kit-manifest.json'),
|
|
160
|
+
check('OEG10', 'skill routes users to the owner/external gate kit', skill.includes('generate-owner-external-gate-kit.mjs') && skill.includes('audit-owner-external-gate-kit.mjs'), 'SKILL.md'),
|
|
161
|
+
check('OEG11', 'validator includes owner/external gate kit audit', validate.includes('audit-owner-external-gate-kit.mjs'), 'scripts/validate-gse.mjs'),
|
|
162
|
+
check('OEG12', 'release bundle includes owner/external gate kit', bundleGenerator.includes('generate-owner-external-gate-kit.mjs') && bundleAudit.includes('owner-external-gate-kit'), 'release bundle generator/audit'),
|
|
163
|
+
check('OEG13', 'kit verification commands use shell-safe placeholders', kitVerificationCommandsAreShellSafe, 'verification commands use __GSE__ instead of <gse>'),
|
|
164
|
+
check('OEG14', 'canonical owner/external gate kit exists', canonicalRequiredKitFilesPresent, '.gse/acceptance/owner-external-gate-kit/'),
|
|
165
|
+
check('OEG15', 'canonical owner/external gate kit matches current generated gate snapshot', canonicalManifest?.publicAccepted === manifest?.publicAccepted && canonicalManifest?.pendingGateCount === manifest?.pendingGateCount && sameJson(stableGateSnapshot(canonicalManifest), stableGateSnapshot(manifest)), '.gse/acceptance/owner-external-gate-kit/kit-manifest.json'),
|
|
166
|
+
check('OEG16', 'canonical owner/external gate kit preserves current handoff commands and boundaries', canonicalReadme.includes('GSE Owner / External Gate Kit') && canonicalActionPacket.includes('Public accepted: ' + (manifest?.publicAccepted ?? 'unknown')) && canonicalCommands.includes('--dry-run --json') && canonicalCommands.includes('--status accepted') && canonicalVerificationCommands.includes('run-gse-command.mjs') && canonicalVerificationCommands.includes('/gse probe') && !canonicalVerificationCommands.includes('node scripts/probe-public-external-gates.mjs') && canonicalVerificationCommands.includes('audit-public-acceptance-command-dry-run-drill.mjs') && canonicalVerificationCommands.includes('__GSE__'), '.gse/acceptance/owner-external-gate-kit/'),
|
|
167
|
+
]
|
|
168
|
+
|
|
169
|
+
const passed = checks.filter((item) => item.status === 'passed').length
|
|
170
|
+
const failed = checks.length - passed
|
|
171
|
+
const report = {
|
|
172
|
+
root,
|
|
173
|
+
generatedAt: new Date().toISOString(),
|
|
174
|
+
summary: { status: failed === 0 ? 'passed' : 'failed', passed, failed, total: checks.length },
|
|
175
|
+
workflows: {
|
|
176
|
+
ownerExternalGateKit: failed === 0 ? 'verified' : 'failed',
|
|
177
|
+
pendingGates: manifest?.pendingGateCount ?? null,
|
|
178
|
+
publicAccepted: manifest?.publicAccepted ?? 'unknown',
|
|
179
|
+
},
|
|
180
|
+
limits: [
|
|
181
|
+
'This audit verifies owner/external gate kit generation and claim boundaries only.',
|
|
182
|
+
'It does not choose a license, publish a package, configure a repository, run public CI, approve a marketplace listing, or prove host-native slash commands.',
|
|
183
|
+
],
|
|
184
|
+
checks,
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function renderMarkdown(data) {
|
|
188
|
+
const lines = []
|
|
189
|
+
lines.push('# GSE Owner / External Gate Kit Audit')
|
|
190
|
+
lines.push('')
|
|
191
|
+
lines.push('Generated: ' + data.generatedAt)
|
|
192
|
+
lines.push('Root: ' + data.root)
|
|
193
|
+
lines.push('')
|
|
194
|
+
lines.push('## Summary')
|
|
195
|
+
lines.push('')
|
|
196
|
+
lines.push('- Status: ' + data.summary.status)
|
|
197
|
+
lines.push('- Checks: ' + data.summary.passed + '/' + data.summary.total)
|
|
198
|
+
lines.push('- Owner/external gate kit: ' + data.workflows.ownerExternalGateKit)
|
|
199
|
+
lines.push('- Pending gates: ' + data.workflows.pendingGates)
|
|
200
|
+
lines.push('- Public accepted: ' + data.workflows.publicAccepted)
|
|
201
|
+
lines.push('')
|
|
202
|
+
lines.push('## Checks')
|
|
203
|
+
lines.push('')
|
|
204
|
+
for (const item of data.checks) {
|
|
205
|
+
const marker = item.status === 'passed' ? '[x]' : '[ ]'
|
|
206
|
+
lines.push('- ' + marker + ' ' + item.id + ' ' + item.label + ': ' + item.evidence)
|
|
207
|
+
}
|
|
208
|
+
lines.push('')
|
|
209
|
+
lines.push('## Limits')
|
|
210
|
+
lines.push('')
|
|
211
|
+
for (const item of data.limits) lines.push('- ' + item)
|
|
212
|
+
return lines.join('\n') + '\n'
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if (jsonOnly) console.log(JSON.stringify(report, null, 2))
|
|
216
|
+
else console.log(renderMarkdown(report))
|
|
217
|
+
|
|
218
|
+
if (failed > 0) process.exit(1)
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import os from 'node:os'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
import { spawnSync } from 'node:child_process'
|
|
6
|
+
|
|
7
|
+
const args = process.argv.slice(2)
|
|
8
|
+
|
|
9
|
+
function readArg(name, fallback = null) {
|
|
10
|
+
const index = args.indexOf(name)
|
|
11
|
+
if (index === -1) return fallback
|
|
12
|
+
return args[index + 1] ?? fallback
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const root = path.resolve(readArg('--root', path.join(import.meta.dirname, '..')))
|
|
16
|
+
const jsonOnly = args.includes('--json')
|
|
17
|
+
const keepTemp = args.includes('--keep-temp')
|
|
18
|
+
const tempRootArg = readArg('--temp-root', null)
|
|
19
|
+
const baseTemp = tempRootArg ? path.resolve(tempRootArg) : fs.mkdtempSync(path.join(os.tmpdir(), 'gse-project-audit-'))
|
|
20
|
+
const initScript = path.join(root, 'scripts', 'init-project.mjs')
|
|
21
|
+
|
|
22
|
+
const commonFiles = [
|
|
23
|
+
'.gse/README.md',
|
|
24
|
+
'.gse/state.json',
|
|
25
|
+
'.gse/project-profile.md',
|
|
26
|
+
'.gse/goal-map.md',
|
|
27
|
+
'.gse/quality-gates.md',
|
|
28
|
+
'.gse/tooling.md',
|
|
29
|
+
'.gse/learnings.md',
|
|
30
|
+
'.gse/evidence/index.jsonl',
|
|
31
|
+
'.gse/goals/README.md',
|
|
32
|
+
'.gse/templates/change-brief.md',
|
|
33
|
+
'.gse/templates/spec.md',
|
|
34
|
+
'.gse/templates/design.md',
|
|
35
|
+
'.gse/templates/tasks.md',
|
|
36
|
+
'.gse/templates/evidence.md',
|
|
37
|
+
'.gse/templates/review.md',
|
|
38
|
+
'.gse/templates/execution-quality-pack.md',
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
const standardFiles = [
|
|
42
|
+
'.gse/agent-workspace.md',
|
|
43
|
+
'.gse/agents/roles.md',
|
|
44
|
+
'.gse/agents/dispatch.md',
|
|
45
|
+
'.gse/skills/README.md',
|
|
46
|
+
'.gse/lsp/README.md',
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
const enterpriseFiles = [
|
|
50
|
+
'.gse/hooks/README.md',
|
|
51
|
+
'.gse/mcp/README.md',
|
|
52
|
+
'.gse/plugins/README.md',
|
|
53
|
+
'.gse/release.md',
|
|
54
|
+
'.gse/incident-review.md',
|
|
55
|
+
'.gse/audit.md',
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
const modeExpectations = {
|
|
59
|
+
lite: {
|
|
60
|
+
files: commonFiles,
|
|
61
|
+
dirs: ['.gse/changes', '.gse/evidence', '.gse/templates', '.gse/goals'],
|
|
62
|
+
},
|
|
63
|
+
standard: {
|
|
64
|
+
files: [...commonFiles, ...standardFiles],
|
|
65
|
+
dirs: ['.gse/changes', '.gse/evidence', '.gse/templates', '.gse/goals', '.gse/agents', '.gse/skills', '.gse/lsp'],
|
|
66
|
+
},
|
|
67
|
+
enterprise: {
|
|
68
|
+
files: [...commonFiles, ...standardFiles, ...enterpriseFiles],
|
|
69
|
+
dirs: [
|
|
70
|
+
'.gse/changes',
|
|
71
|
+
'.gse/evidence',
|
|
72
|
+
'.gse/templates',
|
|
73
|
+
'.gse/goals',
|
|
74
|
+
'.gse/agents',
|
|
75
|
+
'.gse/skills',
|
|
76
|
+
'.gse/lsp',
|
|
77
|
+
'.gse/hooks',
|
|
78
|
+
'.gse/mcp',
|
|
79
|
+
'.gse/plugins',
|
|
80
|
+
],
|
|
81
|
+
},
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const autoExpectations = [
|
|
85
|
+
{
|
|
86
|
+
name: 'empty-project',
|
|
87
|
+
expectedMode: 'lite',
|
|
88
|
+
setup(target) {
|
|
89
|
+
fs.mkdirSync(target, { recursive: true })
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: 'standard-app',
|
|
94
|
+
expectedMode: 'standard',
|
|
95
|
+
setup(target) {
|
|
96
|
+
fs.mkdirSync(target, { recursive: true })
|
|
97
|
+
fs.writeFileSync(path.join(target, 'README.md'), '# Standard App\n', 'utf8')
|
|
98
|
+
fs.writeFileSync(
|
|
99
|
+
path.join(target, 'package.json'),
|
|
100
|
+
JSON.stringify({ scripts: { test: 'vitest run', build: 'vite build' }, devDependencies: { typescript: '^5.0.0', vitest: '^2.0.0' } }, null, 2),
|
|
101
|
+
'utf8',
|
|
102
|
+
)
|
|
103
|
+
fs.writeFileSync(path.join(target, 'tsconfig.json'), '{}\n', 'utf8')
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
name: 'enterprise-agent-runtime',
|
|
108
|
+
expectedMode: 'enterprise',
|
|
109
|
+
setup(target) {
|
|
110
|
+
fs.mkdirSync(path.join(target, '.claude'), { recursive: true })
|
|
111
|
+
fs.mkdirSync(path.join(target, '.codex'), { recursive: true })
|
|
112
|
+
fs.writeFileSync(path.join(target, '.mcp.json'), '{}\n', 'utf8')
|
|
113
|
+
fs.writeFileSync(
|
|
114
|
+
path.join(target, 'package.json'),
|
|
115
|
+
JSON.stringify({ scripts: { test: 'vitest run', release: 'node release.mjs' } }, null, 2),
|
|
116
|
+
'utf8',
|
|
117
|
+
)
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
]
|
|
121
|
+
|
|
122
|
+
function runNode(script, runArgs) {
|
|
123
|
+
return spawnSync(process.execPath, [script, ...runArgs], {
|
|
124
|
+
cwd: root,
|
|
125
|
+
encoding: 'utf8',
|
|
126
|
+
windowsHide: true,
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function readJsonFromRun(run) {
|
|
131
|
+
if (run.status !== 0) return null
|
|
132
|
+
try {
|
|
133
|
+
return JSON.parse(run.stdout)
|
|
134
|
+
} catch {
|
|
135
|
+
return null
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function fileStatus(target, relativePath) {
|
|
140
|
+
const fullPath = path.join(target, relativePath)
|
|
141
|
+
if (!fs.existsSync(fullPath)) return 'missing'
|
|
142
|
+
return fs.statSync(fullPath).isFile() ? 'present' : 'not-file'
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function dirStatus(target, relativePath) {
|
|
146
|
+
const fullPath = path.join(target, relativePath)
|
|
147
|
+
if (!fs.existsSync(fullPath)) return 'missing'
|
|
148
|
+
return fs.statSync(fullPath).isDirectory() ? 'present' : 'not-dir'
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function collectSignatures(target, relativePaths) {
|
|
152
|
+
const signatures = new Map()
|
|
153
|
+
for (const relativePath of relativePaths) {
|
|
154
|
+
const fullPath = path.join(target, relativePath)
|
|
155
|
+
if (!fs.existsSync(fullPath) || !fs.statSync(fullPath).isFile()) continue
|
|
156
|
+
const stat = fs.statSync(fullPath)
|
|
157
|
+
const text = fs.readFileSync(fullPath, 'utf8')
|
|
158
|
+
signatures.set(relativePath, { size: stat.size, text })
|
|
159
|
+
}
|
|
160
|
+
return signatures
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function compareSignatures(before, after) {
|
|
164
|
+
const changed = []
|
|
165
|
+
for (const [relativePath, signature] of before.entries()) {
|
|
166
|
+
const next = after.get(relativePath)
|
|
167
|
+
if (!next || next.size !== signature.size || next.text !== signature.text) changed.push(relativePath)
|
|
168
|
+
}
|
|
169
|
+
return changed
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function auditMode(mode, tempRoot) {
|
|
173
|
+
const target = path.join(tempRoot, mode)
|
|
174
|
+
fs.mkdirSync(target, { recursive: true })
|
|
175
|
+
const expectation = modeExpectations[mode]
|
|
176
|
+
const firstRun = runNode(initScript, ['--target', target, '--mode', mode])
|
|
177
|
+
const firstJson = readJsonFromRun(firstRun)
|
|
178
|
+
const fileChecks = expectation.files.map((relativePath) => ({ relativePath, status: fileStatus(target, relativePath) }))
|
|
179
|
+
const dirChecks = expectation.dirs.map((relativePath) => ({ relativePath, status: dirStatus(target, relativePath) }))
|
|
180
|
+
const before = collectSignatures(target, expectation.files)
|
|
181
|
+
const secondRun = runNode(initScript, ['--target', target, '--mode', mode])
|
|
182
|
+
const secondJson = readJsonFromRun(secondRun)
|
|
183
|
+
const after = collectSignatures(target, expectation.files)
|
|
184
|
+
const changedOnRerun = compareSignatures(before, after)
|
|
185
|
+
const writtenCount = firstJson?.results?.filter((item) => item.status === 'written').length ?? 0
|
|
186
|
+
const skippedCount = secondJson?.results?.filter((item) => item.status === 'skipped').length ?? 0
|
|
187
|
+
const expectedWrites = expectation.files.length
|
|
188
|
+
const ok =
|
|
189
|
+
firstRun.status === 0 &&
|
|
190
|
+
secondRun.status === 0 &&
|
|
191
|
+
firstJson &&
|
|
192
|
+
secondJson &&
|
|
193
|
+
fileChecks.every((item) => item.status === 'present') &&
|
|
194
|
+
dirChecks.every((item) => item.status === 'present') &&
|
|
195
|
+
writtenCount === expectedWrites &&
|
|
196
|
+
skippedCount === expectedWrites &&
|
|
197
|
+
changedOnRerun.length === 0
|
|
198
|
+
return {
|
|
199
|
+
mode,
|
|
200
|
+
target,
|
|
201
|
+
status: ok ? 'passed' : 'failed',
|
|
202
|
+
firstRun: { status: firstRun.status, stderr: firstRun.stderr.trim(), writtenCount, expectedWrites },
|
|
203
|
+
secondRun: { status: secondRun.status, stderr: secondRun.stderr.trim(), skippedCount, expectedSkips: expectedWrites },
|
|
204
|
+
fileChecks,
|
|
205
|
+
dirChecks,
|
|
206
|
+
changedOnRerun,
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function auditAuto(item, tempRoot) {
|
|
211
|
+
const target = path.join(tempRoot, 'auto-' + item.name)
|
|
212
|
+
fs.rmSync(target, { recursive: true, force: true })
|
|
213
|
+
item.setup(target)
|
|
214
|
+
const run = runNode(initScript, ['--target', target, '--mode', 'auto'])
|
|
215
|
+
const model = readJsonFromRun(run)
|
|
216
|
+
const expectation = modeExpectations[item.expectedMode]
|
|
217
|
+
const fileChecks = expectation.files.map((relativePath) => ({ relativePath, status: fileStatus(target, relativePath) }))
|
|
218
|
+
const dirChecks = expectation.dirs.map((relativePath) => ({ relativePath, status: dirStatus(target, relativePath) }))
|
|
219
|
+
const ok =
|
|
220
|
+
run.status === 0 &&
|
|
221
|
+
model?.requestedMode === 'auto' &&
|
|
222
|
+
model?.mode === item.expectedMode &&
|
|
223
|
+
Array.isArray(model?.selectionReasons) &&
|
|
224
|
+
model.selectionReasons.length > 0 &&
|
|
225
|
+
fileChecks.every((check) => check.status === 'present') &&
|
|
226
|
+
dirChecks.every((check) => check.status === 'present') &&
|
|
227
|
+
(item.expectedMode !== 'enterprise' || (fileStatus(target, '.codex/gse-adapter.md') === 'present' && fileStatus(target, '.claude/gse-adapter.md') === 'present'))
|
|
228
|
+
return {
|
|
229
|
+
name: item.name,
|
|
230
|
+
target,
|
|
231
|
+
expectedMode: item.expectedMode,
|
|
232
|
+
detectedMode: model?.mode ?? null,
|
|
233
|
+
selectionReasons: model?.selectionReasons ?? [],
|
|
234
|
+
status: ok ? 'passed' : 'failed',
|
|
235
|
+
run: { status: run.status, stderr: run.stderr.trim() },
|
|
236
|
+
fileChecks,
|
|
237
|
+
dirChecks,
|
|
238
|
+
hostAdapterChecks:
|
|
239
|
+
item.expectedMode === 'enterprise'
|
|
240
|
+
? [
|
|
241
|
+
{ relativePath: '.codex/gse-adapter.md', status: fileStatus(target, '.codex/gse-adapter.md') },
|
|
242
|
+
{ relativePath: '.claude/gse-adapter.md', status: fileStatus(target, '.claude/gse-adapter.md') },
|
|
243
|
+
]
|
|
244
|
+
: [],
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function renderMarkdown(data) {
|
|
249
|
+
const lines = []
|
|
250
|
+
lines.push('# GSE Project Audit')
|
|
251
|
+
lines.push('')
|
|
252
|
+
lines.push('Generated: ' + data.generatedAt)
|
|
253
|
+
lines.push('Root: ' + data.root)
|
|
254
|
+
lines.push('Temp root: ' + data.tempRoot)
|
|
255
|
+
lines.push('')
|
|
256
|
+
lines.push('## Summary')
|
|
257
|
+
lines.push('')
|
|
258
|
+
lines.push('- Status: ' + data.summary.status)
|
|
259
|
+
lines.push('- Modes: ' + data.summary.passed + '/' + data.summary.total)
|
|
260
|
+
lines.push('- Bootstrap scaffold: ' + data.workflows.bootstrapScaffold)
|
|
261
|
+
lines.push('- Rerun safety: ' + data.workflows.rerunSafety)
|
|
262
|
+
lines.push('- Auto mode selection: ' + data.workflows.autoModeSelection)
|
|
263
|
+
lines.push('')
|
|
264
|
+
lines.push('## Modes')
|
|
265
|
+
lines.push('')
|
|
266
|
+
for (const item of data.modes) {
|
|
267
|
+
const marker = item.status === 'passed' ? '[x]' : '[ ]'
|
|
268
|
+
lines.push('- ' + marker + ' ' + item.mode + ': files ' + item.fileChecks.filter((check) => check.status === 'present').length + '/' + item.fileChecks.length + ', dirs ' + item.dirChecks.filter((check) => check.status === 'present').length + '/' + item.dirChecks.length + ', skipped on rerun ' + item.secondRun.skippedCount + '/' + item.secondRun.expectedSkips)
|
|
269
|
+
if (item.changedOnRerun.length) lines.push(' - Changed on rerun: ' + item.changedOnRerun.join(', '))
|
|
270
|
+
}
|
|
271
|
+
lines.push('')
|
|
272
|
+
lines.push('## Auto Mode')
|
|
273
|
+
lines.push('')
|
|
274
|
+
for (const item of data.autoModes) {
|
|
275
|
+
const marker = item.status === 'passed' ? '[x]' : '[ ]'
|
|
276
|
+
const hostAdapters = item.hostAdapterChecks?.length ? ', host adapters ' + item.hostAdapterChecks.filter((check) => check.status === 'present').length + '/' + item.hostAdapterChecks.length : ''
|
|
277
|
+
lines.push('- ' + marker + ' ' + item.name + ': expected ' + item.expectedMode + ', detected ' + item.detectedMode + ', reasons ' + item.selectionReasons.join(', ') + hostAdapters)
|
|
278
|
+
}
|
|
279
|
+
lines.push('')
|
|
280
|
+
lines.push('## Limits')
|
|
281
|
+
lines.push('')
|
|
282
|
+
for (const item of data.limits) lines.push('- ' + item)
|
|
283
|
+
return lines.join('\n') + '\n'
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (!fs.existsSync(initScript)) {
|
|
287
|
+
console.error('Missing init-project.mjs at ' + initScript)
|
|
288
|
+
process.exit(1)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
fs.rmSync(baseTemp, { recursive: true, force: true })
|
|
292
|
+
fs.mkdirSync(baseTemp, { recursive: true })
|
|
293
|
+
|
|
294
|
+
const modes = Object.keys(modeExpectations).map((mode) => auditMode(mode, baseTemp))
|
|
295
|
+
const autoModes = autoExpectations.map((item) => auditAuto(item, baseTemp))
|
|
296
|
+
const passed = modes.filter((item) => item.status === 'passed').length
|
|
297
|
+
const autoPassed = autoModes.filter((item) => item.status === 'passed').length
|
|
298
|
+
const failed = modes.length - passed + (autoModes.length - autoPassed)
|
|
299
|
+
const report = {
|
|
300
|
+
root,
|
|
301
|
+
generatedAt: new Date().toISOString(),
|
|
302
|
+
tempRoot: baseTemp,
|
|
303
|
+
summary: { passed: passed + autoPassed, failed, total: modes.length + autoModes.length, status: failed === 0 ? 'passed' : 'failed' },
|
|
304
|
+
workflows: {
|
|
305
|
+
bootstrapScaffold: modes.every((item) => item.fileChecks.every((check) => check.status === 'present') && item.dirChecks.every((check) => check.status === 'present')) ? 'verified' : 'failed',
|
|
306
|
+
rerunSafety: modes.every((item) => item.changedOnRerun.length === 0 && item.secondRun.skippedCount === item.secondRun.expectedSkips) ? 'verified' : 'failed',
|
|
307
|
+
autoModeSelection: autoModes.every((item) => item.status === 'passed') ? 'verified' : 'failed',
|
|
308
|
+
},
|
|
309
|
+
limits: [
|
|
310
|
+
'Project audit uses temporary directories and generated scaffolds, not arbitrary real repositories.',
|
|
311
|
+
'Project audit verifies init-project scaffold and rerun safety, not tool installation, CI, package install, or fresh-session acceptance.',
|
|
312
|
+
'Use discover-project-profile and focused project checks separately for real existing repo adoption.',
|
|
313
|
+
],
|
|
314
|
+
modes,
|
|
315
|
+
autoModes,
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (!keepTemp) fs.rmSync(baseTemp, { recursive: true, force: true })
|
|
319
|
+
|
|
320
|
+
if (jsonOnly) console.log(JSON.stringify(report, null, 2))
|
|
321
|
+
else console.log(renderMarkdown(report))
|
|
322
|
+
|
|
323
|
+
if (failed > 0) process.exit(1)
|