@t275005746/gse 0.1.1 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. package/.gse/state.json +147 -26
  2. package/CHANGELOG.md +19 -3
  3. package/README.md +13 -6
  4. package/README.zh-CN.md +13 -6
  5. package/SKILL.md +32 -12
  6. package/assets/templates/dispatch-packet.md +22 -14
  7. package/assets/templates/evidence.md +11 -5
  8. package/assets/templates/role-fallback-packet.md +49 -0
  9. package/package.json +18 -10
  10. package/references/agent-roles.md +41 -20
  11. package/references/commands.md +74 -45
  12. package/references/drift-audit.md +2 -1
  13. package/references/evidence-taxonomy.md +46 -9
  14. package/references/file-ownership.md +17 -13
  15. package/references/final-form-roadmap.md +201 -0
  16. package/references/final-readiness.md +3 -3
  17. package/references/host-adapters.md +38 -10
  18. package/references/learning-system.md +24 -8
  19. package/references/maintenance-cadence.md +51 -0
  20. package/references/project-guards.md +52 -0
  21. package/references/quality-gates.md +18 -11
  22. package/references/role-dispatch-fallback.md +54 -0
  23. package/references/tool-adapters.md +13 -3
  24. package/scripts/audit-close-gate-hardening.mjs +188 -0
  25. package/scripts/audit-close-gate.mjs +219 -33
  26. package/scripts/audit-command-execution.mjs +30 -17
  27. package/scripts/audit-commands.mjs +7 -5
  28. package/scripts/audit-completion-plan-drill.mjs +230 -0
  29. package/scripts/audit-completion-readiness.mjs +9 -6
  30. package/scripts/audit-continue-preflight.mjs +234 -0
  31. package/scripts/audit-evidence-levels.mjs +217 -0
  32. package/scripts/audit-evidence-review-queue.mjs +206 -0
  33. package/scripts/audit-final-acceptance-packet.mjs +9 -9
  34. package/scripts/audit-final-form-progress-report.mjs +19 -18
  35. package/scripts/audit-final-form-roadmap.mjs +157 -0
  36. package/scripts/audit-final-form-stale-copy.mjs +2 -2
  37. package/scripts/audit-final-readiness.mjs +3 -3
  38. package/scripts/audit-fresh-session-readiness.mjs +1 -1
  39. package/scripts/audit-host-capabilities.mjs +237 -0
  40. package/scripts/audit-installed-sync.mjs +203 -0
  41. package/scripts/audit-learning-drift.mjs +292 -0
  42. package/scripts/audit-learning-promotion.mjs +351 -0
  43. package/scripts/audit-learning-system.mjs +24 -14
  44. package/scripts/audit-local-final-form-completion.mjs +11 -10
  45. package/scripts/audit-maintenance-cadence.mjs +139 -0
  46. package/scripts/audit-maintenance-snapshot.mjs +181 -0
  47. package/scripts/audit-npm-package-metadata.mjs +8 -2
  48. package/scripts/audit-npm-publish-dry-run.mjs +10 -4
  49. package/scripts/audit-npm-tarball-install.mjs +9 -3
  50. package/scripts/audit-owner-external-gate-kit.mjs +12 -11
  51. package/scripts/audit-project-guards.mjs +189 -0
  52. package/scripts/audit-project.mjs +14 -11
  53. package/scripts/audit-public-acceptance-handoff.mjs +10 -10
  54. package/scripts/audit-public-acceptance-readiness.mjs +6 -6
  55. package/scripts/audit-public-release-checklist.mjs +17 -15
  56. package/scripts/audit-release-bundle.mjs +13 -11
  57. package/scripts/audit-release-owner-action-plan-drill.mjs +5 -4
  58. package/scripts/audit-release-owner-action-plan.mjs +10 -10
  59. package/scripts/audit-release-status-manifest.mjs +4 -4
  60. package/scripts/audit-roadmap-consistency.mjs +11 -7
  61. package/scripts/audit-role-dispatch-fallback.mjs +212 -0
  62. package/scripts/audit-session-sync.mjs +127 -0
  63. package/scripts/audit-state-freshness.mjs +6 -5
  64. package/scripts/audit-state-repair.mjs +360 -0
  65. package/scripts/audit-target-hardening-drills.mjs +267 -0
  66. package/scripts/audit-tool-fallback-policy.mjs +180 -0
  67. package/scripts/audit-ui-browser-evidence-policy.mjs +191 -0
  68. package/scripts/audit-validation-profiles.mjs +6 -4
  69. package/scripts/backfill-evidence-levels.mjs +98 -0
  70. package/scripts/check-encoding.mjs +72 -0
  71. package/scripts/generate-continue-packet.mjs +1214 -0
  72. package/scripts/generate-final-acceptance-packet.mjs +23 -8
  73. package/scripts/generate-final-form-progress-report.mjs +25 -23
  74. package/scripts/generate-host-runtime-evidence-handoff.mjs +23 -16
  75. package/scripts/generate-maintenance-snapshot.mjs +216 -0
  76. package/scripts/generate-public-acceptance-handoff.mjs +26 -14
  77. package/scripts/generate-release-owner-action-plan.mjs +4 -3
  78. package/scripts/generate-release-status-manifest.mjs +4 -3
  79. package/scripts/init-project.mjs +144 -63
  80. package/scripts/record-learning.mjs +37 -8
  81. package/scripts/record-session-sync.mjs +87 -0
  82. package/scripts/run-gse-command.mjs +79 -47
  83. package/scripts/run-validation-profile.mjs +24 -5
  84. package/scripts/validate-gse.mjs +216 -0
@@ -0,0 +1,217 @@
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
+
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 targetArg = readArg('--target')
17
+ const jsonOnly = args.includes('--json')
18
+
19
+ export const evidenceLevels = [
20
+ 'result',
21
+ 'verified-unit',
22
+ 'verified-component',
23
+ 'verified-api',
24
+ 'verified-browser',
25
+ 'verified-ci',
26
+ 'accepted-owner',
27
+ 'accepted-release',
28
+ 'external-required',
29
+ ]
30
+
31
+ const levelRank = new Map([
32
+ ['result', 0],
33
+ ['verified-unit', 1],
34
+ ['verified-component', 2],
35
+ ['verified-api', 2],
36
+ ['verified-browser', 3],
37
+ ['verified-ci', 3],
38
+ ['accepted-owner', 4],
39
+ ['accepted-release', 4],
40
+ ['external-required', 0],
41
+ ])
42
+
43
+ function readText(filePath) {
44
+ return fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8').replace(/^\uFEFF/, '') : ''
45
+ }
46
+
47
+ function readJsonl(filePath) {
48
+ if (!fs.existsSync(filePath)) return { exists: false, ok: false, records: [], error: 'missing' }
49
+ const lines = readText(filePath)
50
+ .split(/\r?\n/)
51
+ .map((line) => line.trim())
52
+ .filter(Boolean)
53
+ const records = []
54
+ for (const [index, line] of lines.entries()) {
55
+ try {
56
+ records.push(JSON.parse(line))
57
+ } catch (error) {
58
+ return { exists: true, ok: false, records, error: `line ${index + 1}: ${error.message}` }
59
+ }
60
+ }
61
+ return { exists: true, ok: true, records, error: '' }
62
+ }
63
+
64
+ export function analyzeEvidenceLevels(records) {
65
+ const allowed = new Set(evidenceLevels)
66
+ const recordsWithLevel = records.filter((record) => typeof record.evidenceLevel === 'string' && record.evidenceLevel)
67
+ const missingLevel = records.filter((record) => !record.evidenceLevel).map((record) => record.summary || record.recordType || '(unknown)')
68
+ const invalidLevel = records
69
+ .filter((record) => record.evidenceLevel && !allowed.has(record.evidenceLevel))
70
+ .map((record) => ({ summary: record.summary || record.recordType || '(unknown)', evidenceLevel: record.evidenceLevel }))
71
+ const downgraded = records
72
+ .filter((record) => record.evidenceLevel && record.requiredEvidenceLevel && allowed.has(record.evidenceLevel) && allowed.has(record.requiredEvidenceLevel))
73
+ .filter((record) => (levelRank.get(record.evidenceLevel) ?? 0) < (levelRank.get(record.requiredEvidenceLevel) ?? 0))
74
+ .map((record) => ({
75
+ summary: record.summary || record.recordType || '(unknown)',
76
+ evidenceLevel: record.evidenceLevel,
77
+ requiredEvidenceLevel: record.requiredEvidenceLevel,
78
+ }))
79
+ return {
80
+ allowed: evidenceLevels,
81
+ records: records.length,
82
+ recordsWithLevel: recordsWithLevel.length,
83
+ missingLevel,
84
+ invalidLevel,
85
+ downgraded,
86
+ }
87
+ }
88
+
89
+ function check(id, label, ok, evidence, risk = '') {
90
+ return { id, label, status: ok ? 'passed' : 'failed', evidence, risk }
91
+ }
92
+
93
+ function createFixture() {
94
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gse-evidence-levels-'))
95
+ fs.mkdirSync(path.join(dir, '.gse', 'evidence'), { recursive: true })
96
+ const records = [
97
+ {
98
+ date: '2026-07-08',
99
+ recordType: 'slice',
100
+ status: 'verified',
101
+ evidenceLevel: 'verified-component',
102
+ requiredEvidenceLevel: 'verified-browser',
103
+ summary: 'UI component proof is downgraded from required browser proof.',
104
+ evidenceFile: '.gse/evidence/2026-07-08.md',
105
+ commands: ['pnpm test component'],
106
+ nextAction: 'Run browser smoke before release.',
107
+ },
108
+ {
109
+ date: '2026-07-08',
110
+ recordType: 'slice',
111
+ status: 'verified',
112
+ evidenceLevel: 'verified-browser',
113
+ requiredEvidenceLevel: 'verified-browser',
114
+ summary: 'Browser proof satisfies UI requirement.',
115
+ evidenceFile: '.gse/evidence/2026-07-08.md',
116
+ commands: ['pnpm smoke browser'],
117
+ nextAction: 'Close slice.',
118
+ },
119
+ {
120
+ date: '2026-07-08',
121
+ recordType: 'release',
122
+ status: 'result',
123
+ evidenceLevel: 'external-required',
124
+ requiredEvidenceLevel: 'accepted-release',
125
+ summary: 'Release evidence still needs owner or external acceptance.',
126
+ evidenceFile: '.gse/evidence/2026-07-08.md',
127
+ commands: ['gse owner-actions'],
128
+ nextAction: 'Collect external gate.',
129
+ },
130
+ ]
131
+ fs.writeFileSync(path.join(dir, '.gse', 'evidence', 'index.jsonl'), records.map((record) => JSON.stringify(record)).join('\n') + '\n', 'utf8')
132
+ fs.writeFileSync(path.join(dir, '.gse', 'evidence', '2026-07-08.md'), '# Evidence\n', 'utf8')
133
+ return dir
134
+ }
135
+
136
+ function audit(target) {
137
+ const resolvedTarget = path.resolve(target)
138
+ const taxonomy = readText(path.join(root, 'references', 'evidence-taxonomy.md'))
139
+ const qualityGates = readText(path.join(root, 'references', 'quality-gates.md'))
140
+ const closeGate = readText(path.join(root, 'scripts', 'audit-close-gate.mjs'))
141
+ const continuePacket = readText(path.join(root, 'scripts', 'generate-continue-packet.mjs'))
142
+ const validationProfile = readText(path.join(root, 'scripts', 'run-validation-profile.mjs'))
143
+ const validator = readText(path.join(root, 'scripts', 'validate-gse.mjs'))
144
+ const evidenceIndex = readJsonl(path.join(resolvedTarget, '.gse', 'evidence', 'index.jsonl'))
145
+ const analysis = analyzeEvidenceLevels(evidenceIndex.records)
146
+ const fixtureRecords = [
147
+ {
148
+ status: 'verified',
149
+ evidenceLevel: 'verified-component',
150
+ requiredEvidenceLevel: 'verified-browser',
151
+ summary: 'fixture component downgrade',
152
+ },
153
+ {
154
+ status: 'verified',
155
+ evidenceLevel: 'verified-browser',
156
+ requiredEvidenceLevel: 'verified-browser',
157
+ summary: 'fixture browser proof',
158
+ },
159
+ ]
160
+ const fixtureAnalysis = analyzeEvidenceLevels(fixtureRecords)
161
+
162
+ const requiredTerms = [
163
+ 'Evidence Level',
164
+ 'verified-unit',
165
+ 'verified-component',
166
+ 'verified-api',
167
+ 'verified-browser',
168
+ 'verified-ci',
169
+ 'accepted-owner',
170
+ 'accepted-release',
171
+ 'external-required',
172
+ 'Evidence status answers whether the work is result, verified, or accepted.',
173
+ 'Evidence level answers what kind of proof produced that status.',
174
+ ]
175
+
176
+ const checks = [
177
+ check('EL01', 'taxonomy defines all evidence levels and separates status from level', requiredTerms.every((term) => taxonomy.includes(term)), 'references/evidence-taxonomy.md'),
178
+ check('EL02', 'quality gates require explicit evidence level for UI/browser/API/CI claims', qualityGates.includes('Evidence level uses `evidence-taxonomy.md`') && qualityGates.includes('verified-browser') && qualityGates.includes('verified-component'), 'references/quality-gates.md'),
179
+ check('EL03', 'continue packet exposes latest evidence level and downgrade summary', continuePacket.includes('analyzeEvidenceLevels') && continuePacket.includes('evidenceLevels') && continuePacket.includes('latestEvidenceLevel'), 'scripts/generate-continue-packet.mjs'),
180
+ check('EL04', 'close gate checks evidence level validity and downgrade warnings', closeGate.includes('analyzeEvidenceLevels') && closeGate.includes('CG09') && closeGate.includes('evidence level'), 'scripts/audit-close-gate.mjs'),
181
+ check('EL05', 'validation routes include evidence level audit', validationProfile.includes('audit-evidence-levels.mjs') && validator.includes('audit-evidence-levels.mjs'), 'validation profile and validate-gse'),
182
+ check('EL06', 'target evidence index parses', evidenceIndex.ok, evidenceIndex.ok ? `${evidenceIndex.records.length} record(s)` : evidenceIndex.error),
183
+ check('EL07', 'target evidence levels are valid when present', analysis.invalidLevel.length === 0, analysis.invalidLevel.length ? JSON.stringify(analysis.invalidLevel) : 'no invalid evidence levels'),
184
+ check('EL08', 'fixture detects downgraded UI/browser proof without failing parse', fixtureAnalysis.downgraded.length === 1 && fixtureAnalysis.invalidLevel.length === 0, `${fixtureAnalysis.downgraded.length} downgrade(s), ${fixtureAnalysis.invalidLevel.length} invalid level(s)`),
185
+ ]
186
+ const passed = checks.filter((item) => item.status === 'passed').length
187
+ const failed = checks.length - passed
188
+ return {
189
+ target: resolvedTarget,
190
+ generatedAt: new Date().toISOString(),
191
+ summary: { status: failed === 0 ? 'passed' : 'failed', passed, failed, total: checks.length },
192
+ workflows: {
193
+ evidenceLevels: failed === 0 ? 'verified' : 'failed',
194
+ recordsWithLevel: analysis.recordsWithLevel,
195
+ missingLevel: analysis.missingLevel.length,
196
+ downgraded: analysis.downgraded.length,
197
+ },
198
+ evidenceLevels: analysis,
199
+ checks,
200
+ limits: [
201
+ 'Evidence level is a proof-strength dimension; it does not replace result/verified/accepted status.',
202
+ 'Missing evidenceLevel is tolerated for historical records but should be added to new records.',
203
+ 'Downgraded evidence is surfaced as a warning path in continue/close gates; project policy decides whether it blocks close.',
204
+ ],
205
+ }
206
+ }
207
+
208
+ const isCli = process.argv[1] && fileURLToPath(import.meta.url) === path.resolve(process.argv[1])
209
+
210
+ if (isCli) {
211
+ const target = targetArg || createFixture()
212
+ const report = audit(target)
213
+
214
+ if (jsonOnly) console.log(JSON.stringify(report, null, 2))
215
+ else console.log(JSON.stringify(report, null, 2))
216
+ if (report.summary.status === 'failed') process.exit(1)
217
+ }
@@ -0,0 +1,206 @@
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
+
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 targetArg = readArg('--target')
17
+ const jsonOnly = args.includes('--json')
18
+
19
+ function readText(filePath) {
20
+ return fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8').replace(/^\uFEFF/, '') : ''
21
+ }
22
+
23
+ function readJsonl(filePath) {
24
+ if (!fs.existsSync(filePath)) return { exists: false, ok: false, records: [], error: 'missing' }
25
+ const lines = readText(filePath)
26
+ .split(/\r?\n/)
27
+ .map((line) => line.trim())
28
+ .filter(Boolean)
29
+ const records = []
30
+ for (const [index, line] of lines.entries()) {
31
+ try {
32
+ records.push(JSON.parse(line))
33
+ } catch (error) {
34
+ return { exists: true, ok: false, records, error: `line ${index + 1}: ${error.message}` }
35
+ }
36
+ }
37
+ return { exists: true, ok: true, records, error: '' }
38
+ }
39
+
40
+ function evidenceFileExists(target, record) {
41
+ return typeof record.evidenceFile === 'string' && fs.existsSync(path.join(target, record.evidenceFile))
42
+ }
43
+
44
+ function classifyRecord(record, target, index) {
45
+ const evidenceLevel = record.evidenceLevel || 'missing'
46
+ const requiredEvidenceLevel = record.requiredEvidenceLevel || 'missing'
47
+ const backfilled = record.evidenceLevelBackfill === 'conservative-historical-default'
48
+ const commands = Array.isArray(record.commands) ? record.commands : []
49
+ const status = record.status || 'unknown'
50
+ const closeableStatus = ['verified', 'accepted'].includes(status)
51
+ const hasEvidenceFile = evidenceFileExists(target, record)
52
+ const hasCommands = commands.length > 0
53
+ const eligibleForStrongerReview = backfilled && closeableStatus && hasEvidenceFile && hasCommands
54
+
55
+ let category = 'not-queued'
56
+ if (evidenceLevel === 'external-required' || requiredEvidenceLevel === 'external-required') {
57
+ category = 'external-required'
58
+ } else if (backfilled && evidenceLevel === 'result') {
59
+ category = 'needs-review'
60
+ } else if (evidenceLevel === 'result' && requiredEvidenceLevel === 'result') {
61
+ category = 'safe-result'
62
+ } else if (!record.evidenceLevel) {
63
+ category = 'needs-review'
64
+ }
65
+
66
+ return {
67
+ index,
68
+ date: record.date || null,
69
+ recordType: record.recordType || null,
70
+ status,
71
+ category,
72
+ evidenceLevel,
73
+ requiredEvidenceLevel,
74
+ backfilled,
75
+ eligibleForStrongerReview,
76
+ evidenceFile: record.evidenceFile || null,
77
+ evidenceFileExists: hasEvidenceFile,
78
+ commandCount: commands.length,
79
+ summary: record.summary || record.recordType || '(unknown evidence record)',
80
+ reviewHint: buildReviewHint({ category, backfilled, eligibleForStrongerReview, hasEvidenceFile, hasCommands }),
81
+ }
82
+ }
83
+
84
+ function buildReviewHint({ category, backfilled, eligibleForStrongerReview, hasEvidenceFile, hasCommands }) {
85
+ if (category === 'external-required') return 'Collect or attach owner, release, CI, marketplace, or host evidence before upgrading this claim.'
86
+ if (eligibleForStrongerReview) return 'Review the referenced evidence file and commands before manually raising this record above result.'
87
+ if (backfilled && !hasEvidenceFile) return 'Keep as result until the referenced evidence file is restored or replaced.'
88
+ if (backfilled && !hasCommands) return 'Keep as result until the original verification command is known.'
89
+ if (category === 'safe-result') return 'No stronger claim is implied; leave as result unless a human review proves otherwise.'
90
+ if (category === 'needs-review') return 'Review this record before using it as stronger proof.'
91
+ return 'No review action required.'
92
+ }
93
+
94
+ export function analyzeEvidenceReviewQueue(records, target) {
95
+ const items = records.map((record, index) => classifyRecord(record, target, index + 1))
96
+ const queued = items.filter((item) => item.category !== 'not-queued')
97
+ const counts = {
98
+ totalRecords: records.length,
99
+ queued: queued.length,
100
+ needsReview: queued.filter((item) => item.category === 'needs-review').length,
101
+ safeResult: queued.filter((item) => item.category === 'safe-result').length,
102
+ eligibleForStrongerReview: queued.filter((item) => item.eligibleForStrongerReview).length,
103
+ externalRequired: queued.filter((item) => item.category === 'external-required').length,
104
+ missingEvidenceFile: queued.filter((item) => !item.evidenceFileExists).length,
105
+ }
106
+ return { counts, queue: queued }
107
+ }
108
+
109
+ function check(id, label, ok, evidence, risk = '') {
110
+ return { id, label, status: ok ? 'passed' : 'failed', evidence, risk }
111
+ }
112
+
113
+ function createFixture() {
114
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gse-evidence-review-'))
115
+ fs.mkdirSync(path.join(dir, '.gse', 'evidence'), { recursive: true })
116
+ fs.writeFileSync(path.join(dir, '.gse', 'evidence', '2026-07-09.md'), '# Evidence\n', 'utf8')
117
+ const records = [
118
+ {
119
+ date: '2026-07-06',
120
+ recordType: 'slice',
121
+ status: 'verified',
122
+ evidenceLevel: 'result',
123
+ requiredEvidenceLevel: 'result',
124
+ evidenceLevelBackfill: 'conservative-historical-default',
125
+ summary: 'Backfilled historical verified record.',
126
+ evidenceFile: '.gse/evidence/2026-07-09.md',
127
+ commands: ['node scripts/old-audit.mjs --json'],
128
+ nextAction: 'Review before stronger claim.',
129
+ },
130
+ {
131
+ date: '2026-07-09',
132
+ recordType: 'note',
133
+ status: 'result',
134
+ evidenceLevel: 'result',
135
+ requiredEvidenceLevel: 'result',
136
+ summary: 'Intentional result-only note.',
137
+ evidenceFile: '.gse/evidence/2026-07-09.md',
138
+ commands: [],
139
+ nextAction: 'Keep as result.',
140
+ },
141
+ {
142
+ date: '2026-07-09',
143
+ recordType: 'release',
144
+ status: 'result',
145
+ evidenceLevel: 'external-required',
146
+ requiredEvidenceLevel: 'accepted-release',
147
+ summary: 'External release gate.',
148
+ evidenceFile: '.gse/evidence/2026-07-09.md',
149
+ commands: ['node scripts/owner-actions.mjs'],
150
+ nextAction: 'Collect owner evidence.',
151
+ },
152
+ ]
153
+ fs.writeFileSync(path.join(dir, '.gse', 'evidence', 'index.jsonl'), records.map((record) => JSON.stringify(record)).join('\n') + '\n', 'utf8')
154
+ return dir
155
+ }
156
+
157
+ function audit(target) {
158
+ const resolvedTarget = path.resolve(target)
159
+ const evidenceIndexPath = path.join(resolvedTarget, '.gse', 'evidence', 'index.jsonl')
160
+ const evidenceIndexBefore = readText(evidenceIndexPath)
161
+ const evidenceIndex = readJsonl(evidenceIndexPath)
162
+ const analysis = analyzeEvidenceReviewQueue(evidenceIndex.records, resolvedTarget)
163
+ const evidenceIndexAfter = readText(evidenceIndexPath)
164
+
165
+ const checks = [
166
+ check('ERQ01', 'evidence index parses before review queue analysis', evidenceIndex.ok, evidenceIndex.ok ? `${evidenceIndex.records.length} record(s)` : evidenceIndex.error),
167
+ check('ERQ02', 'conservative historical result records are queued for review', analysis.counts.needsReview > 0 || evidenceIndex.records.every((record) => record.evidenceLevelBackfill !== 'conservative-historical-default'), `${analysis.counts.needsReview} needs-review record(s)`),
168
+ check('ERQ03', 'review queue does not auto-upgrade or rewrite evidence records', evidenceIndexBefore === evidenceIndexAfter, 'queue-only audit'),
169
+ check('ERQ04', 'queue distinguishes safe result records from historical backfills', analysis.queue.some((item) => item.category === 'safe-result') || evidenceIndex.records.every((record) => !(record.evidenceLevel === 'result' && record.requiredEvidenceLevel === 'result' && !record.evidenceLevelBackfill)), `${analysis.counts.safeResult} safe-result record(s)`),
170
+ check('ERQ05', 'queue exposes stronger-review candidates without changing records', analysis.counts.eligibleForStrongerReview <= analysis.counts.needsReview, `${analysis.counts.eligibleForStrongerReview} eligible-for-stronger-review record(s)`),
171
+ ]
172
+ const passed = checks.filter((item) => item.status === 'passed').length
173
+ const failed = checks.length - passed
174
+ return {
175
+ target: resolvedTarget,
176
+ generatedAt: new Date().toISOString(),
177
+ summary: { status: failed === 0 ? 'passed' : 'failed', passed, failed, total: checks.length },
178
+ workflows: {
179
+ evidenceReviewQueue: failed === 0 ? 'verified' : 'failed',
180
+ historicalResultReview: failed === 0 ? 'visible' : 'failed',
181
+ },
182
+ reviewQueue: {
183
+ ...analysis.counts,
184
+ items: analysis.queue.slice(0, 20),
185
+ },
186
+ checks,
187
+ limits: [
188
+ 'The review queue is diagnostic and does not mutate evidence records.',
189
+ 'Conservative historical result records must be reviewed record by record before claiming stronger proof.',
190
+ 'External-required records need owner, release, CI, marketplace, or host evidence before upgrade.',
191
+ ],
192
+ }
193
+ }
194
+
195
+ const isCli = process.argv[1] && fileURLToPath(import.meta.url) === path.resolve(process.argv[1])
196
+
197
+ if (isCli) {
198
+ const target = targetArg || createFixture()
199
+ const report = audit(target)
200
+ if (!targetArg) fs.rmSync(target, { recursive: true, force: true })
201
+
202
+ if (jsonOnly) console.log(JSON.stringify(report, null, 2))
203
+ else console.log(JSON.stringify(report, null, 2))
204
+
205
+ if (report.summary.status === 'failed') process.exit(1)
206
+ }
@@ -69,29 +69,29 @@ const requiredSections = [
69
69
  '## Anti-Overclaim Rules',
70
70
  '## Next Action',
71
71
  ]
72
- const pendingTerms = [
72
+ const pendingTerms = [
73
73
  ...(publicAcceptanceJson?.pendingGates ?? []).map((gate) => gate.area),
74
74
  'audit-public-acceptance-readiness.mjs',
75
75
  ...(publicAcceptanceJson?.pendingGates ?? []).map((gate) => String(gate.recordCommand ?? '').match(/scripts\/([\w-]+\.mjs)/)?.[1]).filter(Boolean),
76
- ]
76
+ ]
77
77
  const antiOverclaimTerms = [
78
78
  'Do not claim public release acceptance',
79
79
  'Do not claim marketplace availability',
80
80
  'Do not claim native slash-command support',
81
81
  'Do not claim support for a host',
82
82
  ]
83
- const packetUsesCompleteRecordCommandTemplates = !packet.includes('--invocation-status') &&
84
- !/record-[a-z-]+\.mjs[\s\S]*\.\.\./.test(packet) &&
85
- !/record-[a-z-]+\.mjs[^\n`]*[<>]/.test(packet) &&
86
- packet.includes('--status accepted') &&
87
- packet.includes('--dry-run --json')
83
+ const packetUsesCompleteRecordCommandTemplates = (publicAcceptanceJson?.pendingGates?.length ?? 0) === 0 || (!packet.includes('--invocation-status') &&
84
+ !/record-[a-z-]+\.mjs[\s\S]*\.\.\./.test(packet) &&
85
+ !/record-[a-z-]+\.mjs[^\n`]*[<>]/.test(packet) &&
86
+ packet.includes('--status accepted') &&
87
+ packet.includes('--dry-run --json'))
88
88
 
89
89
  const checks = [
90
90
  check('FAP01', 'final acceptance packet generator exists', exists('scripts/generate-final-acceptance-packet.mjs'), 'scripts/generate-final-acceptance-packet.mjs'),
91
91
  check('FAP02', 'generator is based on final readiness audit', generator.includes('audit-final-readiness.mjs') && generator.includes('Pending Acceptance Gates'), 'generator calls final readiness and renders pending gates'),
92
92
  check('FAP03', 'generator produces a packet in a temporary output path', generated.status === 0 && generatedJson?.status === 'ready' && packet.length > 0, generated.stderr || out),
93
93
  check('FAP04', 'packet contains required owner/external gate sections', requiredSections.every((term) => packet.includes(term)), requiredSections.join(', ')),
94
- check('FAP05', 'packet enumerates all current pending final-form gates', pendingTerms.length > 1 && pendingTerms.every((term) => packet.includes(term)), pendingTerms.join(', ')),
94
+ check('FAP05', 'packet enumerates all current pending final-form gates or none are pending', (publicAcceptanceJson?.pendingGates?.length ?? 0) === 0 || (pendingTerms.length > 1 && pendingTerms.every((term) => packet.includes(term))), pendingTerms.join(', ') || 'none'),
95
95
  check('FAP06', 'packet keeps anti-overclaim boundaries explicit', antiOverclaimTerms.every((term) => packet.includes(term)), antiOverclaimTerms.join(', ')),
96
96
  check('FAP07', 'final readiness docs route users to the packet generator', finalReadiness.includes('generate-final-acceptance-packet.mjs'), 'references/final-readiness.md'),
97
97
  check('FAP08', 'consolidated validator includes this audit', validate.includes('audit-final-acceptance-packet.mjs'), 'scripts/validate-gse.mjs'),
@@ -110,7 +110,7 @@ const report = {
110
110
  },
111
111
  limits: [
112
112
  'This audit verifies packet generation and claim boundaries only.',
113
- 'It does not choose a license, approve a security contact, publish a marketplace listing, or prove host-native slash commands.',
113
+ 'It does not choose a license, approve a security contact, publish a marketplace listing, or prove optional host-native slash commands.',
114
114
  ],
115
115
  checks,
116
116
  }
@@ -69,37 +69,38 @@ const generator = read('scripts/generate-final-form-progress-report.mjs')
69
69
  const canonicalMarkdown = read('.gse/acceptance/final-form-progress-report.md')
70
70
  const canonicalReport = parseJson(read('.gse/acceptance/final-form-progress-report.json'))
71
71
 
72
- const blockerNames = new Set((report?.blockers ?? []).map((item) => item.area))
73
- const blockerSetMatchesReportCount = (report?.blockers?.length ?? -1) === report?.readiness?.pendingGateCount &&
74
- (report?.blockers?.length ?? 0) > 0 &&
75
- [...blockerNames].every(Boolean)
72
+ const pendingReleaseEvidence = report?.pendingReleaseEvidence ?? []
73
+ const pendingEvidenceNames = new Set(pendingReleaseEvidence.map((item) => item.area))
74
+ const pendingEvidenceSetMatchesReportCount = pendingReleaseEvidence.length === report?.readiness?.pendingGateCount &&
75
+ [...pendingEvidenceNames].every(Boolean)
76
76
  const verifiedNames = new Set((report?.verifiedCapabilities ?? []).map((item) => item.area))
77
77
  const licenseDecisionResolved = verifiedNames.has('License decision')
78
- const blockerCommandsAreCompleteTemplates = (report?.blockers?.length ?? 0) > 0 &&
79
- report.blockers.every((blocker) =>
80
- !String(blocker.recordCommand ?? '').includes('...') &&
78
+ const pendingEvidenceCommandsAreCompleteTemplates = pendingReleaseEvidence.length === 0 || (
79
+ pendingReleaseEvidence.every((blocker) =>
80
+ !String(blocker.recordCommand ?? '').includes('...') &&
81
81
  !String(blocker.preflightCommand ?? '').includes('...') &&
82
82
  !/[<>]/.test(String(blocker.recordCommand ?? '')) &&
83
83
  !/[<>]/.test(String(blocker.preflightCommand ?? '')) &&
84
84
  !String(blocker.recordCommand ?? '').includes('--invocation-status') &&
85
85
  !String(blocker.preflightCommand ?? '').includes('--invocation-status') &&
86
- (String(blocker.recordCommand ?? '').includes('record-host-invocation.mjs') ? String(blocker.recordCommand).includes('--status accepted') : true),
87
- ) &&
88
- !markdown.includes('--invocation-status') &&
89
- !/record-[a-z-]+\.mjs[^\n`]*[<>]/.test(markdown) &&
90
- !/record-[a-z-]+\.mjs[\s\S]*\.\.\./.test(markdown)
86
+ (String(blocker.recordCommand ?? '').includes('record-host-invocation.mjs') ? String(blocker.recordCommand).includes('--status accepted') : true),
87
+ ) &&
88
+ !markdown.includes('--invocation-status') &&
89
+ !/record-[a-z-]+\.mjs[^\n`]*[<>]/.test(markdown) &&
90
+ !/record-[a-z-]+\.mjs[\s\S]*\.\.\./.test(markdown)
91
+ )
91
92
 
92
93
  const checks = [
93
94
  check('FFP01', 'progress report generator exists and writes markdown/json', generated.status === 0 && generatedData?.status === 'written' && Boolean(report) && markdown.includes('# GSE Final-Form Progress Report'), generated.stderr || `${out}, ${jsonOut}`),
94
95
  check('FFP02', 'report derives status from final readiness, public acceptance, and command dry-run drill audits', generator.includes('audit-final-readiness.mjs') && generator.includes('audit-public-acceptance-readiness.mjs') && generator.includes('audit-public-acceptance-command-dry-run-drill.mjs'), 'generator source audits'),
95
- check('FFP03', 'report separates local engineering and full final-form readiness', report?.scores?.scoringBasis?.includes('local engineering excludes owner-required and external-required') && report?.scores?.localEngineeringReadiness === 100 && report?.scores?.fullFinalFormReadiness < 100 && report?.claimBoundary?.mayClaimPublicAcceptedFinalForm === false, `local=${report?.scores?.localEngineeringReadiness}; full=${report?.scores?.fullFinalFormReadiness}; publicAccepted=${report?.readiness?.publicAccepted}`),
96
- check('FFP04', 'report lists current owner/external blockers', blockerSetMatchesReportCount && licenseDecisionResolved && !blockerNames.has('License decision'), [...blockerNames].join(', ') + '; License decision resolved'),
96
+ check('FFP03', 'report separates portable-core readiness from optional host-native claims', report?.scores?.scoringBasis?.includes('local engineering excludes owner-required and external-required') && report?.scores?.localEngineeringReadiness === 100 && report?.scores?.fullFinalFormReadiness === 100 && report?.claimBoundary?.mayClaimPublicAcceptedFinalForm === true, `local=${report?.scores?.localEngineeringReadiness}; full=${report?.scores?.fullFinalFormReadiness}; publicAccepted=${report?.readiness?.publicAccepted}`),
97
+ check('FFP04', 'report has no pending release evidence after optional host-native claims are not-claimed', pendingEvidenceSetMatchesReportCount && licenseDecisionResolved && !pendingEvidenceNames.has('License decision') && pendingReleaseEvidence.length === 0 && !Object.hasOwn(report ?? {}, 'blockers'), [...pendingEvidenceNames].join(', ') || 'none' + '; License decision resolved'),
97
98
  check('FFP05', 'report lists verified local capabilities', ['Local install', 'npm tarball install', 'URL install', 'Signing', 'Portable command execution', 'Host adapters'].every((name) => verifiedNames.has(name)), 'local engineering capabilities'),
98
99
  check('FFP06', 'report preserves native slash-command boundary', report?.hostRuntime?.nativeSlashCommandRecords === 0 && report?.claimBoundary?.cannotClaim?.some((item) => item.includes('native slash-command')), `native=${report?.hostRuntime?.nativeSlashCommandRecords}`),
99
100
  check('FFP06b', 'report does not list resolved license decision as a cannot-claim item', licenseDecisionResolved && !report?.claimBoundary?.cannotClaim?.some((item) => item.includes('license acceptance')), 'License decision verified; cannotClaim excludes license acceptance'),
100
- check('FFP07', 'human report includes claim boundary and verification commands', markdown.includes('## Claim Boundary') && markdown.includes('## Verification Commands') && markdown.includes('May claim public accepted final form: false') && markdown.includes('audit-public-acceptance-command-dry-run-drill.mjs'), 'markdown sections'),
101
- check('FFP07b', 'report exposes dry-run preflight commands for blockers', (report?.blockers?.length ?? 0) > 0 && report.blockers.every((blocker) => blocker.preflightCommand?.includes('--dry-run --json')) && markdown.includes('Preflight command'), 'blocker preflight commands'),
102
- check('FFP07c', 'report uses complete record command templates', blockerCommandsAreCompleteTemplates, 'no ellipsis, no stale host invocation flag, host records use --status accepted'),
101
+ check('FFP07', 'human report includes claim boundary and verification commands', markdown.includes('## Claim Boundary') && markdown.includes('## Verification Commands') && markdown.includes('May claim public accepted final form: true') && markdown.includes('audit-public-acceptance-command-dry-run-drill.mjs'), 'markdown sections'),
102
+ check('FFP07b', 'report exposes dry-run preflight commands when pending release evidence exists', pendingReleaseEvidence.length === 0 || (pendingReleaseEvidence.every((blocker) => blocker.preflightCommand?.includes('--dry-run --json')) && markdown.includes('Preflight command')), 'pending evidence preflight commands'),
103
+ check('FFP07c', 'report uses complete record command templates', pendingEvidenceCommandsAreCompleteTemplates, 'no ellipsis, no stale host invocation flag, host records use --status accepted'),
103
104
  check('FFP07d', 'canonical progress report carries command dry-run drill evidence', canonicalMarkdown.includes('audit-public-acceptance-command-dry-run-drill.mjs') && canonicalReport?.commandEvidence?.publicAcceptanceCommandDryRunDrill?.command?.includes('audit-public-acceptance-command-dry-run-drill.mjs'), '.gse/acceptance/final-form-progress-report.md, .gse/acceptance/final-form-progress-report.json'),
104
105
  check('FFP08', 'skill routes users to final-form progress report', skill.includes('generate-final-form-progress-report.mjs'), 'SKILL.md'),
105
106
  check('FFP09', 'validator includes final-form progress report audit', validate.includes('audit-final-form-progress-report.mjs'), 'scripts/validate-gse.mjs'),
@@ -120,7 +121,7 @@ const audit = {
120
121
  },
121
122
  limits: [
122
123
  'This audit verifies honest progress reporting from local evidence.',
123
- 'It does not create owner decisions, public CI, public repository settings, registry publication, marketplace approval, or native host evidence.',
124
+ 'It does not create owner decisions, public CI, public repository settings, registry publication, marketplace approval, or native host evidence.',
124
125
  ],
125
126
  checks,
126
127
  }