@gotcos/glasses-server 6.83.0 → 6.85.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.
@@ -1,151 +0,0 @@
1
- #!/usr/bin/env node
2
- // Lens plans mutation gate (6.83.0: lens rounds in Work chat history, Send this plan). Mutations run only in a disposable copy of server/ and shared/; the live
3
- // checkout is never rewritten. Same proof as the autonomous-save gate:
4
- // 0. The target suites are GREEN unmutated (a gate over a red suite proves nothing).
5
- // 1. Each target string appears EXACTLY ONCE in its file. 2. The mutated file differs.
6
- // 3. A NAMED test fails ("FAIL server/x.test.ts > name"); a build error or harness failure is not a kill.
7
- // 4. The file is restored byte for byte before the next mutant.
8
- // Usage: node server/scripts/work-lens-rounds-mutation-gate.mjs [--list | --check] [name…]
9
- import { mkdtempSync, cpSync, readFileSync, writeFileSync, symlinkSync, rmSync } from 'node:fs'
10
- import { tmpdir } from 'node:os'
11
- import { resolve, join, dirname, basename } from 'node:path'
12
- import { fileURLToPath } from 'node:url'
13
- import { spawnSync } from 'node:child_process'
14
-
15
- const root = resolve(dirname(fileURLToPath(import.meta.url)), '../..')
16
- const tests = [
17
- 'server/routes/work-lens-rounds.test.ts',
18
- 'server/routes/work-chat.test.ts',
19
- ]
20
- const LR = 'server/lib/work-lens-rounds.ts', RR = 'server/routes/work-lens-rounds.ts', WR = 'server/routes/work-chat.ts'
21
- const EXPECTED_SURVIVORS = new Set([])
22
- const mutations = [
23
- // The record body.
24
- ['lr-v-gate', LR, " if (v.v !== 1) throw invalid('v')\n", ''],
25
- ['lr-workid', LR, " if (!str(v.workID, 256) || !WORK_ID.test(v.workID)) throw invalid('workID')\n", ''],
26
- ['lr-question-bound', LR, " if (!str(v.question, LENS_QUESTION_CHARS) || !v.question.trim()) throw invalid('question')\n", ''],
27
- ['lr-answer-bound', LR, "if (!str(v.answer, LENS_ANSWER_CHARS)) throw invalid('answer')", "if (typeof v.answer !== 'string') throw invalid('answer')"],
28
- ['lr-source-lens', LR, " if (v.source !== 'lens') throw invalid('source')\n", ''],
29
- ['lr-ids-in-order', LR, "a.id !== `a${i + 1}`", "typeof a.id !== 'string'"],
30
- ['lr-continue-needs-session', LR, " if (mode !== 'newSession' && !(str(a.sessionID, 64) && SID.test(a.sessionID))) throw invalid('assignments.sessionID')\n", ''],
31
- ['lr-new-session-provider', LR, "mode === 'newSession' ? !['claude', 'codex'].includes(a.provider)", "mode === 'newSession' ? false"],
32
- ['lr-prompt-bound', LR, "!str(a.prompt, LENS_PROMPT_CHARS)", "typeof a.prompt !== 'string'"],
33
- ['lr-assignment-cap', LR, "!Array.isArray(v.assignments) || v.assignments.length > WORK_CHAT_MAX_ASSIGNMENTS) throw invalid('assignments')", "!Array.isArray(v.assignments)) throw invalid('assignments')"],
34
- // Plan stale.
35
- ['lr-stale-size', LR, "return after.length !== before.size || after.some(s => !before.has(s))", "return after.some(s => !before.has(s))"],
36
- ['lr-stale-members', LR, "return after.length !== before.size || after.some(s => !before.has(s))", "return after.length !== before.size"],
37
- ['lr-stale-own-excluded', LR, " const after = now.filter(s => !own.has(s))\n", " const after = now\n"],
38
- ['lr-stale-unknown', LR, " if (!round.cardSessions || !now) return false\n", ''],
39
- ['lr-card-fork-source', LR, "if (r.sessionID && !(r.mode === 'fork' && r.sessionID === r.sourceSessionID)) out.add(r.sessionID)", "if (r.sessionID) out.add(r.sessionID)"],
40
- ['lr-card-fork-child', LR, " if (r.forkChildID) out.add(r.forkChildID)\n", ''],
41
- // Outcomes, expiry and bounds.
42
- ['lr-outcome-partly', LR, " if (sent > 0) return 'partly_sent'\n", ''],
43
- ['lr-outcome-stopped', LR, " if (states.every(s => s === 'stopped')) return 'stopped'\n", ''],
44
- ['lr-outcome-failed', LR, "return states.every(s => s === 'refused' || s === 'stopped') ? 'refused' : 'failed'", "return 'refused'"],
45
- // COS Control's own Send on the Mac (QA 6.10.651 blocker: a later lens send must never deliver twice).
46
- ['lr-already-held', LR, "const ALREADY: readonly AssignmentState[] = ['sent', 'delivered', 'held']", "const ALREADY: readonly AssignmentState[] = ['sent', 'delivered']"],
47
- ['lr-already-lists-ids', LR, "{ assignmentIds: r.assignments.map(a => a.id).filter(sentAlready) }", "{}"],
48
- ['lr-mac-records', LR, " r.mac = { at: now, ids: [...new Set([...(r.mac?.ids ?? []), ...results.map(x => x.id)])] }\n", ''],
49
- ['lr-mac-view', LR, " if (r.mac && (!send || r.mac.at >= send.requestedAt)) {", " if (false) {"],
50
- ['lr-mac-partial', LR, " if (state === 'sent' && r.mac.ids.length < r.assignments.length) state = 'partly_sent'\n", ''],
51
- ['rr-local-mac', RR, " local(req)\n const round = deps.store.reportMac", " const round = deps.store.reportMac"],
52
- ['lr-settle-terminal', LR, "if (states.every(s => TERMINAL.includes(s)))", "if (true)"],
53
- ['lr-pickup-expiry', LR, "now - send.requestedAt > LENS_SEND_PICKUP_MS", "false"],
54
- ['lr-report-timeout', LR, "now - send.pickedUpAt > LENS_SEND_REPORT_MS", "false"],
55
- ['lr-ttl', LR, "const kept = this.file.rounds.filter(r => now - r.recordedAt <= LENS_ROUND_TTL_MS)", "const kept = this.file.rounds"],
56
- ['lr-cap-200', LR, " while (this.file.rounds.length > LENS_ROUNDS_MAX) {", " while (false) {"],
57
- ['lr-cap-keeps-active', LR, "this.file.rounds.splice(idle >= 0 ? idle : 0, 1)", "this.file.rounds.splice(0, 1)"],
58
- ['lr-idempotent', LR, " if (existing) return { round: existing, created: false }\n", ''],
59
- ['lr-unreadable-aside', LR, " try { renameSync(path, aside) } catch { /* nothing to move */ }\n", ''],
60
- ['lr-test-store-in-memory', LR, "process.env.VITEST || process.env.NODE_ENV === 'test' ? null : ", ''],
61
- // Send rules.
62
- ['lr-in-progress', LR, " if (this.active(r)) throw new LensRoundError('send_in_progress', 409, 'This plan is already being sent.')\n", ''],
63
- ['lr-already-sent', LR, "if (!ids.length || ids.some(sentAlready))", "if (!ids.length)"],
64
- ['lr-whole-plan-skips-sent', LR, ".map(a => a.id).filter(id => !sentAlready(id))", ".map(a => a.id)"],
65
- ['lr-confirm-required', LR, "if (body.confirm !== 'two-step' && !r.autoSendCard)", "if (false)"],
66
- ['lr-auto-send-card', LR, "if (body.confirm !== 'two-step' && !r.autoSendCard)", "if (body.confirm !== 'two-step')"],
67
- ['lr-control-required', LR, " if (!checks.controlConnected) throw new LensRoundError('control_not_connected', 423,", " if (false) throw new LensRoundError('control_not_connected', 423,"],
68
- ['lr-stop-control-required', LR, " if (!controlConnected) throw new LensRoundError('control_not_connected', 423, 'COS Control is not connected. Stop it from the Mac.')\n", ''],
69
- ['lr-fresh-60s', LR, "connected: age <= LENS_CONTROL_FRESH_MS", "connected: true"],
70
- // The inbox.
71
- ['lr-pickup-once', LR, "s.state === 'send_requested') { s.state = 'sending';", "(s.state === 'send_requested' || s.state === 'sending')) { s.state = 'sending';"],
72
- ['lr-stop-once', LR, "if (room > 0 && s.pickedUpAt === null)", "if (room > 0)"],
73
- ['lr-inbox-cap', LR, "let room = LENS_INBOX_MAX, changed = false", "let room = Infinity, changed = false"],
74
- ['lr-inbox-since', LR, "!r.inHistory && r.seq > since", "!r.inHistory"],
75
- ['lr-inbox-history', LR, "!r.inHistory && r.seq > since", "r.seq > since"],
76
- ['lr-report-picked-up', LR, " if (send.state === 'send_requested' || send.state === 'expired') throw new LensRoundError('send_not_picked_up'", " if (false) throw new LensRoundError('send_not_picked_up'"],
77
- ['lr-view-hides-prompts', LR, "assignments: r.assignments.map(a => ({ id: a.id, state: a.state,", "assignments: r.assignments.map(a => ({ id: a.id, prompt: a.prompt, state: a.state,"],
78
- // Routes.
79
- ['rr-known-work', RR, " if (!known) throw new LensRoundError('unknown_work', 404,", " if (false) throw new LensRoundError('unknown_work', 404,"],
80
- ['rr-known-snapshot-first', RR, " if (s && (s.links.some(l => l.workID === workID) || s.receipts.some(r => r.workID === workID))) return true\n", ''],
81
- ['rr-body-cap', RR, "> LENS_ROUND_BODY_BYTES) {", "> Infinity) {"],
82
- ['rr-201-vs-200', RR, "res.status(created ? 201 : 200)", "res.status(201)"],
83
- ['rr-admissions', RR, " if (!deps.admissions()) return res.status(503).json(MAINTENANCE)\n", ''],
84
- ['rr-lease-maintenance', RR, " try { lease = deps.lease() } catch { return res.status(503).json(MAINTENANCE) }", " lease = deps.lease()"],
85
- ['rr-lease-released', RR, ".finally(() => lease.release())", ""],
86
- ['rr-local-inbox', RR, " local(req)\n const { since, autoSend } = req.query", " const { since, autoSend } = req.query"],
87
- ['rr-local-history', RR, " local(req)\n const roundId = roundIdOf(req)\n const b = req.body", " const roundId = roundIdOf(req)\n const b = req.body"],
88
- ['rr-local-result', RR, " local(req)\n const id = String(req.params.sendRequestId", " const id = String(req.params.sendRequestId"],
89
- ['rr-card-sessions-at-send', RR, "cardSessions: cardSessionsOf(deps.snapshot(), round.workID), controlConnected", "cardSessions: null, controlConnected"],
90
- ['rr-card-sessions-at-record', RR, "deps.store.record(input, cardSessionsOf(deps.snapshot(), input.workID))", "deps.store.record(input, null)"],
91
- ['rr-history-autosend', RR, " const round = deps.store.markInHistory(roundId, b.autoSendCard)", " const round = deps.store.markInHistory(roundId, undefined)"],
92
- ['rr-inbox-autosend', RR, "deps.store.inbox(since === undefined ? 0 : Number(since), autoSend === '1')", "deps.store.inbox(since === undefined ? 0 : Number(since), false)"],
93
- // The capability.
94
- ['wr-lens-plans-capability', WR, ",\n lensPlans:1,lensPlansControl:deps.lensPlansControl()}))", "}))"],
95
- ]
96
-
97
- const args = process.argv.slice(2)
98
- if (args.includes('--list')) { for (const [name] of mutations) console.log(name); process.exit(0) }
99
- const names = args.filter(a => !a.startsWith('--'))
100
- const selected = names.length ? mutations.filter(([name]) => names.includes(name)) : mutations
101
- if (names.length && selected.length !== names.length) throw new Error('Unknown mutation name in: ' + args.join(' '))
102
- if (args.includes('--check')) {
103
- let bad = 0
104
- for (const [name, file, find, replacement] of mutations) {
105
- const text = readFileSync(join(root, file), 'utf8'), count = text.split(find).length - 1
106
- if (count !== 1 || find === replacement) { bad++; console.log(`${name}: ${count} matches in ${file}${find === replacement ? ' (no change)' : ''}`) }
107
- }
108
- console.log(`${mutations.length - bad} of ${mutations.length} targets unique`); process.exit(bad ? 1 : 0)
109
- }
110
- const scratch = mkdtempSync(join(tmpdir(), 'work-lens-rounds-mutations-'))
111
- try {
112
- const skip = new Set(['data', 'models', 'certs', 'node_modules'])
113
- cpSync(join(root, 'server'), join(scratch, 'server'), { recursive: true, filter: src => !(skip.has(basename(src)) && dirname(src) === join(root, 'server')) })
114
- cpSync(join(root, 'shared'), join(scratch, 'shared'), { recursive: true })
115
- cpSync(join(root, 'bin'), join(scratch, 'bin'), { recursive: true })
116
- cpSync(join(root, 'vitest.config.ts'), join(scratch, 'vitest.config.ts'))
117
- cpSync(join(root, 'package.json'), join(scratch, 'package.json'))
118
- symlinkSync(join(root, 'node_modules'), join(scratch, 'node_modules'), 'dir')
119
- const run = () => spawnSync(process.execPath, [join(root, 'node_modules/vitest/vitest.mjs'), 'run', '--maxWorkers=1', ...tests], {
120
- cwd: scratch,
121
- env: { PATH: process.env.PATH, HOME: scratch, COS_DATA_DIR: join(scratch, 'data'), COS_PROFILE_PATH: join(scratch, 'data', 'profile.json'),
122
- COS_MAINTENANCE_GATE_PATH: join(scratch, 'gate', 'maintenance-gate.json') },
123
- encoding: 'utf8', timeout: 300000,
124
- })
125
- const baseline = run()
126
- if (baseline.status !== 0) throw new Error('Baseline failed; a gate over a red suite proves nothing:\n' + baseline.stdout + '\n' + baseline.stderr)
127
- const totals = /Tests\s+(\d+) passed/.exec(baseline.stdout)
128
- console.log(`baseline PASS (${totals ? totals[1] : '?'} tests in ${tests.length} files)`)
129
- const survived = []
130
- for (const [name, file, find, replacement] of selected) {
131
- const target = join(scratch, file), original = readFileSync(target, 'utf8')
132
- if (original.split(find).length !== 2) throw new Error(`Mutation ${name} does not match exactly once in ${file}`)
133
- // A function replacement: `$&` and `$1` in a mutant are literal text, never replace() patterns.
134
- const mutated = original.replace(find, () => replacement)
135
- if (mutated === original) throw new Error(`Mutation ${name} did not change ${file}`)
136
- writeFileSync(target, mutated)
137
- const result = run(), output = result.stdout + '\n' + result.stderr
138
- writeFileSync(target, original)
139
- if (readFileSync(target, 'utf8') !== original) throw new Error(`Restore failed for ${file} after ${name}`)
140
- const killers = [...new Set([...output.matchAll(/FAIL\s+(server\/\S+\.test\.ts > [^\n]+)/g)].map(m => m[1].trim()))]
141
- if (result.status === 0 || result.error || !killers.length) {
142
- const expected = EXPECTED_SURVIVORS.has(name) && result.status === 0
143
- if (!expected) survived.push(name)
144
- console.log(`${name} ${expected ? 'SURVIVED (expected, documented)' : 'SURVIVED'}${result.error ? ' (harness: ' + result.error.message + ')' : ''}`); continue
145
- }
146
- console.log(`${name} KILLED by ${killers.slice(0, 2).join(' | ')}${killers.length > 2 ? ` (+${killers.length - 2} more)` : ''}`)
147
- }
148
- const expectedCount = selected.filter(([n]) => EXPECTED_SURVIVORS.has(n)).length
149
- console.log(`${selected.length - survived.length - expectedCount} of ${selected.length} lens-plans mutations killed by a named test; ${expectedCount} expected survivors`)
150
- if (survived.length) { console.log('SURVIVED: ' + survived.join(', ')); process.exitCode = 1 }
151
- } finally { rmSync(scratch, { recursive: true, force: true }) }