@t275005746/gse 0.1.1 → 0.2.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.
Files changed (86) hide show
  1. package/.gse/releases/public-github-release-v0.1.2.md +49 -0
  2. package/.gse/releases/public-registry-publication-npm.md +49 -49
  3. package/.gse/state.json +148 -26
  4. package/CHANGELOG.md +52 -8
  5. package/README.md +13 -6
  6. package/README.zh-CN.md +13 -6
  7. package/SKILL.md +32 -12
  8. package/assets/templates/dispatch-packet.md +22 -14
  9. package/assets/templates/evidence.md +11 -5
  10. package/assets/templates/role-fallback-packet.md +49 -0
  11. package/package.json +18 -10
  12. package/references/agent-roles.md +41 -20
  13. package/references/commands.md +75 -45
  14. package/references/drift-audit.md +2 -1
  15. package/references/evidence-taxonomy.md +46 -9
  16. package/references/file-ownership.md +17 -13
  17. package/references/final-form-roadmap.md +203 -0
  18. package/references/final-readiness.md +3 -3
  19. package/references/host-adapters.md +38 -10
  20. package/references/learning-system.md +24 -8
  21. package/references/maintenance-cadence.md +51 -0
  22. package/references/project-guards.md +52 -0
  23. package/references/quality-gates.md +18 -11
  24. package/references/role-dispatch-fallback.md +54 -0
  25. package/references/tool-adapters.md +13 -3
  26. package/scripts/audit-close-gate-hardening.mjs +188 -0
  27. package/scripts/audit-close-gate.mjs +219 -33
  28. package/scripts/audit-command-execution.mjs +30 -17
  29. package/scripts/audit-commands.mjs +7 -5
  30. package/scripts/audit-completion-plan-drill.mjs +271 -0
  31. package/scripts/audit-completion-readiness.mjs +9 -6
  32. package/scripts/audit-continue-preflight.mjs +318 -0
  33. package/scripts/audit-evidence-levels.mjs +217 -0
  34. package/scripts/audit-evidence-review-queue.mjs +206 -0
  35. package/scripts/audit-final-acceptance-packet.mjs +9 -9
  36. package/scripts/audit-final-form-progress-report.mjs +19 -18
  37. package/scripts/audit-final-form-roadmap.mjs +157 -0
  38. package/scripts/audit-final-form-stale-copy.mjs +2 -2
  39. package/scripts/audit-final-readiness.mjs +3 -3
  40. package/scripts/audit-fresh-session-readiness.mjs +1 -1
  41. package/scripts/audit-host-capabilities.mjs +237 -0
  42. package/scripts/audit-installed-sync.mjs +203 -0
  43. package/scripts/audit-learning-drift.mjs +292 -0
  44. package/scripts/audit-learning-promotion.mjs +351 -0
  45. package/scripts/audit-learning-system.mjs +24 -14
  46. package/scripts/audit-local-final-form-completion.mjs +11 -10
  47. package/scripts/audit-maintenance-cadence.mjs +139 -0
  48. package/scripts/audit-maintenance-snapshot.mjs +181 -0
  49. package/scripts/audit-npm-package-metadata.mjs +8 -2
  50. package/scripts/audit-npm-publish-dry-run.mjs +10 -4
  51. package/scripts/audit-npm-tarball-install.mjs +9 -3
  52. package/scripts/audit-owner-external-gate-kit.mjs +12 -11
  53. package/scripts/audit-project-guards.mjs +189 -0
  54. package/scripts/audit-project.mjs +14 -11
  55. package/scripts/audit-public-acceptance-handoff.mjs +10 -10
  56. package/scripts/audit-public-acceptance-readiness.mjs +6 -6
  57. package/scripts/audit-public-release-checklist.mjs +17 -15
  58. package/scripts/audit-release-bundle.mjs +13 -11
  59. package/scripts/audit-release-owner-action-plan-drill.mjs +5 -4
  60. package/scripts/audit-release-owner-action-plan.mjs +10 -10
  61. package/scripts/audit-release-status-manifest.mjs +4 -4
  62. package/scripts/audit-roadmap-consistency.mjs +11 -7
  63. package/scripts/audit-role-dispatch-fallback.mjs +212 -0
  64. package/scripts/audit-session-sync.mjs +127 -0
  65. package/scripts/audit-state-freshness.mjs +6 -5
  66. package/scripts/audit-state-repair.mjs +360 -0
  67. package/scripts/audit-target-hardening-drills.mjs +518 -0
  68. package/scripts/audit-tool-fallback-policy.mjs +180 -0
  69. package/scripts/audit-ui-browser-evidence-policy.mjs +191 -0
  70. package/scripts/audit-validation-profiles.mjs +6 -4
  71. package/scripts/backfill-evidence-levels.mjs +98 -0
  72. package/scripts/check-encoding.mjs +72 -0
  73. package/scripts/generate-continue-packet.mjs +1487 -0
  74. package/scripts/generate-final-acceptance-packet.mjs +23 -8
  75. package/scripts/generate-final-form-progress-report.mjs +25 -23
  76. package/scripts/generate-host-runtime-evidence-handoff.mjs +23 -16
  77. package/scripts/generate-maintenance-snapshot.mjs +216 -0
  78. package/scripts/generate-public-acceptance-handoff.mjs +26 -14
  79. package/scripts/generate-release-owner-action-plan.mjs +4 -3
  80. package/scripts/generate-release-status-manifest.mjs +4 -3
  81. package/scripts/init-project.mjs +144 -63
  82. package/scripts/record-learning.mjs +37 -8
  83. package/scripts/record-session-sync.mjs +87 -0
  84. package/scripts/run-gse-command.mjs +79 -47
  85. package/scripts/run-validation-profile.mjs +24 -5
  86. package/scripts/validate-gse.mjs +262 -9
@@ -101,8 +101,8 @@ const gateGuidance = {
101
101
  const finalReadiness = run(process.execPath, [path.join(root, 'scripts', 'audit-final-readiness.mjs'), '--root', root, '--json'])
102
102
  const finalReadinessData = parseJson(finalReadiness.stdout)
103
103
  const matrix = finalReadinessData?.matrix ?? []
104
- const pendingGates = matrix
105
- .filter((row) => row.status !== 'verified')
104
+ const pendingGates = matrix
105
+ .filter((row) => row.status === 'owner-required' || row.status === 'external-required')
106
106
  .map((row) => {
107
107
  const rawCommand = gateGuidance[row.area]?.command ?? 'No command mapping exists yet.'
108
108
  const recordCommand = shellSafePlaceholders(rawCommand)
@@ -133,9 +133,9 @@ const pendingCommandsAreExecutableTemplates = pendingGates.every((gate) =>
133
133
  (gate.recordCommand.includes('record-host-invocation.mjs') ? gate.recordCommand.includes('--status accepted') : true),
134
134
  )
135
135
  const expectedGatesCoveredByMatrix = expectedFinalGates.every((gate) => matrixGateNames.has(gate))
136
- const ownerExternalGatesRemainHonest = matrix.every((row) => row.status === 'verified' || row.status === 'owner-required' || row.status === 'external-required')
136
+ const ownerExternalGatesRemainHonest = matrix.every((row) => row.status === 'verified' || row.status === 'owner-required' || row.status === 'external-required' || row.status === 'not-claimed')
137
137
  const publicAccepted = finalReadinessData?.workflows?.publicAccepted ?? 'unknown'
138
- const claimBoundaryHonest = publicAccepted === 'verified' ? pendingGates.length === 0 : pendingGates.length > 0
138
+ const claimBoundaryHonest = publicAccepted === 'verified' ? pendingGates.length === 0 : pendingGates.length > 0
139
139
 
140
140
  const checks = [
141
141
  check('PAD01', 'final readiness audit runs before public acceptance diagnosis', finalReadiness.status === 0 && finalReadinessData?.summary?.status === 'passed', finalReadiness.command, finalReadiness.stderr),
@@ -144,7 +144,7 @@ const checks = [
144
144
  check('PAD03b', 'pending final gates expose dry-run preflight commands', currentPendingRowsHavePreflightCommands, pendingGates.map((gate) => `${gate.area} -> ${gate.preflightCommand}`).join('; ')),
145
145
  check('PAD03c', 'pending final gate commands are complete executable templates', pendingCommandsAreExecutableTemplates, pendingGates.map((gate) => `${gate.area} -> ${gate.recordCommand}`).join('; ')),
146
146
  check('PAD04', 'pending final gates keep owner/external responsibility visible', pendingGates.every((gate) => gate.owner !== 'unknown'), pendingGates.map((gate) => `${gate.area}:${gate.owner}`).join(', ')),
147
- check('PAD05', 'final readiness statuses remain honest until accepted records promote them', ownerExternalGatesRemainHonest && claimBoundaryHonest, `publicAccepted=${publicAccepted}; pending=${pendingGates.length}`),
147
+ check('PAD05', 'final readiness statuses remain honest when required gates are accepted and optional claims are not-claimed', ownerExternalGatesRemainHonest && claimBoundaryHonest, `publicAccepted=${publicAccepted}; pending=${pendingGates.length}`),
148
148
  check('PAD06', 'doctor does not claim external acceptance from local mechanics', publicAccepted !== 'verified' || pendingGates.length === 0, 'public acceptance follows final-readiness matrix only'),
149
149
  ]
150
150
 
@@ -170,7 +170,7 @@ const report = {
170
170
  nextPreflightCommands: pendingGates.map((gate) => gate.preflightCommand),
171
171
  limits: [
172
172
  'This doctor identifies missing owner/external evidence and record commands.',
173
- 'It does not choose a license, publish a package, configure a public repository, run public CI, approve a marketplace listing, or prove host-native slash commands.',
173
+ 'It does not choose a license, publish a package, configure a public repository, run public CI, approve a marketplace listing, or prove optional host-native slash commands.',
174
174
  'A gate is accepted only after a real accepted record is created and final readiness is re-audited.',
175
175
  ],
176
176
  checks,
@@ -66,8 +66,9 @@ const checklistData = checklistRun ? parseJson(checklistRun.stdout) : null
66
66
  const canonicalChecklist = read('.gse/acceptance/public-release-checklist.md')
67
67
  rmSync(tmp, { recursive: true, force: true })
68
68
 
69
- const pendingGates = manifest?.publicAcceptance?.pendingGates ?? []
70
- const expectedAreas = pendingGates.map((gate) => gate.area)
69
+ const pendingGates = manifest?.publicAcceptance?.pendingGates ?? []
70
+ const hasPendingGates = pendingGates.length > 0
71
+ const expectedAreas = pendingGates.map((gate) => gate.area)
71
72
  const requiredPhases = [
72
73
  '01. Prepare the release bundle',
73
74
  '02. Publish and configure the public repository',
@@ -82,24 +83,25 @@ const expectedScripts = [...new Set(pendingGates
82
83
  .map((gate) => String(gate.recordCommand ?? '').match(/scripts\/([\w-]+\.mjs)/)?.[1])
83
84
  .filter(Boolean))]
84
85
  const noLocalPaths = !/[A-Z]:\\|C:\\Users\\|D:\\codex\\/i.test(checklist) && !/[A-Z]:\\|C:\\Users\\|D:\\codex\\/i.test(canonicalChecklist)
85
- const noFalseAcceptance = checklist.includes('Public accepted: not-accepted') &&
86
- checklist.includes('does not publish, approve, or accept') &&
87
- checklist.includes('A gate is complete only after real accepted evidence')
88
- const commandCoverage = expectedScripts.every((script) => checklist.includes(script)) &&
89
- checklist.includes('/gse release') &&
90
- checklist.includes('/gse release --execute --out <bundle>') &&
91
- checklist.includes('/gse probe')
92
- const canonicalFresh = canonicalChecklist.includes('GSE Public Release Checklist') &&
93
- requiredPhases.every((phase) => canonicalChecklist.includes(phase)) &&
94
- expectedAreas.every((area) => canonicalChecklist.includes(`Gate: ${area}`)) &&
95
- canonicalChecklist.includes(`Pending owner/external gates: ${pendingGates.length}`)
86
+ const publicAcceptedStatus = manifest?.publicAcceptance?.publicAccepted ?? 'unknown'
87
+ const noFalseAcceptance = checklist.includes('Public accepted: ' + publicAcceptedStatus) &&
88
+ checklist.includes('does not publish, approve, or accept') &&
89
+ checklist.includes('A gate is complete only after real accepted evidence')
90
+ const commandCoverage = (hasPendingGates ? expectedScripts.every((script) => checklist.includes(script)) : true) &&
91
+ checklist.includes('/gse release') &&
92
+ checklist.includes('/gse release --execute --out <bundle>') &&
93
+ checklist.includes('/gse probe')
94
+ const canonicalFresh = canonicalChecklist.includes('GSE Public Release Checklist') &&
95
+ requiredPhases.every((phase) => canonicalChecklist.includes(phase)) &&
96
+ (hasPendingGates ? expectedAreas.every((area) => canonicalChecklist.includes(`Gate: ${area}`)) : canonicalChecklist.includes('Public accepted: verified')) &&
97
+ canonicalChecklist.includes(`Pending owner/external gates: ${pendingGates.length}`)
96
98
 
97
99
  const checks = [
98
100
  check('PRC01', 'public release checklist generator exists', exists('scripts/generate-public-release-checklist.mjs'), 'scripts/generate-public-release-checklist.mjs'),
99
101
  check('PRC02', 'generator writes checklist from release status manifest', checklistRun?.status === 0 && checklistData?.status === 'written' && checklist.includes('GSE Public Release Checklist'), 'generate-public-release-checklist.mjs'),
100
102
  check('PRC03', 'checklist uses ordered public release runway phases', requiredPhases.every((phase) => checklist.includes(phase)), 'public-release-checklist.md'),
101
- check('PRC04', 'checklist covers every pending owner/external gate', expectedAreas.length > 0 && expectedAreas.every((area) => checklist.includes(`Gate: ${area}`)), 'release-status-manifest pending gates'),
102
- check('PRC05', 'checklist includes executable preflight and record command families', commandCoverage && checklist.includes('Preflight:') && checklist.includes('Record accepted evidence:'), 'public-release-checklist.md'),
103
+ check('PRC04', 'checklist covers every pending owner/external gate', hasPendingGates ? expectedAreas.every((area) => checklist.includes(`Gate: ${area}`)) : checklist.includes('Pending owner/external gates: 0'), 'release-status-manifest pending gates'),
104
+ check('PRC05', 'checklist includes executable preflight and record command families', commandCoverage && (hasPendingGates ? (checklist.includes('Preflight:') && checklist.includes('Record accepted evidence:')) : checklist.includes('Final Verification')), 'public-release-checklist.md'),
103
105
  check('PRC06', 'checklist preserves public acceptance boundary', noFalseAcceptance, 'public-release-checklist.md'),
104
106
  check('PRC07', 'checklist avoids local machine paths', noLocalPaths, 'public-release-checklist.md'),
105
107
  check('PRC08', 'canonical checklist is fresh against current manifest', canonicalFresh, '.gse/acceptance/public-release-checklist.md'),
@@ -87,9 +87,11 @@ const installablePackageManifest = readBundle('installable-package/gse-package-m
87
87
  const provenance = readBundle('provenance.json')
88
88
  const checksums = readBundle('checksums.sha256')
89
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')
90
+ const pendingGates = releaseStatusData?.publicAcceptance?.pendingGates ?? []
91
+ const pendingAreas = pendingGates.map((gate) => gate.area)
92
+ const hasPendingGates = pendingGates.length > 0
93
+ const publicAcceptedStatus = releaseStatusData?.publicAcceptance?.publicAccepted ?? releaseStatusData?.claimBoundary?.publicAccepted ?? 'unknown'
94
+ const hasPendingRegistryPublication = pendingAreas.includes('Public registry publication')
93
95
  const bundleManifestData = parseJson(manifest)
94
96
  const ownerExternalGateKitData = parseJson(ownerExternalGateKitManifest)
95
97
  const installablePackageManifestData = parseJson(installablePackageManifest)
@@ -171,30 +173,30 @@ const checks = [
171
173
  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
174
  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
175
  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'),
176
+ check('RB06', 'bundle manifest records validation and limits', manifest.includes('"validation"') && manifest.includes('Bundle does not choose a license') && bundleManifestData?.publicReleaseAcceptance === publicAcceptedStatus && bundleManifestData?.licenseDecision === 'accepted' && manifest.includes('"publicAcceptanceHandoff": "included"') && manifest.includes('"hostRuntimeEvidenceHandoff": "included"') && manifest.includes('"releaseStatusManifest": "included"') && manifest.includes('"releaseOwnerActionPlan": "included"'), 'bundle-manifest.json'),
175
177
  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
178
  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
179
  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
180
  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
181
  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'),
182
+ check('RB06v6', 'bundle provenance records local generation boundaries', provenanceData?.generator === 'scripts/generate-release-bundle.mjs' && provenanceData?.installablePackage?.packageDigest === installablePackageManifestData?.integrity?.packageDigest && provenanceData?.publicAcceptance?.status === publicAcceptedStatus && Array.isArray(provenanceData?.claimBoundaries) && provenanceData.claimBoundaries.some((item) => item.includes('not a registry attestation')), 'provenance.json'),
181
183
  check('RB06v7', 'bundle checksums verify installable package files', verifyChecksumFile(), 'checksums.sha256'),
182
184
  check('RB06v8', 'bundle provenance and checksums do not expose local paths', !hasLocalPathLeak(provenance) && !hasLocalPathLeak(checksums), 'provenance.json, checksums.sha256'),
183
185
  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'),
186
+ check('RB06b', 'bundle includes public acceptance handoff boundaries', handoff.includes('GSE Public Acceptance Handoff') && handoff.includes('Public accepted: ' + publicAcceptedStatus) && handoff.includes('Do not claim public release acceptance'), 'public-acceptance-handoff.md'),
185
187
  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'),
188
+ check('RB06b3', 'bundle public acceptance handoff includes dry-run preflight commands', hasPendingGates ? (handoff.includes('Preflight command') && handoff.includes('--dry-run --json')) : handoff.includes('No owner/external acceptance gate is pending'), 'public-acceptance-handoff.md'),
187
189
  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'),
190
+ check('RB06d', 'bundle includes machine-readable release status manifest', releaseStatusData?.claimBoundary?.publicAccepted === publicAcceptedStatus && 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
191
  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
192
  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
193
  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'),
194
+ check('RB06e2', 'bundle owner action plan includes dry-run preflight commands and drill', (hasPendingGates ? (releaseOwnerActionPlan.includes('Preflight command') && releaseOwnerActionPlan.includes('--dry-run --json')) : releaseOwnerActionPlan.includes('No pending owner or external gates were reported by the manifest.')) && releaseOwnerActionPlan.includes('audit-public-acceptance-command-dry-run-drill.mjs'), 'release-owner-action-plan.md'),
193
195
  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'),
196
+ 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: ' + publicAcceptedStatus) && publicReleaseChecklist.includes('/gse release --execute --out <bundle>'), 'public-release-checklist.md'),
195
197
  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
198
  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'),
199
+ check('RB06f2', 'bundle owner/external gate kit includes dry-run preflight commands', hasPendingGates ? (ownerExternalGateKitRecordCommands.includes('Preflight command') && ownerExternalGateKitRecordCommands.includes('--dry-run --json')) : ownerExternalGateKitData?.pendingGateCount === 0, 'owner-external-gate-kit/record-commands.md'),
198
200
  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
201
  check('RB07', 'validator includes release bundle audit', validate.includes('audit-release-bundle.mjs'), 'scripts/validate-gse.mjs'),
200
202
  check('RB08', 'packaging docs route release bundle command', packaging.includes('generate-release-bundle.mjs') && packaging.includes('audit-release-bundle.mjs'), 'references/packaging.md'),
@@ -78,8 +78,9 @@ const generatePlan = runFixtureScript(fixture, 'generate-release-owner-action-pl
78
78
  ])
79
79
  const manifest = fs.existsSync(manifestPath) ? parseJson(fs.readFileSync(manifestPath, 'utf8')) : null
80
80
  const plan = fs.existsSync(planPath) ? fs.readFileSync(planPath, 'utf8') : ''
81
- const pendingGates = manifest?.publicAcceptance?.pendingGates ?? []
82
- const commandScripts = requiredCommandScriptsFor(pendingGates)
81
+ const pendingGates = manifest?.publicAcceptance?.pendingGates ?? []
82
+ const hasPendingGates = pendingGates.length > 0
83
+ const commandScripts = requiredCommandScriptsFor(pendingGates)
83
84
 
84
85
  const recordRuns = [
85
86
  runFixtureScript(fixture, 'record-public-security-contact.mjs', [
@@ -214,8 +215,8 @@ const recordFailures = recordRuns.filter((item) => item.status !== 0)
214
215
  const promotedRows = finalReadinessData?.matrix?.filter((row) => row.status === 'verified') ?? []
215
216
 
216
217
  const checks = [
217
- check('ROAD01', 'release status manifest and owner action plan generate in fixture', generateManifest.status === 0 && generatePlan.status === 0 && manifest?.publicAcceptance?.publicAccepted === 'not-accepted' && plan.includes('GSE Release Owner Action Plan'), 'generate manifest and action plan'),
218
- check('ROAD02', 'owner action plan covers all pending gates from manifest', pendingGates.length > 0 && pendingGates.every((gate) => plan.includes(gate.area) && plan.includes(gate.recordCommand)) && !pendingGates.some((gate) => gate.area === 'License decision'), `${pendingGates.length} pending gate(s); License decision resolved`),
218
+ check('ROAD01', 'release status manifest and owner action plan generate in fixture', generateManifest.status === 0 && generatePlan.status === 0 && ['not-accepted', 'verified'].includes(manifest?.publicAcceptance?.publicAccepted) && plan.includes('GSE Release Owner Action Plan'), 'generate manifest and action plan'),
219
+ check('ROAD02', 'owner action plan covers all pending gates from manifest', hasPendingGates ? (pendingGates.every((gate) => plan.includes(gate.area) && plan.includes(gate.recordCommand)) && !pendingGates.some((gate) => gate.area === 'License decision')) : manifest?.publicAcceptance?.publicAccepted === 'verified' && plan.includes('No pending owner or external gates were reported by the manifest.'), `${pendingGates.length} pending gate(s); License decision resolved`),
219
220
  check('ROAD03', 'owner action plan commands map to real record scripts', expectedScripts.every((script) => commandScripts.includes(script)) && !plan.includes('proves-public-registry-publication'), commandScripts.join(', ')),
220
221
  check('ROAD04', 'accepted fixture records can be created for every owner/external gate family', recordFailures.length === 0, recordFailures.map((item) => item.stderr || item.stdout || item.command).join('; ') || 'all fixture records written'),
221
222
  check('ROAD05', 'final readiness promotes to publicAccepted verified after fixture records', finalReadiness.status === 0 && finalReadinessData?.workflows?.publicAccepted === 'verified' && promotedRows.length >= 22, finalReadinessData?.workflows?.publicAccepted ?? 'unknown'),
@@ -77,14 +77,14 @@ const verifiedRows = manifest?.readiness?.verified?.length ?? 0
77
77
  const ownerRequiredRows = manifest?.readiness?.ownerRequired?.length ?? 0
78
78
  const externalRequiredRows = manifest?.readiness?.externalRequired?.length ?? 0
79
79
  const publicAccepted = manifest?.publicAcceptance?.publicAccepted ?? manifest?.claimBoundary?.publicAccepted ?? 'unknown'
80
- const requiredOwners = ['project owner', 'repository owner', 'external CI', 'external registry', 'external marketplace', 'host runtime']
80
+ const requiredOwners = ['project owner', 'repository owner', 'external CI', 'external registry', 'external marketplace', 'host runtime']
81
81
  const recordCommands = [...new Set(pendingGates
82
82
  .map((gate) => String(gate.recordCommand ?? '').match(/node scripts\/([^ ]+)/)?.[1] ?? '')
83
83
  .filter(Boolean))]
84
- const planUsesCompleteRecordCommandTemplates = !plan.includes('--invocation-status') &&
85
- !/record-[a-z-]+\.mjs[\s\S]*\.\.\./.test(plan) &&
86
- !/record-[a-z-]+\.mjs[^\n`]*[<>]/.test(plan) &&
87
- plan.includes('--status accepted')
84
+ const planUsesCompleteRecordCommandTemplates = pendingGates.length === 0 || (!plan.includes('--invocation-status') &&
85
+ !/record-[a-z-]+\.mjs[\s\S]*\.\.\./.test(plan) &&
86
+ !/record-[a-z-]+\.mjs[^\n`]*[<>]/.test(plan) &&
87
+ plan.includes('--status accepted'))
88
88
  const planVerificationCommandsAreShellSafe = !/(audit-[a-z-]+|validate-gse|generate-release-status-manifest)\.mjs[^\n`]*[<>]/.test(plan) &&
89
89
  plan.includes('__GSE__')
90
90
  const canonicalPlanMatchesCurrentManifest = canonicalPlan.includes('- Public accepted: ' + publicAccepted) &&
@@ -98,11 +98,11 @@ const checks = [
98
98
  check('ROAP01', 'release owner action plan generator exists', exists('scripts/generate-release-owner-action-plan.mjs'), 'scripts/generate-release-owner-action-plan.mjs'),
99
99
  check('ROAP02', 'generator reads release status manifest', generator.includes('release-status-manifest.json') && generator.includes('pendingGates') && generator.includes('recordCommand'), 'generator source'),
100
100
  check('ROAP03', 'generator writes parseable action plan report', generatedPlan?.status === 0 && generatedData?.status === 'written' && plan.includes('GSE Release Owner Action Plan'), generatedPlan?.stderr || tmpPlan),
101
- check('ROAP04', 'plan preserves public acceptance boundary', plan.includes('Public accepted: not-accepted') && plan.includes('Local validation does not mean public acceptance') && plan.includes('Native slash-command support requires a real host invocation record'), 'claim boundary'),
101
+ check('ROAP04', 'plan preserves public acceptance boundary', plan.includes('Public accepted: verified') && plan.includes('Local validation does not mean public acceptance') && plan.includes('Native slash-command support requires a real host invocation record only when a host adapter claims it'), 'claim boundary'),
102
102
  check('ROAP05', 'plan groups actions by responsible owner', requiredOwners.every((owner) => plan.includes(owner.replace(/\b\w/g, (char) => char.toUpperCase())) || pendingGates.every((gate) => gate.owner !== owner)), 'owner groups'),
103
- check('ROAP06', 'plan includes every pending gate area', pendingGates.length > 0 && pendingGates.every((gate) => plan.includes(gate.area)) && !plan.includes('#### License decision'), 'pending gate areas; License decision resolved'),
104
- check('ROAP07', 'plan includes concrete record commands', recordCommands.length > 0 && recordCommands.every((command) => plan.includes(command)), recordCommands.join(', ')),
105
- check('ROAP08', 'plan includes dry-run preflight commands and post-action verification commands', plan.includes('Preflight command') && plan.includes('--dry-run --json') && plan.includes('validate-gse.mjs') && plan.includes('audit-public-acceptance-command-dry-run-drill.mjs') && plan.includes('audit-release-owner-action-plan.mjs') && plan.includes('generate-release-status-manifest.mjs'), 'dry-run preflight and verification commands'),
103
+ check('ROAP06', 'plan includes every pending gate area or states none are pending', (pendingGates.length === 0 && plan.includes('No pending owner or external gates were reported by the manifest')) || (pendingGates.length > 0 && pendingGates.every((gate) => plan.includes(gate.area)) && !plan.includes('#### License decision')), 'pending gate areas'),
104
+ check('ROAP07', 'plan includes concrete record commands when actions are pending', pendingGates.length === 0 || (recordCommands.length > 0 && recordCommands.every((command) => plan.includes(command))), recordCommands.join(', ') || 'none'),
105
+ check('ROAP08', 'plan includes dry-run preflight commands when needed and post-action verification commands', (pendingGates.length === 0 || (plan.includes('Preflight command') && plan.includes('--dry-run --json'))) && plan.includes('validate-gse.mjs') && plan.includes('audit-public-acceptance-command-dry-run-drill.mjs') && plan.includes('audit-release-owner-action-plan.mjs') && plan.includes('generate-release-status-manifest.mjs'), 'dry-run preflight and verification commands'),
106
106
  check('ROAP08b', 'plan uses complete record command templates', planUsesCompleteRecordCommandTemplates, 'no ellipsis, no stale host invocation flag, host records use --status accepted'),
107
107
  check('ROAP09', 'skill routes users to release owner action plan', skill.includes('generate-release-owner-action-plan.mjs'), 'SKILL.md'),
108
108
  check('ROAP10', 'packaging docs route release owner action plan', packaging.includes('generate-release-owner-action-plan.mjs') && packaging.includes('audit-release-owner-action-plan.mjs'), 'references/packaging.md'),
@@ -125,7 +125,7 @@ const report = {
125
125
  },
126
126
  limits: [
127
127
  'This audit verifies generation of an owner-facing action plan from the release status manifest.',
128
- 'It does not create owner decisions, public CI, public repository settings, marketplace approval, registry publication, or native slash-command evidence.',
128
+ 'It does not create optional host-native slash-command evidence.',
129
129
  ],
130
130
  checks,
131
131
  }
@@ -98,11 +98,11 @@ const checks = [
98
98
  check('RSM01', 'release status manifest generator exists', exists('scripts/generate-release-status-manifest.mjs'), 'scripts/generate-release-status-manifest.mjs'),
99
99
  check('RSM02', 'generator composes authoritative non-circular audits', ['audit-final-readiness.mjs', 'audit-public-acceptance-readiness.mjs', 'audit-host-runtime-invocations.mjs', 'audit-host-runtime-invocation-drill.mjs'].every((term) => generator.includes(term)) && !generator.includes("audit('audit-release-bundle.mjs')") && !generator.includes("audit('audit-distribution.mjs')"), 'generator source audits'),
100
100
  check('RSM03', 'generator writes parseable manifest', generated?.status === 0 && generatedData?.status === 'written' && manifest?.schemaVersion === 1, generated?.stderr || out),
101
- check('RSM04', 'manifest preserves public acceptance boundary', manifest?.claimBoundary?.publicAccepted === 'not-accepted' && manifest?.claimBoundary?.localValidationDoesNotMeanPublicAcceptance === true, 'publicAccepted not-accepted'),
102
- check('RSM05', 'manifest covers verified rows and current owner/external rows', (manifest?.readiness?.verified?.length ?? 0) > 0 && ((manifest?.readiness?.ownerRequired?.length ?? 0) + (manifest?.readiness?.externalRequired?.length ?? 0)) > 0, 'readiness row groups'),
101
+ check('RSM04', 'manifest preserves public acceptance boundary', manifest?.claimBoundary?.publicAccepted === 'verified' && manifest?.claimBoundary?.localValidationDoesNotMeanPublicAcceptance === true && manifest?.claimBoundary?.nativeSlashCommandIsOptionalAdapterClaim === true, 'publicAccepted verified; native slash optional adapter claim'),
102
+ check('RSM05', 'manifest covers verified rows and optional not-claimed rows', (manifest?.readiness?.verified?.length ?? 0) > 0 && (manifest?.readiness?.notClaimed?.length ?? 0) > 0 && ((manifest?.readiness?.ownerRequired?.length ?? 0) + (manifest?.readiness?.externalRequired?.length ?? 0)) === 0, 'readiness row groups'),
103
103
  check('RSM06', 'manifest covers install and distribution status', requiredDistribution.every((key) => manifest?.distribution?.[key]), requiredDistribution.join(', ')),
104
- check('RSM07', 'manifest covers public acceptance next commands', (manifest?.publicAcceptance?.pendingGates?.length ?? 0) > 0 && (manifest?.publicAcceptance?.nextCommands?.length ?? 0) === (manifest?.publicAcceptance?.pendingGates?.length ?? -1), 'pending gates and next commands'),
105
- check('RSM07b', 'manifest covers public acceptance dry-run preflight commands', (manifest?.publicAcceptance?.pendingGates?.length ?? 0) > 0 && (manifest?.publicAcceptance?.nextPreflightCommands?.length ?? 0) === (manifest?.publicAcceptance?.pendingGates?.length ?? -1) && manifest.publicAcceptance.nextPreflightCommands.every((command) => command.includes('--dry-run --json')), 'pending gates and preflight commands'),
104
+ check('RSM07', 'manifest has no public acceptance next commands when no required gates remain', (manifest?.publicAcceptance?.pendingGates?.length ?? -1) === 0 && (manifest?.publicAcceptance?.nextCommands?.length ?? -1) === 0, 'pending gates and next commands'),
105
+ check('RSM07b', 'manifest has no public acceptance dry-run preflight commands when no required gates remain', (manifest?.publicAcceptance?.pendingGates?.length ?? -1) === 0 && (manifest?.publicAcceptance?.nextPreflightCommands?.length ?? -1) === 0, 'pending gates and preflight commands'),
106
106
  check('RSM08', 'manifest covers host runtime evidence counts', Number.isInteger(manifest?.hostRuntime?.nativeSlashCommandRecords) && Number.isInteger(manifest?.hostRuntime?.portableTextCommandRecords) && manifest.hostRuntime.nativeSlashCommandRecords >= 0 && manifest.hostRuntime.portableTextCommandRecords >= 0, `native ${manifest?.hostRuntime?.nativeSlashCommandRecords ?? 'unknown'}, portable ${manifest?.hostRuntime?.portableTextCommandRecords ?? 'unknown'}`),
107
107
  check('RSM08b', 'manifest covers host runtime fixture drill status', manifest?.hostRuntime?.fixtureDrill === 'verified' && manifest?.hostRuntime?.fixtureNativeSlashCommandRecords === 1 && manifest?.hostRuntime?.fixturePortableTextCommandRecords === 4 && manifest?.hostRuntime?.fixtureEvidenceIsPersistent === false, `drill ${manifest?.hostRuntime?.fixtureDrill ?? 'unknown'}, fixture native ${manifest?.hostRuntime?.fixtureNativeSlashCommandRecords ?? 'unknown'}, fixture portable ${manifest?.hostRuntime?.fixturePortableTextCommandRecords ?? 'unknown'}`),
108
108
  check('RSM09', 'manifest points to handoff artifacts', requiredArtifacts.every((key) => manifest?.artifacts?.[key]), requiredArtifacts.join(', ')),
@@ -67,9 +67,11 @@ const requiredVerifiedAssets = [
67
67
  'assets/templates/public-repository-settings-record.md',
68
68
  'scripts/audit-gse.mjs',
69
69
  'scripts/validate-gse.mjs',
70
- 'scripts/audit-target-project.mjs',
71
- 'scripts/generate-session-prompt.mjs',
72
- 'scripts/run-gse-command.mjs',
70
+ 'scripts/audit-target-project.mjs',
71
+ 'scripts/generate-session-prompt.mjs',
72
+ 'scripts/generate-continue-packet.mjs',
73
+ 'scripts/audit-continue-preflight.mjs',
74
+ 'scripts/run-gse-command.mjs',
73
75
  'scripts/audit-command-execution.mjs',
74
76
  'scripts/audit-close-gate.mjs',
75
77
  'scripts/update-project-state.mjs',
@@ -108,8 +110,10 @@ const requiredVerifiedAssets = [
108
110
  'scripts/record-host-invocation.mjs',
109
111
  'scripts/audit-host-runtime-invocations.mjs',
110
112
  'references/final-readiness.md',
111
- 'scripts/audit-final-readiness.mjs',
112
- 'scripts/audit-final-readiness-promotion.mjs',
113
+ 'scripts/audit-final-readiness.mjs',
114
+ 'references/final-form-roadmap.md',
115
+ 'scripts/audit-final-form-roadmap.mjs',
116
+ 'scripts/audit-final-readiness-promotion.mjs',
113
117
  'scripts/audit-final-form-stale-copy.mjs',
114
118
  'scripts/generate-final-acceptance-packet.mjs',
115
119
  'scripts/audit-final-acceptance-packet.mjs',
@@ -135,7 +139,7 @@ const currentStatusTerms = [
135
139
  'native `.gse/changes/<change-id>/` change pack generation',
136
140
  'execution-quality pack templates',
137
141
  'domain model reference and ADR template are verified',
138
- 'role docs, dispatch packet, file ownership, and forward-test fallback are verified',
142
+ 'role docs, dispatch packet, file ownership, forward-test fallback, role fallback packet template, scaffolded `.gse/agents/role-fallback-packets.md`, and role dispatch fallback audit are verified',
139
143
  'review protocol and architecture health references are verified',
140
144
  'v0.2 - Self-Auditing GSE',
141
145
  'Status: verified.',
@@ -186,7 +190,7 @@ const checks = [
186
190
  check('RC03', 'master plan no longer contains stale missing/planned status phrases', stalePhrases.every((phrase) => !masterPlan.includes(phrase)), stalePhrases.join(', ')),
187
191
  check('RC04', 'master plan contains current verified status terms', currentStatusTerms.every((term) => masterPlan.includes(term)), currentStatusTerms.join(', ')),
188
192
  check('RC05', 'goal map tracks v1 execution-state work as verified', goalMap.includes('GSE-008') && goalMap.includes('Productize v1.0 execution-state workflow') && goalMap.includes('verified') && goalMap.includes('audit-v1-target-validation.mjs'), '.gse/goal-map.md'),
189
- check('RC06', 'master plan preserves target validation while keeping current final-form limits explicit', masterPlan.includes('AION/MuseFlow v1 target validation are verified') && masterPlan.includes('MIT license decision') && masterPlan.includes('public registry publication for `@t275005746/gse@0.1.0`') && masterPlan.includes('Remaining final-form work is host-native slash-command support with real host runtime evidence.') && masterPlan.includes('P1: harden GSE beyond the verified v1.0 execution-state workflow') && !masterPlan.includes('This does not yet prove the full v1.0 execution-state layer') && !masterPlan.includes('owner license selection remains required before an accepted public release'), '.gse/gse-design-master-plan.md'),
193
+ check('RC06', 'master plan preserves target validation and routes final-form execution through the roadmap contract', masterPlan.includes('AION/MuseFlow v1 target validation are verified') && masterPlan.includes('MIT license decision') && masterPlan.includes('public registry publication for `@t275005746/gse@0.1.0`') && masterPlan.includes('The Final Form Roadmap now lives in `references/final-form-roadmap.md`') && masterPlan.includes('/gse continue` now runs a hard preflight and returns a compact action prompt plus state packet') && masterPlan.includes('AION and MuseFlow remain the primary real-project drills') && !masterPlan.includes('This does not yet prove the full v1.0 execution-state layer') && !masterPlan.includes('owner license selection remains required before an accepted public release'), '.gse/gse-design-master-plan.md'),
190
194
  ]
191
195
 
192
196
  const passed = checks.filter((item) => item.status === 'passed').length
@@ -0,0 +1,212 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs'
3
+ import os from 'node:os'
4
+ import path from 'node:path'
5
+ import { fileURLToPath } from 'node:url'
6
+ import { spawnSync } from 'node:child_process'
7
+
8
+ const args = process.argv.slice(2)
9
+
10
+ function readArg(name, fallback = null) {
11
+ const index = args.indexOf(name)
12
+ if (index === -1) return fallback
13
+ return args[index + 1] ?? fallback
14
+ }
15
+
16
+ const root = path.resolve(readArg('--root', path.join(import.meta.dirname, '..')))
17
+ const targetArg = readArg('--target')
18
+ const jsonOnly = args.includes('--json')
19
+
20
+ const requiredRoles = ['Planner', 'Locator', 'Implementer', 'Verifier', 'Reviewer', 'Docs/Evidence', 'Release']
21
+ const allowedModes = new Set(['real-subagent', 'sequential-role', 'handoff-session'])
22
+ const allowedDelegation = new Set(['yes', 'no'])
23
+ const allowedToolStatuses = new Set(['verified', 'documented', 'unknown', 'unavailable'])
24
+
25
+ function readText(filePath) {
26
+ return fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8').replace(/^\uFEFF/, '') : ''
27
+ }
28
+
29
+ function parseRoleTable(text) {
30
+ const rows = []
31
+ for (const line of text.split(/\r?\n/)) {
32
+ const trimmed = line.trim()
33
+ if (!trimmed.startsWith('|')) continue
34
+ if (/^\|\s*-+/.test(trimmed)) continue
35
+ if (/^\|\s*Role\s*\|/i.test(trimmed)) continue
36
+ const cells = trimmed
37
+ .slice(1, trimmed.endsWith('|') ? -1 : undefined)
38
+ .split('|')
39
+ .map((cell) => cell.trim())
40
+ if (cells.length < 8) continue
41
+ rows.push({
42
+ role: cells[0],
43
+ mode: cells[1],
44
+ realDelegationUsed: cells[2],
45
+ toolStatus: cells[3],
46
+ fallbackOutput: cells[4],
47
+ evidence: cells[5],
48
+ stopCondition: cells[6],
49
+ writeAccess: cells[7],
50
+ })
51
+ }
52
+ return rows
53
+ }
54
+
55
+ export function readRoleDispatchFallback(target) {
56
+ const filePath = path.join(target, '.gse', 'agents', 'role-fallback-packets.md')
57
+ const exists = fs.existsSync(filePath)
58
+ const text = exists ? readText(filePath) : ''
59
+ const packets = parseRoleTable(text)
60
+ const roleSet = new Set(packets.map((packet) => packet.role))
61
+ const missingRoles = requiredRoles.filter((role) => !roleSet.has(role))
62
+ const invalidMode = packets.filter((packet) => !allowedModes.has(packet.mode))
63
+ const invalidDelegation = packets.filter((packet) => !allowedDelegation.has(packet.realDelegationUsed))
64
+ const invalidToolStatus = packets.filter((packet) => !allowedToolStatuses.has(packet.toolStatus))
65
+ const missingEvidence = packets.filter((packet) => !packet.fallbackOutput || !packet.evidence || !packet.stopCondition || !packet.writeAccess)
66
+ const fakeDelegationRisk = packets.filter((packet) => packet.realDelegationUsed === 'yes' && packet.toolStatus !== 'verified')
67
+ const status = !exists
68
+ ? 'warning'
69
+ : missingRoles.length > 0 || invalidMode.length > 0 || invalidDelegation.length > 0 || invalidToolStatus.length > 0 || missingEvidence.length > 0 || fakeDelegationRisk.length > 0
70
+ ? 'failed'
71
+ : 'passed'
72
+ return {
73
+ path: '.gse/agents/role-fallback-packets.md',
74
+ exists,
75
+ status,
76
+ packets,
77
+ summary: {
78
+ requiredRoles,
79
+ total: packets.length,
80
+ missingRoles,
81
+ invalidMode: invalidMode.map((packet) => packet.role),
82
+ invalidDelegation: invalidDelegation.map((packet) => packet.role),
83
+ invalidToolStatus: invalidToolStatus.map((packet) => packet.role),
84
+ missingEvidence: missingEvidence.map((packet) => packet.role),
85
+ fakeDelegationRisk: fakeDelegationRisk.map((packet) => packet.role),
86
+ sequentialFallbackRoles: packets.filter((packet) => packet.mode === 'sequential-role').map((packet) => packet.role),
87
+ },
88
+ }
89
+ }
90
+
91
+ function run(script, commandArgs) {
92
+ const result = spawnSync(process.execPath, [path.join(root, 'scripts', script), ...commandArgs], {
93
+ cwd: root,
94
+ encoding: 'utf8',
95
+ windowsHide: true,
96
+ })
97
+ return {
98
+ command: [process.execPath, path.join(root, 'scripts', script), ...commandArgs].join(' '),
99
+ status: result.status ?? 1,
100
+ stdout: (result.stdout ?? '').trim(),
101
+ stderr: (result.stderr ?? '').trim(),
102
+ }
103
+ }
104
+
105
+ function check(id, label, ok, evidence, risk = '') {
106
+ return { id, label, status: ok ? 'passed' : 'failed', evidence, risk }
107
+ }
108
+
109
+ function createFixture() {
110
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gse-role-fallback-'))
111
+ const init = run('init-project.mjs', ['--target', dir, '--mode', 'standard', '--json'])
112
+ return { dir, init }
113
+ }
114
+
115
+ function audit(target) {
116
+ const resolvedTarget = path.resolve(target)
117
+ const reference = readText(path.join(root, 'references', 'role-dispatch-fallback.md'))
118
+ const agentRoles = readText(path.join(root, 'references', 'agent-roles.md'))
119
+ const dispatchTemplate = readText(path.join(root, 'assets', 'templates', 'dispatch-packet.md'))
120
+ const fallbackTemplate = readText(path.join(root, 'assets', 'templates', 'role-fallback-packet.md'))
121
+ const initProject = readText(path.join(root, 'scripts', 'init-project.mjs'))
122
+ const continuePacket = readText(path.join(root, 'scripts', 'generate-continue-packet.mjs'))
123
+ const validationProfile = readText(path.join(root, 'scripts', 'run-validation-profile.mjs'))
124
+ const validator = readText(path.join(root, 'scripts', 'validate-gse.mjs'))
125
+ const fallback = readRoleDispatchFallback(resolvedTarget)
126
+ const roleDocsCoverRequired = requiredRoles.every((role) => reference.includes(role)) && agentRoles.includes('Planner') && agentRoles.includes('Release')
127
+ const templatesCoverEvidence =
128
+ dispatchTemplate.includes('Real delegation used') &&
129
+ dispatchTemplate.includes('Role output evidence') &&
130
+ dispatchTemplate.includes('Claim boundary') &&
131
+ fallbackTemplate.includes('Execution mode') &&
132
+ fallbackTemplate.includes('Evidence level')
133
+ const checks = [
134
+ check('RDF01', 'role dispatch fallback reference defines required roles and claim boundary', roleDocsCoverRequired && reference.includes('real-subagent') && reference.includes('sequential-role') && reference.includes('handoff-session'), 'references/role-dispatch-fallback.md and references/agent-roles.md'),
135
+ check('RDF02', 'dispatch templates expose auditable fallback fields', templatesCoverEvidence, 'assets/templates/dispatch-packet.md and assets/templates/role-fallback-packet.md'),
136
+ check('RDF03', 'init-project scaffolds role fallback packets for standard or enterprise projects', initProject.includes('agents/role-fallback-packets.md') && initProject.includes('Planner') && initProject.includes('Release'), 'scripts/init-project.mjs'),
137
+ check('RDF04', 'target role fallback packet is present or reported as warning', fallback.exists || fallback.status === 'warning', fallback.exists ? fallback.path : 'missing role fallback packet warning'),
138
+ check('RDF05', 'target role fallback packets cover all required roles when present', !fallback.exists || fallback.summary.missingRoles.length === 0, fallback.summary.missingRoles.join(', ') || 'required roles present'),
139
+ check('RDF06', 'target role fallback packets use valid mode/delegation/tool status vocabulary', !fallback.exists || (fallback.summary.invalidMode.length === 0 && fallback.summary.invalidDelegation.length === 0 && fallback.summary.invalidToolStatus.length === 0), 'mode/delegation/tool status vocabulary'),
140
+ check('RDF07', 'target role fallback packets include output evidence and stop conditions', !fallback.exists || fallback.summary.missingEvidence.length === 0, fallback.summary.missingEvidence.join(', ') || 'fallback evidence fields present'),
141
+ check('RDF08', 'target role fallback packets do not claim real delegation without verified tool status', !fallback.exists || fallback.summary.fakeDelegationRisk.length === 0, fallback.summary.fakeDelegationRisk.join(', ') || 'no fake delegation risk'),
142
+ check('RDF09', 'continue packet surfaces role fallback readiness', continuePacket.includes('readRoleDispatchFallback') && continuePacket.includes('roleFallback'), 'scripts/generate-continue-packet.mjs'),
143
+ check('RDF10', 'validation routes include role dispatch fallback audit', validationProfile.includes('audit-role-dispatch-fallback.mjs') && validator.includes('audit-role-dispatch-fallback.mjs'), 'validation profile and validate-gse'),
144
+ ]
145
+ const passed = checks.filter((item) => item.status === 'passed').length
146
+ const failed = checks.length - passed
147
+ return {
148
+ target: resolvedTarget,
149
+ generatedAt: new Date().toISOString(),
150
+ summary: { status: failed === 0 ? fallback.status : 'failed', passed, failed, total: checks.length },
151
+ workflows: {
152
+ roleDispatchFallback: failed === 0 && fallback.status !== 'failed' ? 'verified' : 'failed',
153
+ requiredRoles,
154
+ packets: fallback.summary.total,
155
+ sequentialFallbackRoles: fallback.summary.sequentialFallbackRoles.length,
156
+ },
157
+ roleFallback: fallback,
158
+ checks,
159
+ limits: [
160
+ 'Role fallback packets prove auditable role boundaries, not real subagent execution.',
161
+ 'Real subagent support still requires current host dispatch evidence.',
162
+ 'Missing role fallback packets are a warning for target projects until the scaffold is adopted.',
163
+ ],
164
+ }
165
+ }
166
+
167
+ function selfTestReport() {
168
+ const fixture = createFixture()
169
+ const fixtureReport = audit(fixture.dir)
170
+ const missingDir = fs.mkdtempSync(path.join(os.tmpdir(), 'gse-role-fallback-missing-'))
171
+ fs.mkdirSync(path.join(missingDir, '.gse'), { recursive: true })
172
+ const missingReport = audit(missingDir)
173
+ fs.rmSync(fixture.dir, { recursive: true, force: true })
174
+ fs.rmSync(missingDir, { recursive: true, force: true })
175
+ const checks = [
176
+ check('RDFA01', 'init-project creates role fallback packet scaffold', fixture.init.status === 0 && fixtureReport.roleFallback.exists, 'scripts/init-project.mjs'),
177
+ check('RDFA02', 'scaffold covers every required role', fixtureReport.roleFallback.summary.missingRoles.length === 0, fixtureReport.roleFallback.packets.map((packet) => packet.role).join(', ')),
178
+ check('RDFA03', 'scaffold uses sequential fallback without fake real delegation', fixtureReport.roleFallback.summary.fakeDelegationRisk.length === 0 && fixtureReport.roleFallback.packets.every((packet) => packet.realDelegationUsed === 'no'), 'sequential fallback packets'),
179
+ check('RDFA04', 'missing role fallback file is warning, not hard failure', missingReport.roleFallback.status === 'warning', 'missing fixture'),
180
+ check('RDFA05', 'audit source is wired to continue packet and validation scripts', fixtureReport.checks.find((item) => item.id === 'RDF09')?.status === 'passed' && fixtureReport.checks.find((item) => item.id === 'RDF10')?.status === 'passed', 'continue/validation wiring'),
181
+ ]
182
+ const passed = checks.filter((item) => item.status === 'passed').length
183
+ const failed = checks.length - passed
184
+ return {
185
+ root,
186
+ generatedAt: new Date().toISOString(),
187
+ summary: { status: failed === 0 ? 'passed' : 'failed', passed, failed, total: checks.length },
188
+ workflows: {
189
+ roleDispatchFallback: failed === 0 ? 'verified' : 'failed',
190
+ initProjectRoleFallbackScaffold: failed === 0 ? 'verified' : 'failed',
191
+ },
192
+ fixture: {
193
+ scaffoldStatus: fixtureReport.roleFallback.status,
194
+ missingStatus: missingReport.roleFallback.status,
195
+ roles: fixtureReport.roleFallback.packets.map((packet) => packet.role),
196
+ },
197
+ checks,
198
+ limits: [
199
+ 'This self-test verifies scaffold and audit mechanics.',
200
+ 'It does not prove a real host spawned subagents.',
201
+ ],
202
+ }
203
+ }
204
+
205
+ const isCli = process.argv[1] && fileURLToPath(import.meta.url) === path.resolve(process.argv[1])
206
+
207
+ if (isCli) {
208
+ const report = targetArg ? audit(targetArg) : selfTestReport()
209
+ if (jsonOnly) console.log(JSON.stringify(report, null, 2))
210
+ else console.log(JSON.stringify(report, null, 2))
211
+ if (report.summary.status === 'failed') process.exit(1)
212
+ }