@fprad0/skill-master-mcp 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -3
- package/README.md +2 -2
- package/VERSION.md +4 -4
- package/bin/lib/client-config.mjs +11 -9
- package/bin/lib/menu-core.mjs +241 -70
- package/bin/skill-master-activation.mjs +3 -1
- package/bin/skill-master-menu.mjs +127 -17
- package/bin/skill-master-success-skills.mjs +52 -2
- package/docs/operations/assets/menu-frame-compact.html +78 -75
- package/docs/operations/assets/menu-frame-dna-hero.html +87 -0
- package/docs/operations/assets/menu-frame-fine-helix.html +89 -0
- package/docs/operations/assets/menu-frame-large.html +86 -83
- package/docs/operations/assets/menu-frame-running.html +82 -79
- package/docs/operations/assets/menu-frame-score-10-contact-sheet.html +184 -0
- package/docs/planning/mcp-1.0.0/00_RESUMO_EXECUTIVO_AUDITORIA_MENU.md +118 -0
- package/docs/planning/mcp-1.0.0/01_MATRIZ_TESTES_MENU_E_RESULTADOS.md +250 -0
- package/docs/planning/mcp-1.0.0/02_PLANO_CORRECAO_ATIVAR_SKILL_APRENDIDA.md +200 -0
- package/docs/planning/mcp-1.0.0/03_PLANO_COMPATIBILIDADE_WINDOWS_LINUX_MACOS.md +167 -0
- package/docs/planning/mcp-1.0.0/04_PLANO_UI_CYBERPUNK_PIXEL_ART_E_PERFORMANCE.md +165 -0
- package/docs/planning/mcp-1.0.0/05_PROMPT_TASK_EXECUCAO_CORRECOES.md +151 -0
- package/docs/planning/mcp-1.0.0/06_CHECKLIST_REGRESSAO_PRE_RELEASE.md +159 -0
- package/docs/planning/mcp-1.0.0/07_RELATORIO_APLICACAO_CORRECOES_MENU_SKILL_MASTER.md +136 -0
- package/docs/planning/mcp-1.0.0/08_AUDITORIA_CRITICA_MENU_NOTA_E_DNA_REFINADO.md +184 -0
- package/docs/planning/mcp-1.0.0/prompt-tasks-nota-10-10/00_PROMPT_TASK_MASTER_NOTA_10_10.md +103 -0
- package/docs/planning/mcp-1.0.0/prompt-tasks-nota-10-10/01_PROMPT_TASK_FINE_HELIX_DNA.md +116 -0
- package/docs/planning/mcp-1.0.0/prompt-tasks-nota-10-10/02_PROMPT_TASK_DNA_HERO_BOOT_AND_MOTION.md +109 -0
- package/docs/planning/mcp-1.0.0/prompt-tasks-nota-10-10/03_PROMPT_TASK_MENU_UX_HELP_ERROR_COPY.md +99 -0
- package/docs/planning/mcp-1.0.0/prompt-tasks-nota-10-10/04_PROMPT_TASK_EVIDENCE_RENDERER_1_0_0.md +97 -0
- package/docs/planning/mcp-1.0.0/prompt-tasks-nota-10-10/05_PROMPT_TASK_CROSS_PLATFORM_UTF8_MOJIBAKE.md +99 -0
- package/docs/planning/mcp-1.0.0/prompt-tasks-nota-10-10/06_PROMPT_TASK_VISUAL_REGRESSION_QA.md +105 -0
- package/docs/planning/mcp-1.0.0/prompt-tasks-nota-10-10/07_PROMPT_TASK_PRE_RELEASE_SCORE_GATE_10_10.md +104 -0
- package/docs/planning/mcp-1.0.0/prompt-tasks-nota-10-10/README_ORDEM_EXECUCAO_NOTA_10_10.md +77 -0
- package/manifests/channels/beta.json +7 -7
- package/manifests/channels/stable.json +8 -8
- package/package.json +16 -14
- package/scripts/render-menu-evidence.mjs +115 -49
- package/scripts/verify-menu-actions.mjs +13 -8
- package/scripts/verify-menu-visual.mjs +90 -0
|
@@ -1,54 +1,54 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
4
4
|
import { dirname, join, resolve } from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
buildMenuCommands,
|
|
8
|
+
formatCyberMenuFrame,
|
|
9
|
+
formatRunningActionFrame,
|
|
10
|
+
formatSkillMasterDnaHeroFrame,
|
|
11
|
+
getMenuStatus,
|
|
12
|
+
} from '../bin/lib/menu-core.mjs';
|
|
7
13
|
|
|
8
14
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
9
15
|
const rootDir = resolve(here, '..');
|
|
10
16
|
const outputDir = join(rootDir, 'docs', 'operations', 'assets');
|
|
17
|
+
const packageJson = JSON.parse(readFileSync(join(rootDir, 'package.json'), 'utf8'));
|
|
18
|
+
const liveStatus = getMenuStatus(rootDir);
|
|
11
19
|
|
|
12
20
|
const commands = buildMenuCommands({
|
|
13
|
-
rootDir
|
|
14
|
-
currentFile: '
|
|
15
|
-
nodeExecPath:
|
|
21
|
+
rootDir,
|
|
22
|
+
currentFile: join(rootDir, 'bin', 'skill-master-menu.mjs'),
|
|
23
|
+
nodeExecPath: process.execPath,
|
|
16
24
|
});
|
|
17
25
|
|
|
18
26
|
const baseStatus = {
|
|
19
|
-
|
|
20
|
-
semver:
|
|
21
|
-
|
|
22
|
-
manifestSemver: '0.0.12',
|
|
27
|
+
...liveStatus,
|
|
28
|
+
semver: packageJson.version,
|
|
29
|
+
manifestSemver: packageJson.version,
|
|
23
30
|
versionText: '',
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
studyCandidates: 8,
|
|
31
|
+
pendingSuccessDrafts: Math.max(3, liveStatus.pendingSuccessDrafts),
|
|
32
|
+
studyCandidates: Math.max(8, liveStatus.studyCandidates),
|
|
27
33
|
globalReadiness: {
|
|
34
|
+
...liveStatus.globalReadiness,
|
|
28
35
|
ready: true,
|
|
29
36
|
mode: 'ready',
|
|
30
|
-
required:
|
|
31
|
-
installed: Array.from(
|
|
37
|
+
required: liveStatus.globalReadiness.required,
|
|
38
|
+
installed: Array.from(
|
|
39
|
+
{ length: liveStatus.globalReadiness.required },
|
|
40
|
+
(_, index) => liveStatus.globalReadiness.installed[index] ?? `skill-${index + 1}`,
|
|
41
|
+
),
|
|
32
42
|
missing: [],
|
|
33
|
-
codex: { present: true, kind: '
|
|
34
|
-
claude: { present: true, globalCommand: true },
|
|
35
|
-
gemini: { present: true, globalCommand: true },
|
|
36
|
-
antigravity: { present: true, globalCommand: true },
|
|
37
|
-
},
|
|
38
|
-
bundledCatalog: {
|
|
39
|
-
total: 66,
|
|
40
|
-
categories: [
|
|
41
|
-
{ key: 'moral', label: 'moral-core', count: 10 },
|
|
42
|
-
{ key: 'frontend', label: 'frontend-ui', count: 22 },
|
|
43
|
-
{ key: 'backend', label: 'backend-data', count: 19 },
|
|
44
|
-
{ key: 'ops', label: 'ops-clients', count: 12 },
|
|
45
|
-
{ key: 'knowledge', label: 'knowledge', count: 2 },
|
|
46
|
-
],
|
|
43
|
+
codex: { present: true, kind: 'absolute-node', globalCommand: true },
|
|
44
|
+
claude: { present: true, kind: 'absolute-node', globalCommand: true },
|
|
45
|
+
gemini: { present: true, kind: 'absolute-node', globalCommand: true },
|
|
46
|
+
antigravity: { present: true, kind: 'absolute-node', globalCommand: true },
|
|
47
47
|
},
|
|
48
48
|
};
|
|
49
49
|
|
|
50
50
|
function stripAnsi(text) {
|
|
51
|
-
return String(text).replace(/\x1b\[[0-9;?]*[
|
|
51
|
+
return String(text).replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, '');
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
function escapeHtml(text) {
|
|
@@ -67,12 +67,14 @@ function renderPage(title, frame) {
|
|
|
67
67
|
<style>
|
|
68
68
|
:root {
|
|
69
69
|
color-scheme: dark;
|
|
70
|
-
--bg: #
|
|
71
|
-
--panel: #
|
|
72
|
-
--grid: rgba(
|
|
73
|
-
--line: rgba(
|
|
74
|
-
--text: #
|
|
75
|
-
--muted: #
|
|
70
|
+
--bg: #050708;
|
|
71
|
+
--panel: #0b1113;
|
|
72
|
+
--grid: rgba(112, 230, 229, 0.08);
|
|
73
|
+
--line: rgba(112, 230, 229, 0.34);
|
|
74
|
+
--text: #ecffff;
|
|
75
|
+
--muted: #9aa8ad;
|
|
76
|
+
--cyan: #70e6e5;
|
|
77
|
+
--amber: #e5c27a;
|
|
76
78
|
}
|
|
77
79
|
* { box-sizing: border-box; }
|
|
78
80
|
body {
|
|
@@ -81,23 +83,24 @@ function renderPage(title, frame) {
|
|
|
81
83
|
background:
|
|
82
84
|
linear-gradient(var(--grid) 1px, transparent 1px),
|
|
83
85
|
linear-gradient(90deg, var(--grid) 1px, transparent 1px),
|
|
84
|
-
radial-gradient(circle at
|
|
86
|
+
radial-gradient(circle at 70% 10%, rgba(112,230,229,0.13), transparent 36%),
|
|
87
|
+
radial-gradient(circle at 20% 80%, rgba(229,194,122,0.09), transparent 30%),
|
|
85
88
|
var(--bg);
|
|
86
|
-
background-size:
|
|
89
|
+
background-size: 42px 42px, 42px 42px, auto, auto, auto;
|
|
87
90
|
color: var(--text);
|
|
88
91
|
font-family: "Courier New", monospace;
|
|
89
92
|
padding: 32px;
|
|
90
93
|
}
|
|
91
94
|
.frame {
|
|
92
95
|
max-width: max-content;
|
|
93
|
-
background: linear-gradient(180deg, rgba(
|
|
96
|
+
background: linear-gradient(180deg, rgba(112,230,229,0.05), rgba(255,255,255,0.01));
|
|
94
97
|
border: 1px solid var(--line);
|
|
95
|
-
box-shadow: 0 0 0 1px rgba(
|
|
98
|
+
box-shadow: 0 0 0 1px rgba(112,230,229,0.08), 0 24px 64px rgba(0,0,0,0.48);
|
|
96
99
|
padding: 20px 24px;
|
|
97
100
|
white-space: pre;
|
|
98
101
|
line-height: 1.1;
|
|
99
102
|
font-size: 13px;
|
|
100
|
-
border-radius:
|
|
103
|
+
border-radius: 10px;
|
|
101
104
|
}
|
|
102
105
|
.title {
|
|
103
106
|
margin: 0 0 16px;
|
|
@@ -115,16 +118,79 @@ function renderPage(title, frame) {
|
|
|
115
118
|
</html>`;
|
|
116
119
|
}
|
|
117
120
|
|
|
121
|
+
function renderContactSheet(items) {
|
|
122
|
+
const cards = items.map(({ title, frame }) => `
|
|
123
|
+
<section class="card">
|
|
124
|
+
<h2>${escapeHtml(title)}</h2>
|
|
125
|
+
<pre>${escapeHtml(stripAnsi(frame))}</pre>
|
|
126
|
+
</section>`).join('\n');
|
|
127
|
+
|
|
128
|
+
return `<!doctype html>
|
|
129
|
+
<html lang="pt-BR">
|
|
130
|
+
<head>
|
|
131
|
+
<meta charset="utf-8" />
|
|
132
|
+
<title>Skill Master Menu Evidence Contact Sheet</title>
|
|
133
|
+
<style>
|
|
134
|
+
:root { color-scheme: dark; --bg: #050708; --line: rgba(112,230,229,0.3); --text: #ecffff; --muted: #9aa8ad; }
|
|
135
|
+
body { margin: 0; background: var(--bg); color: var(--text); font-family: "Courier New", monospace; padding: 24px; }
|
|
136
|
+
h1 { font-size: 16px; letter-spacing: .16em; text-transform: uppercase; color: var(--muted); }
|
|
137
|
+
.grid { display: grid; gap: 18px; grid-template-columns: repeat(auto-fit, minmax(360px, 1fr)); align-items: start; }
|
|
138
|
+
.card { border: 1px solid var(--line); border-radius: 10px; padding: 14px; background: rgba(112,230,229,.035); overflow: auto; }
|
|
139
|
+
h2 { font-size: 12px; color: var(--muted); margin: 0 0 10px; text-transform: uppercase; letter-spacing: .1em; }
|
|
140
|
+
pre { margin: 0; white-space: pre; line-height: 1.08; font-size: 10px; }
|
|
141
|
+
</style>
|
|
142
|
+
</head>
|
|
143
|
+
<body>
|
|
144
|
+
<h1>Skill Master Menu Evidence - ${escapeHtml(packageJson.version)}</h1>
|
|
145
|
+
<main class="grid">${cards}</main>
|
|
146
|
+
</body>
|
|
147
|
+
</html>`;
|
|
148
|
+
}
|
|
149
|
+
|
|
118
150
|
mkdirSync(outputDir, { recursive: true });
|
|
119
151
|
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
152
|
+
const frames = [
|
|
153
|
+
{
|
|
154
|
+
file: 'menu-frame-large.html',
|
|
155
|
+
title: 'Menu Frame 120x32',
|
|
156
|
+
frame: formatCyberMenuFrame(baseStatus, commands, 1, 8, { columns: 120, rows: 32, useColor: false }),
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
file: 'menu-frame-compact.html',
|
|
160
|
+
title: 'Menu Frame 96x24',
|
|
161
|
+
frame: formatCyberMenuFrame(baseStatus, commands, 20, 8, { columns: 96, rows: 24, useColor: false }),
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
file: 'menu-frame-running.html',
|
|
165
|
+
title: 'Menu Running State',
|
|
166
|
+
frame: formatRunningActionFrame(baseStatus, commands.find((command) => command.key === 'doctor'), 5, { columns: 120, rows: 28, useColor: false }),
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
file: 'menu-frame-dna-hero.html',
|
|
170
|
+
title: 'DNA Hero Boot',
|
|
171
|
+
frame: formatSkillMasterDnaHeroFrame(baseStatus, 5, { columns: 120, rows: 32, useColor: false }),
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
file: 'menu-frame-fine-helix.html',
|
|
175
|
+
title: 'Fine Helix Focus',
|
|
176
|
+
frame: formatCyberMenuFrame(baseStatus, commands, commands.findIndex((command) => command.key === 'promptRecommendation'), 15, { columns: 120, rows: 34, useColor: false }),
|
|
177
|
+
},
|
|
178
|
+
];
|
|
123
179
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
180
|
+
for (const item of frames) {
|
|
181
|
+
if (item.frame.includes('0.0.12')) {
|
|
182
|
+
throw new Error(`${item.file} contains stale version 0.0.12`);
|
|
183
|
+
}
|
|
184
|
+
writeFileSync(join(outputDir, item.file), renderPage(item.title, item.frame), 'utf8');
|
|
185
|
+
}
|
|
127
186
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
187
|
+
writeFileSync(
|
|
188
|
+
join(outputDir, 'menu-frame-score-10-contact-sheet.html'),
|
|
189
|
+
renderContactSheet(frames),
|
|
190
|
+
'utf8',
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
for (const item of frames) {
|
|
194
|
+
console.log(join(outputDir, item.file));
|
|
195
|
+
}
|
|
196
|
+
console.log(join(outputDir, 'menu-frame-score-10-contact-sheet.html'));
|
|
@@ -54,12 +54,16 @@ const READ_ONLY_ACTIONS = [
|
|
|
54
54
|
'status',
|
|
55
55
|
'doctor',
|
|
56
56
|
'publicNpm',
|
|
57
|
-
'activationStatus',
|
|
58
|
-
'successNotifications',
|
|
59
|
-
'studySkills',
|
|
60
|
-
'
|
|
61
|
-
|
|
62
|
-
|
|
57
|
+
'activationStatus',
|
|
58
|
+
'successNotifications',
|
|
59
|
+
'studySkills',
|
|
60
|
+
'notionSummary',
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
const WRITES_LOCAL_ACTIONS = [
|
|
64
|
+
'approvalPackage',
|
|
65
|
+
'activateLearnedLocal',
|
|
66
|
+
];
|
|
63
67
|
|
|
64
68
|
const commands = buildMenuCommands({
|
|
65
69
|
rootDir,
|
|
@@ -96,8 +100,9 @@ for (const key of READ_ONLY_ACTIONS) {
|
|
|
96
100
|
console.log('Skill Master menu action verification');
|
|
97
101
|
console.log(`- Actions: ${commands.length}`);
|
|
98
102
|
console.log(`- Required actions: ${REQUIRED_ACTIONS.length}`);
|
|
99
|
-
console.log(`- Read-only smoke candidates: ${READ_ONLY_ACTIONS.join(', ')}`);
|
|
100
|
-
console.log(`-
|
|
103
|
+
console.log(`- Read-only smoke candidates: ${READ_ONLY_ACTIONS.join(', ')}`);
|
|
104
|
+
console.log(`- Local write actions: ${WRITES_LOCAL_ACTIONS.join(', ')}`);
|
|
105
|
+
console.log(`- Confirmation protected: ${CONFIRMATION_REQUIRED.join(', ')}`);
|
|
101
106
|
console.log('');
|
|
102
107
|
|
|
103
108
|
for (const command of commands) {
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { readFileSync } from 'node:fs';
|
|
4
|
+
import { dirname, join, resolve } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import {
|
|
7
|
+
buildMenuCommands,
|
|
8
|
+
formatCyberMenuFrame,
|
|
9
|
+
formatRunningActionFrame,
|
|
10
|
+
formatSkillMasterDnaHeroFrame,
|
|
11
|
+
getMenuStatus,
|
|
12
|
+
} from '../bin/lib/menu-core.mjs';
|
|
13
|
+
|
|
14
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
15
|
+
const rootDir = resolve(here, '..');
|
|
16
|
+
const packageJson = JSON.parse(readFileSync(join(rootDir, 'package.json'), 'utf8'));
|
|
17
|
+
const commands = buildMenuCommands({
|
|
18
|
+
rootDir,
|
|
19
|
+
currentFile: join(rootDir, 'bin', 'skill-master-menu.mjs'),
|
|
20
|
+
nodeExecPath: process.execPath,
|
|
21
|
+
});
|
|
22
|
+
const liveStatus = getMenuStatus(rootDir);
|
|
23
|
+
const status = {
|
|
24
|
+
...liveStatus,
|
|
25
|
+
semver: packageJson.version,
|
|
26
|
+
manifestSemver: packageJson.version,
|
|
27
|
+
versionText: '',
|
|
28
|
+
globalReadiness: {
|
|
29
|
+
...liveStatus.globalReadiness,
|
|
30
|
+
ready: true,
|
|
31
|
+
mode: 'ready',
|
|
32
|
+
missing: [],
|
|
33
|
+
installed: Array.from(
|
|
34
|
+
{ length: liveStatus.globalReadiness.required },
|
|
35
|
+
(_, index) => liveStatus.globalReadiness.installed[index] ?? `skill-${index + 1}`,
|
|
36
|
+
),
|
|
37
|
+
codex: { present: true, kind: 'absolute-node', globalCommand: true },
|
|
38
|
+
claude: { present: true, kind: 'absolute-node', globalCommand: true },
|
|
39
|
+
gemini: { present: true, kind: 'absolute-node', globalCommand: true },
|
|
40
|
+
antigravity: { present: true, kind: 'absolute-node', globalCommand: true },
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
function stripAnsi(text) {
|
|
45
|
+
return String(text).replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, '');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function assert(condition, message) {
|
|
49
|
+
if (!condition) throw new Error(message);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function assertMaxWidth(frame, width, label) {
|
|
53
|
+
for (const [index, line] of stripAnsi(frame).split('\n').entries()) {
|
|
54
|
+
assert(line.length <= width, `${label} line ${index + 1} exceeds ${width}: ${line.length}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function assertNoHeavyUnicode(frame, label) {
|
|
59
|
+
const plain = stripAnsi(frame);
|
|
60
|
+
assert(!/[┏┓┗┛╭╮╰╯╔╗╚╝┌┐└┘╾╼╴╶─═╍│▌▐▰▱█░▁▂▃▄▅▆▇·◢\u2800-\u28ff]/u.test(plain), `${label} contains premium Unicode in ASCII mode`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const large = formatCyberMenuFrame(status, commands, 1, 8, { columns: 120, rows: 32, useColor: false });
|
|
64
|
+
const compact = formatCyberMenuFrame(status, commands, 20, 8, { columns: 96, rows: 24, useColor: false });
|
|
65
|
+
const running = formatRunningActionFrame(status, commands.find((command) => command.key === 'doctor'), 5, { columns: 120, rows: 28, useColor: false });
|
|
66
|
+
const hero = formatSkillMasterDnaHeroFrame(status, 5, { columns: 120, rows: 32, useColor: false });
|
|
67
|
+
const ascii = formatCyberMenuFrame(status, commands, 1, 8, { columns: 96, rows: 24, useColor: false, ascii: true });
|
|
68
|
+
|
|
69
|
+
assertMaxWidth(large, 120, 'large');
|
|
70
|
+
assertMaxWidth(compact, 96, 'compact');
|
|
71
|
+
assertMaxWidth(running, 120, 'running');
|
|
72
|
+
assertMaxWidth(hero, 120, 'dna-hero');
|
|
73
|
+
assertMaxWidth(ascii, 96, 'ascii');
|
|
74
|
+
|
|
75
|
+
assert(large.includes('DNA CYBER HELIX'), 'large frame missing DNA CYBER HELIX');
|
|
76
|
+
assert(large.includes('luminous fine helix'), 'large frame missing fine helix subtitle');
|
|
77
|
+
assert(hero.includes('SKILL_MASTER DNA ONLINE'), 'hero frame missing online title');
|
|
78
|
+
assert(hero.includes('DNA'), 'hero frame missing DNA label');
|
|
79
|
+
assert(hero.includes('ONLINE'), 'hero frame missing ONLINE state');
|
|
80
|
+
assert(compact.includes('DETAIL FOCUS'), 'compact frame missing details panel');
|
|
81
|
+
assert(compact.includes('DNA-CORE'), 'compact frame missing DNA panel');
|
|
82
|
+
assert(running.includes('SKILL_MASTER WORKFLOW ONLINE'), 'running frame missing running title');
|
|
83
|
+
assertNoHeavyUnicode(ascii, 'ascii frame');
|
|
84
|
+
|
|
85
|
+
for (const frame of [large, compact, running, hero, ascii]) {
|
|
86
|
+
assert(!frame.includes('0.0.12'), 'visual frame contains stale 0.0.12 version');
|
|
87
|
+
assert(!frame.includes('stable frame'), 'visual frame leaked old stable frame text');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
console.log('verify-menu-visual: ok');
|