@guozhi-fe/skills 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,34 @@
1
- # Temporary Holding Version
1
+ # 国智前端 Skills
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ 面向 Codex 的微前端内存排查技能。需要 Node.js 18 或更高版本。在业务项目根目录运行 CLI。
4
+
5
+ | 技能 | 适用场景 |
6
+ | --- | --- |
7
+ | microfrontend-memory-assessment | 反复打开和关闭子应用,判断内存增长的程度,输出可交接的采样证据。 |
8
+ | microfrontend-memory-lifecycle | 追踪保留引用链,实施修复,并用生产构建验收。 |
9
+
10
+ ## 首次安装
11
+
12
+ 在 npm 发布前,可在本仓库运行 `npm pack`,通过 `npm i -g /绝对路径/guozhi-fe-skills-0.1.0.tgz` 安装 CLI。npm 发布后可用 `npm i -g @guozhi-fe/skills`,或通过 `npx --package=@guozhi-fe/skills gzfe-skills init` 直接接入。
13
+
14
+ ```sh
15
+ gzfe-skills init microfrontend-memory-assessment
16
+ gzfe-skills list
17
+ ```
18
+
19
+ 省略技能名会安装全部技能。文件和版本记录位于当前项目的 `.agents/skills/<技能名>/`,建议一起提交到项目 Git 仓库。重复 init 会保留已安装版本。
20
+
21
+ ## 更新
22
+
23
+ 安装目标版本的 CLI 后,在业务项目运行以下命令。更新使用该 CLI 随包携带的技能。
24
+
25
+ ```sh
26
+ gzfe-skills update --dry-run
27
+ gzfe-skills update
28
+ ```
29
+
30
+ update 默认处理已安装的受管理技能,也可在命令后指定名称。若本地新增、修改或删除了技能文件,CLI 会列出差异并停止。保存自己的改动、恢复已安装版本后,再执行更新。list 可查看安装版本及本地改动。
31
+
32
+ ## 维护
33
+
34
+ 编辑 skills 下的技能,变更分发内容时同步提升 package.json 的版本。两个技能共享的证据协议维护在 lifecycle 的 references 目录;评估技能安装时会获得独立副本。
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs'
3
+ import path from 'node:path'
4
+ import { fileURLToPath } from 'node:url'
5
+ import { createHash } from 'node:crypto'
6
+
7
+ const root = fileURLToPath(new URL('../', import.meta.url))
8
+ const source = path.join(root, 'skills')
9
+ const catalog = fs.readdirSync(source).sort()
10
+ const version = JSON.parse(fs.readFileSync(path.join(root, 'package.json'))).version
11
+ const dest = path.resolve('.agents/skills')
12
+ const marker = '.gzfe-skills.json'
13
+ const assessment = 'microfrontend-memory-assessment'
14
+ const lifecycle = 'microfrontend-memory-lifecycle'
15
+
16
+ function manifest(dir, prefix = '') {
17
+ const result = {}
18
+ for (const item of fs.readdirSync(dir, { withFileTypes: true })) {
19
+ const key = prefix + item.name
20
+ if (key === marker) continue
21
+ const file = path.join(dir, item.name)
22
+ if (item.isDirectory()) Object.assign(result, manifest(file, key + '/'))
23
+ else result[key] = item.isFile() ? createHash('sha256').update(fs.readFileSync(file)).digest('hex') : 'special'
24
+ }
25
+ return result
26
+ }
27
+
28
+ function status(name) {
29
+ const target = path.join(dest, name)
30
+ const stat = fs.lstatSync(target, { throwIfNoEntry: false })
31
+ if (!stat) return { label: '未安装' }
32
+ if (!stat.isDirectory()) return { label: '同名路径已存在' }
33
+ const record = path.join(target, marker)
34
+ if (!fs.lstatSync(record, { throwIfNoEntry: false })?.isFile()) return { label: '非本工具管理' }
35
+ const saved = JSON.parse(fs.readFileSync(record))
36
+ if (saved.name !== name || !saved.files || !saved.version) throw Error('安装记录无效:' + record)
37
+ const current = manifest(target)
38
+ const changes = [...new Set([...Object.keys(current), ...Object.keys(saved.files)])]
39
+ .filter(key => current[key] !== saved.files[key])
40
+ return { label: [saved.version, ...changes].join(' '), saved, changes }
41
+ }
42
+
43
+ function stage(name, dir) {
44
+ fs.cpSync(path.join(source, name), dir, { recursive: true, filter: file =>
45
+ file !== path.join(source, assessment, 'references/evidence-contract.md') })
46
+ if (name === assessment) {
47
+ const refs = path.join(dir, 'references')
48
+ fs.mkdirSync(refs, { recursive: true })
49
+ fs.copyFileSync(path.join(source, lifecycle, 'references/evidence-contract.md'), path.join(refs, 'evidence-contract.md'))
50
+ }
51
+ fs.writeFileSync(path.join(dir, marker), JSON.stringify({ name, version, files: manifest(dir) }, null, 2) + '\n')
52
+ }
53
+
54
+ function run([command, ...args]) {
55
+ if (!command || ['--help', '-h'].includes(command)) {
56
+ console.log('gzfe-skills init [技能名 ...] | list | update [技能名 ...] [--dry-run]\n' + catalog.join('\n'))
57
+ return
58
+ }
59
+ if (command === '--version') return console.log(version)
60
+ if (!['init', 'update', 'list'].includes(command)) throw Error('未知命令:' + command)
61
+ const dry = command === 'update' && args.includes('--dry-run')
62
+ const names = [...new Set(dry ? args.filter(x => x !== '--dry-run') : args)]
63
+ if (names.some(x => !catalog.includes(x)) || (command === 'list' && names.length)) throw Error('参数无效,请查看 --help')
64
+ for (const dir of [path.dirname(dest), dest]) {
65
+ const entry = fs.lstatSync(dir, { throwIfNoEntry: false })
66
+ if (entry && !entry.isDirectory()) throw Error('需要普通项目目录:' + dir)
67
+ }
68
+ if (command === 'list') {
69
+ for (const name of catalog) console.log(name, status(name).label)
70
+ return
71
+ }
72
+ const plan = () => (names.length ? names : catalog).filter(name => {
73
+ const state = status(name)
74
+ if (command === 'update' && !names.length && !state.saved) return false
75
+ if (!state.saved && (command === 'update' || state.label !== '未安装')) throw Error(name + ':' + state.label)
76
+ if (state.changes?.length) throw Error(name + ' 有本地修改:\n' + state.changes.join('\n'))
77
+ return !state.saved || (command === 'update' && state.saved.version !== version)
78
+ })
79
+ if (dry) {
80
+ const selected = plan()
81
+ return console.log(selected.length ? selected.join(', ') + ' → ' + version : '无需更新')
82
+ }
83
+ fs.mkdirSync(dest, { recursive: true })
84
+ const work = path.join(dest, '.gzfe-skills-work')
85
+ fs.mkdirSync(work) // Exclusive across CLI writers. Retain backups if recovery fails.
86
+ const applied = []
87
+ let cleanup = true
88
+ try {
89
+ const selected = plan()
90
+ for (const name of selected) stage(name, path.join(work, name))
91
+ for (const name of selected) {
92
+ const target = path.join(dest, name)
93
+ const backup = path.join(work, name + '.old')
94
+ const existed = fs.existsSync(target)
95
+ if (existed) fs.renameSync(target, backup)
96
+ applied.push({ target, backup, existed, installed: false })
97
+ fs.renameSync(path.join(work, name), target)
98
+ applied.at(-1).installed = true
99
+ }
100
+ console.log(selected.length ? selected.join(', ') + ' → ' + version : '无需变更')
101
+ } catch (error) {
102
+ cleanup = false
103
+ for (const item of applied.reverse()) {
104
+ if (item.installed) fs.rmSync(item.target, { recursive: true })
105
+ if (item.existed) fs.renameSync(item.backup, item.target)
106
+ }
107
+ cleanup = true
108
+ throw error
109
+ } finally {
110
+ if (cleanup) fs.rmSync(work, { recursive: true })
111
+ }
112
+ }
113
+ try { run(process.argv.slice(2)) } catch (error) {
114
+ console.error(error.message)
115
+ process.exitCode = 1
116
+ }
package/package.json CHANGED
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "name": "@guozhi-fe/skills",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "国智前端项目级 Codex 技能集",
5
+ "type": "module",
6
+ "bin": { "gzfe-skills": "bin/gzfe-skills.js" },
7
+ "files": ["bin", "skills"],
8
+ "engines": { "node": ">=18" },
9
+ "repository": "github:soleilLulu/gz-fe-skills"
10
+ }
@@ -0,0 +1,47 @@
1
+ ---
2
+ name: microfrontend-memory-assessment
3
+ description: 评估微前端子应用反复打开关闭后的内存增长是否异常、影响程度与证据置信度,生成可复用的采样证据和修复交接报告。适用于内存体检、多个子应用初筛或仅判断严重性;明确要求定位修复时使用 microfrontend-memory-lifecycle。
4
+ ---
5
+
6
+ # 微前端内存增长评估
7
+
8
+ 回答三个独立问题:是否存在异常增长,业务影响有多大,证据有多可靠。此 skill 以评估报告和交接材料结束,不自动进入业务修复。若用户已明确要求修复,直接使用修复 skill 并复用现有证据,不要求用户额外批准一次评估转修复。
9
+
10
+ ## 主应用基准与修改边界
11
+
12
+ 使用同事已下载到本地并切到 `release/1.0.2-HF1` 的 `gz-zd-web` 项目,记录本地 HEAD。主应用只读,评估与修复均不得修改其源码、配置或依赖/锁文件;实现改动仅限子应用。获取基准、本地 Mock 接入和无法在此边界内修复时的处理,遵循共用证据协议中的“主应用基准与只读边界”。本节优先于下文有关补齐主子配置的一般说明。
13
+
14
+ ## 默认环境:本地主子应用与受控 Mock
15
+
16
+ 内网与外网使用相同的本地 Mock 流程。本 skill 默认基于本地主应用和子应用、创建或补齐的受控 Mock 数据执行,不把部署地址、真实账号或内网连通性作为前置条件。先按 [本地 Mock 协议](references/evidence-contract.md#本地-mock-环境) 跑通实际宿主菜单,再采样;正式评估使用生产构建和同一 Mock,不含 HMR。
17
+
18
+ 缺少宿主时先完成子应用与 Mock 准备,明确需要的本地材料;独立子应用或简化测试壳结果只能作为局部证据。仅有截图可以初筛,不能替代本地 Mock 评估。除非用户另行指定可访问的测试环境,不尝试依赖内网接口。
19
+
20
+ ## 范围与证据
21
+
22
+ 先读取适用 AGENTS.md、工作区状态和已有记录。确认宿主/子应用、菜单、实际数据量、常用操作频率、部署/构建方式及用户关心的影响。遵守当前授权;可以只读调查代码、创建或补齐本地 Mock、测试专用启动/构建配置,以及必要且可移除的标量探针或本地测试工具,不改变业务生命周期、依赖版本、缓存策略或共享环境配置来完成评估。
23
+
24
+ 开始采样前读 [共用证据与交接协议](references/evidence-contract.md)。它与修复 skill 共用一份规范;安装时随本技能附带协议。引用缺失时明确说明,不能声称已按完整协议验收。
25
+
26
+ 只有截图时,先解读可见的趋势并指出未知项,不编造轮次、GC、数据量和对象归属。例如 132→144→159→177 MB,再经过另一菜单到178 MB,只能说明前一段仍增长、后一段暂平;不能把两个菜单合并判定为已稳定。
27
+
28
+ ## 分层评估
29
+
30
+ 1. **材料初筛**:复用已有版本、采样和快照,检查可比性、是否真实关闭、是否预热、是否包含 HMR/调试对象。若只能得到观察线索,允许输出“证据不足”的初筛结论。
31
+ 2. **受控采样**:优先选择最常用或最易复现的菜单和代表性数据场景,按共用协议执行。初筛可先做预热后 10 轮并采 0/5/10;这不等于正式稳定性通过。数据场景应覆盖实际结构,空表与有数据结果不可互相替代。
32
+ 3. **有针对性补证**:出现增长时延长至至少 30 轮,补早晚快照和必要的打开态正对照;查对象类别、数量和初步保留链,足够支持影响判断即可。不必穷尽具体修复方案。已经发现明确逐轮累积的对象或业务故障,可以交接修复,不为完成矩阵继续制造负载。
33
+ 4. **平台判断或结束**:若要给出“观察范围内稳定”的结论,至少完成 30 轮且有后期趋势及对象证据。仍无法判断时,在同一文档按范围/预算延长,或如实以“证据不足/增长待定位”交接;不能无限延长初筛,也不能通过刷新重置曲线。批量评估各子应用时使用相同条件,条件不同不直接排名。
34
+
35
+ 生产构建是正式判断基础;开发模式可以定位或提出嫌疑,但不能把 HMR 增长当生产泄漏。环境不足时完成不依赖环境的检查并列出缺口。短测无法判定严重性时,不强行打分。
36
+
37
+ ## 报告结论
38
+
39
+ 分别填写,附证据位置:
40
+
41
+ - **问题状态**:证据不足 / 观察范围内未见持续增长 / 持续增长待定位 / 已证实不应保留的资源或对象累积。
42
+ - **严重程度**:未定;低(已观察影响有限且有边界);中(可重复增长,按实际使用频率有累积风险);高(正常会话内已出现明显退化、崩溃或重要资源持续活动)。没有对应业务影响证据时写条件性风险,不用假想崩溃提升等级。低严重度也不代表无泄漏。
43
+ - **置信度**:低(截图/条件不明);中(受控重复趋势,但对象归属未明);高(可复现且有对象/资源与保留链交叉证据)。置信度高不代表影响必然高。
44
+
45
+ 不用固定 MB、每轮 MB 或百分比作为跨应用通用通过线。记录实际累计轮数与后期分段增量;轮次未知时不计算每轮增长。不把总量较高等同泄漏,也不把增长变慢或单次下降当作稳定。
46
+
47
+ 输出结论、影响与依据、可复现步骤、采样/快照位置、已证实与假设、缺口,以及建议继续采样/进入修复/暂时观察。按共用协议生成交接记录。仅评估授权下可以提出修复方向,但不修改业务实现、不自动启动修复 skill。结束后清理本任务启动的服务和浏览器会话,保留证据。
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "微前端内存评估"
3
+ short_description: "评估微前端内存增长趋势、业务影响和证据置信度,输出修复交接材料"
4
+ default_prompt: "使用 $microfrontend-memory-assessment 评估指定主子应用反复开关后的内存增长和严重程度,生成证据与修复交接报告。"
@@ -0,0 +1,69 @@
1
+ ---
2
+ name: microfrontend-memory-lifecycle
3
+ description: 对微前端反复开关的内存增长定位保留引用链、实施修复并完成生产浏览器验收。适用于明确要求分析修复或已有评估证据的任务;仅做体检、严重性判断时使用 microfrontend-memory-assessment。
4
+ ---
5
+
6
+ # 微前端内存分析、修复与验收
7
+
8
+ 目标是预热后关闭态占用形成有界平台,旧页面业务数据和资源能够释放;允许有边界、有解释的代码缓存。根据证据选择修复,不预设框架、Blob URL 或某个组件是根因。
9
+
10
+ ## 进入任务
11
+
12
+ 读取 [共用证据与交接协议](references/evidence-contract.md)。若已有评估报告,先验证它与当前代码、运行产物及数据条件是否一致;复用有效基线与驱动,只补缺口,不重新执行无必要的初筛。评估假设需要再次用保留路径验证。无报告时自行建立基线,用户已授权修复则直接推进,不要求先运行评估 skill 或重新确认。
13
+
14
+ 仅要求判断严重性/评估时,转用 microfrontend-memory-assessment,不自动实施修复。保留本 skill 原调用名称以兼容已有用法。
15
+
16
+ 读取主子仓库适用的 AGENTS.md、工作区差异和已有排查记录。保留用户修改。明确宿主/子应用目录、受影响菜单、当前加载与销毁策略以及已有补丁。缺少浏览器或环境条件时,继续独立的源码调查,明确剩余验收条件,不把静态推断写成实测。
17
+
18
+ 在修改前保存 HEAD、相关未提交差异、lockfile、实际安装的依赖补丁及构建产物标识。基线必须对应当前策略:旧的“关闭复用”结果不能替代当前“关闭 unmount、重开新实例”的结果。安装目录存在补丁不等于补丁已纳入版本管理或实际运行产物。
19
+
20
+ ## 主应用基准与修改边界
21
+
22
+ 使用同事已下载到本地并切到 `release/1.0.2-HF1` 的 `gz-zd-web` 项目,记录本地 HEAD。主应用只读,评估与修复均不得修改其源码、配置或依赖/锁文件;实现改动仅限子应用。获取基准、本地 Mock 接入和无法在此边界内修复时的处理,遵循共用证据协议中的“主应用基准与只读边界”。本节优先于下文有关补齐主子配置的一般说明。
23
+
24
+ ## 默认环境
25
+
26
+ 内网与外网使用相同的本地 Mock 流程。默认在本地运行真实主子应用,并创建或补齐受控 Mock 数据完成复现、修复和验收,不等待内网资源、真实账号或后端开放。遵循 [本地 Mock 协议](references/evidence-contract.md#本地-mock-环境)。除非用户明确指定其他可访问环境,否则所有结论限定为本地受控 Mock 范围。
27
+
28
+ 若缺少本地宿主材料,先完成独立源码调查及 Mock 准备,明确提出所需材料,不能用简化测试壳或子应用独立运行冒充宿主集成验收。
29
+
30
+ ## 联调和基线
31
+
32
+ 1. 优先复用现有 Mock,跑通宿主登录、权限、菜单注册与子应用实际入口,验证关闭、重开、并存和切换。独立子应用页面不能代替宿主联调。
33
+ 2. 提供空数据和代表性有数据场景,默认 1000 条稳定 ID,覆盖页面真实消费的结构;按业务与用户要求调整规模。让权限、布局、轮询和延迟响应可控。
34
+ 3. 开发环境用于调试;正式采样用双应用生产构建与同一受控数据服务,无 HMR。Mock 不应误连真实业务接口,也不应无界缓存响应或添加无法解释的 Worker/监听。未覆盖接口明确失败,不能一概返回成功。
35
+ 4. 固定浏览器、扩展、预热动作、加载完成条件、关闭等待、GC 方法和指标单位。先采修复前证据,再做修复。详细执行方法见 [验收协议](references/acceptance.md)。
36
+
37
+ ## 定位保留路径
38
+
39
+ 追踪:菜单关闭 → 宿主任务队列/实例管理 → provider.destroy → React 或其他框架卸载 → effect/业务清理 → 容器和宿主引用解除。
40
+
41
+ 同时检查以下独立类别:
42
+
43
+ - **代码**:新模块 URL、Blob ESM、ModuleMap、模块级缓存、重复创建的依赖运行时。
44
+ - **页面**:App/provider 闭包、React root/Fiber、表格 API/RowNode、detached DOM、全局 Map、已完成 Promise 的 result。
45
+ - **业务资源**:行数据与派生对象、订阅、请求回调、晚到响应、定时器、轮询 owner、Worker。
46
+ - **观测开销**:控制台对象、DevTools、自动化探针、HMR、Mock、网络记录。
47
+
48
+ 使用堆快照中的强引用链,结合对象数量和生命周期字段确认归属。unmount 返回成功、实例池归零、URL revoke 都不能证明堆对象已释放。形状匹配的对象可能只是 API 定义表;稳定 ID 字符串也可能只是字符串缓存。用打开态正对照和对象 ID/字段检查验证分析方法。
49
+
50
+ 诊断优先保存标量,不在全局变量、控制台或探针历史中保存 App、DOM、表格或业务对象。失败记录保留;改正驱动错误后从新会话重测。
51
+
52
+ ## 选择修复
53
+
54
+ - 外部引用泄漏:修复持有者,解除缓存、监听、订阅和回调;验证取消和晚到响应。避免盲目重复 destroy 或对所有组件清 state。
55
+ - 重复模块/依赖运行时:评估代码生命周期与页面状态生命周期分离。可以共享稳定 URL 的依赖运行时,每次创建独立页面状态;也可以采用有明确容量及淘汰行为的运行时方案。必须验证依赖共享的单例副作用、provider 重挂载、多实例隔离、主题、权限和退出行为。
56
+ - 依赖补丁:锁定适用版本,幂等重放,未知代码形状失败;接入安装/构建并核实真实产物。避免把原来已生效的补丁重新归为此次收益。
57
+ - 注册表:长期元数据不应携带旧会话默认 props;每次加载传当前会话,退出清理遗留引用。功能跨用户隔离不等于所有旧凭据字符串已从堆擦除。
58
+
59
+ 保留现有架构优先。不得通过隐藏页面、增大缓存、定期刷新或关闭正常功能掩盖增长。仅凭这份 skill 不授权迁移承载架构、提交、推送或部署。
60
+
61
+ 修改代码加载或构建插件时,同时检查打包机器的平台路径、业务入口是否仍位于页面包、provider 注册是否真正注入。构建成功和资源 HTTP 200 不代表页面可挂载;本地 macOS 通过不等于 Windows 构建通过。具体检查见验收协议的“构建与部署兼容性”。
62
+
63
+ 涉及 Garfish、React bridge、Vite 构建时,按需阅读 [交易子应用案例](references/garfish-case.md)。该案例是可供验证的候选方案,不是通用补丁。
64
+
65
+ ## 完成条件和交付
66
+
67
+ 按 [验收协议](references/acceptance.md) 完成修复前后配对、多轮真实浏览器采样、堆对象验证及功能回归。单测/构建通过不能替代浏览器验收。
68
+
69
+ 交付可复现启动/停止与数据切换方法、源码/产物标识、根因引用链、必要修改与相关检查、前后记录、功能结果、有限常驻对象解释及未覆盖范围。报告区分已证实、待验证、验收限制。关闭态仍增长则继续调查或明确未完成,不能用统一 MB 阈值或单次下降宣布通过。
@@ -0,0 +1,4 @@
1
+ interface:
2
+ display_name: "微前端内存修复与验收"
3
+ short_description: "定位主子应用反复开关的内存增长,并以生产构建堆证据验收修复"
4
+ default_prompt: "使用 $microfrontend-memory-lifecycle 排查当前主子应用反复打开关闭后的内存增长,完成证据驱动修复和生产浏览器验收。"
@@ -0,0 +1,44 @@
1
+ # 生产构建验收协议
2
+
3
+ 默认使用本地主子应用生产构建和受控 Mock,环境准备遵循 [本地 Mock 协议](evidence-contract.md#本地-mock-环境)。不依赖内网部署或真实后端。
4
+
5
+ ## 可重复条件
6
+
7
+ 每个场景从新的门户文档/会话开始。记录浏览器版本、扩展、构建命令、主子产物哈希、Mock 数据版本、当前实例策略和探针版本。修复前后使用一致的环境、数据、动作及等待;若修正 Mock 或驱动影响结论,重跑配对组,保留旧记录。
8
+
9
+ 默认矩阵:空表/1000 条 × 菜单 A/菜单 B/交替;只有一个受影响菜单时按实际范围缩减。每组预热后至少 30 轮,采样预热基线及 5/10/20/30;用户有明确要求时遵守其范围。第 30 轮仍无法辨别平台则在同一文档延长至 50/100 轮等,不通过重启文档制造平台。
10
+
11
+ 采样动作、等待、GC、指标定义与证据交接统一遵循 [共用证据协议](evidence-contract.md),避免评估与修复采用不同口径。
12
+
13
+ ## 快照与对象验收
14
+
15
+ 至少保留有数据打开态正对照、预热关闭态和末轮关闭态快照;增长或平台不明时补中间/延长快照。快照较大时流式解析,预留磁盘和超时。分析:
16
+
17
+ - ModuleMap/模块 URL 是否随轮数增加。
18
+ - 稳定 ID 对应的实际行对象、派生容器是否归零或符合明确的合法共享用途。
19
+ - 实际活体表格 API、React/root、App、detached 容器是否累积。
20
+ - 残留对象的强引用路径、生命周期状态、业务数据和 props 是否仍存在。
21
+ - 调试工具、JIT、代码缓存能否解释有限差异;不要把任何未知增长笼统归为缓存。
22
+
23
+ 正常资源请求缓存不能代替原生模块数量。Worker 可能在线程内 revoke URL,主线程 revoke 计数不足以判断泄漏。对象形状只能用于筛选,不能直接作为实例数。
24
+
25
+ ## 结果判断
26
+
27
+ 通过需要多轮关闭态趋势和对象证据共同支持:后期内存形成平台,模块/DOM/监听/业务对象不随轮数复制,活动资源按设计释放。记录有限残留的数量、保留路径和验证轮数;有限观察不能证明永远不增长,也不能将实例池 0 写成堆 App 0。
28
+
29
+ 打开期间业务轮询应实际运行,关闭后停止;不能靠禁用功能通过。验证双菜单隔离、页签保活、关闭重开新状态、刷新与路由恢复、主题切换、关闭全部、退出与跨用户权限清理。延迟请求需证明服务端已接受,关闭后客户端取消/代次防护生效,晚到响应不能复活页面。
30
+
31
+ 相关测试、lint、构建结果分别报告;原有失败与本次新增失败区分。Mock 验收不能表述为真实后端、CDN/CSP、发布热更新或其他浏览器已验收。结束时停止本任务创建的服务并按浏览器工具要求释放会话,不终止用户无关进程。
32
+
33
+ ## 构建与部署兼容性
34
+
35
+ 改动 external 分类、入口注入或共享运行时后,记录实际构建命令、操作系统和工具版本;根据部署团队实际使用的平台验证,不只验证开发机。
36
+
37
+ - 不以“不是 / 开头”判断裸包名。解析模块 ID 时区分 POSIX 绝对路径、Windows 盘符路径(正斜杠和反斜杠)、UNC 路径、相对路径、别名和虚拟模块。Node 插件可同时使用 `path.posix.isAbsolute` 与 `path.win32.isAbsolute`;仅使用当前系统的 `path.isAbsolute` 无法在 macOS 测试中完整识别 Windows 路径。
38
+ - 匹配源码入口前规范化路径分隔符,并按打包器约定处理查询串。将真实业务入口是否经过转换作为构建不变量;缺失时明确失败,不输出永不 resolve 的 provider shim。
39
+ - 共享依赖清单中不得混入被误判的项目源码绝对路径。产物检查需确认业务入口和 provider 连接有效;仅验证哈希、模块导出存在不足以发现业务入口被整体移入共享运行时。
40
+ - 回归用例覆盖 POSIX、Windows 两种分隔符、UNC、正常包名/子路径、项目别名,以及缺失入口时构建失败。模拟路径用例通过只能证明分类逻辑,不能代替实际 Windows 构建与部署浏览器验证。
41
+
42
+ 部署后超时的排查顺序:先取入口及共享 runtime 的 URL、状态码、响应体、Content-Type、CORS、压缩和耗时,核对产物清单与哈希;再查脚本执行、provider 注册及挂载。比较入口大小是否异常缩小,并检查响应正文是否仅剩包装代码或被 SPA fallback 替换。大小只是线索,不设置通用最小 KB 阈值。
43
+
44
+ 若资源均已成功返回,不继续无证据地归咎于下载慢。若同时出现 postMessage origin 警告,检查实际收件窗口和备用事件通道,不能直接认定警告导致挂载超时,也不能用 `*` 掩盖目标 origin 错误。本地无法访问部署网络时,请用户提供最小必要的响应内容/清单;明确未完成远端复验。
@@ -0,0 +1,88 @@
1
+ # 共用证据与交接协议
2
+
3
+ 本文件同时供 microfrontend-memory-assessment 与 microfrontend-memory-lifecycle 使用。评估可缩小场景/轮次,但必须报告范围;修复验收的完整要求见 [验收协议](acceptance.md)。不得将短时初筛当作最终验收。
4
+
5
+ ## 主应用基准与只读边界
6
+
7
+ - 使用本地已有的 `gz-zd-web`。先按用户提供的路径或相邻目录定位项目,读取适用的 AGENTS.md。在该目录执行 `git branch --show-current`,确认结果为 `release/1.0.2-HF1`。用 `git rev-parse HEAD` 记录本地提交,用 `git status --short` 记录已有差异,前后实验固定同一基准。
8
+ - 无需提供或核实远端地址,也不自动 clone、fetch、pull 或切换分支。目录缺失、分支不符或处于 detached HEAD 时,请同事提供已切到指定分支的本地工作区;期间可继续子应用侧准备。
9
+ - 主应用是只读测试基座。不得修改其受版本管理的源码、构建配置、Mock 配置、package.json、lockfile,也不得给主应用依赖打新补丁。可以按仓库已有方式安装依赖、构建和启动,保留原有构建行为;若这些命令改变被跟踪文件,检查并报告,不把变动纳入修复或覆盖用户已有修改。
10
+ - 修复的实现与配置变更仅放在子应用。Mock fixtures、测试驱动和联调适配放在子应用测试目录或仓库外临时测试目录;通过主应用已有配置入口或受控本地 Mock 服务接入,不为联调便利修改主应用。已有通用说明中的“补齐主子配置”仅允许调用主应用已有能力,不授权新增主应用修改。
11
+ - 请求拦截可用于替代不可达的数据服务,必须记录映射且保持基准主应用脚本及生命周期行为不变。禁止用测试代理替换/重写宿主 JavaScript、注入宿主修复或改变其销毁逻辑来规避只读约束。若基准缺少必要接入能力且无法通过既有入口联调,如实报告具体阻塞。
12
+ - 发现宿主保留路径时仍须记录证据。优先评估是否存在不损害行为的子应用侧修复;若无法仅修改子应用满足目标,交付根因、已完成部分和所需宿主调整建议,明确在当前边界内未完成,不实际修改主应用、不把问题掩盖为通过。
13
+ - 最终交付列出基准分支/SHA及主应用工作区检查结果;修复差异应只涉及子应用。
14
+
15
+ ## 本地 Mock 环境
16
+
17
+ 内网与外网均采用本地 Mock。评估与修复均基于本地主子应用及受控生成数据;真实环境反馈属于补充证据,不能替代或扩大 Mock 验收结论。
18
+
19
+ 1. **检查现有方案**:读取主子仓库的 Mock 配置、启动命令、接口类型和页面消费字段,优先复用;缺口在测试专用文件中补齐,避免改变正常业务逻辑。两个 skill 均允许为本地实验新增必要的 Mock 与启动/构建配置,这不等于授权业务修复。
20
+ 2. **跑通宿主**:模拟登录、会话、菜单、权限、子应用注册及必要布局接口;子应用地址指向本地资源。从真实宿主打开目标菜单,验证关页、重开、并存及切换,保留主子通信和正常生命周期行为。
21
+ 3. **创建代表性数据**:按接口类型、组件消费和已有测试生成确定性的合成数据。提供可重复切换的空表和默认 1000 条场景(按业务规模调整),稳定 ID、有效关联、分页/筛选/状态字段应足以触发实际表格及派生计算。不要把同一个对象重复引用 1000 次冒充独立行。未知字段语义记录假设,未知接口明确失败,不一律返回成功。
22
+ 4. **保持真实资源行为**:保留正常表格、订阅、轮询和 Worker;权限与布局固定可控,另提供延迟响应以验证取消和晚到防护。不能为通过测试关闭这些功能。Mock 只替代数据与服务依赖,不替代页面实现或销毁链路。
23
+ 5. **隔离外部请求**:测试配置关闭真实业务代理和未命中请求的真实上游回退,使用虚拟 token,不转发真实登录凭据。通过浏览器请求记录确认业务请求实际命中本地 Mock。项目安装依赖仍可能需要外网包源,不声称完全离线。
24
+ 6. **统一开发和生产数据**:先开发联调,再生产构建采样。已有 Mock 若只能在开发服务器使用,补充本地数据服务或等效生产可用方案;生产主子客户端无 HMR,前后版本共用相同 fixture 和控制参数。优先服务端生成/保存有限 fixture,避免浏览器 Mock 把全量数据常驻造成测量偏差。
25
+ 7. **排除基础设施泄漏**:Mock 不无限累积响应历史、请求日志、监听或 Worker;浏览器侧 Mock 若不可避免,应单独记录其资源和稳定性。未知请求与失败尝试保留必要记录,但使用有界日志或流式写盘。
26
+ 8. **交付复现方式**:记录主子启动/构建命令、端口、入口、虚拟登录方式、场景切换、fixture 版本/哈希和停止方式。评估转修复复用这套环境,不另造不一致的数据集。
27
+
28
+ 缺少主子源码或本地运行产物时,继续不依赖它们的分析并说明具体缺口;内网不可达本身不是停止工作的理由。结论明确标为“受控 Mock 下”的趋势、影响和资源行为;真实后端、实际数据分布、网关/CDN/CSP及部署平台兼容性未验证的部分单列,不能写成真实业务环境已通过。
29
+
30
+ ## 可比性
31
+
32
+ 记录源码 HEAD 与相关工作区差异、依赖锁及补丁、实际构建命令/平台、主子运行产物标识、浏览器和扩展、数据来源/规模/结构及当前加载销毁策略。模拟数据用稳定 ID 便于追踪;真实业务数据的快照按敏感业务材料保存,不上传到未经授权的服务,也不把数据或凭据复制进 skill。
33
+
34
+ 每个独立场景从新门户文档开始;预热不计入重复轮次。同一场景延长测试不刷新文档。每条快照标明打开或关闭态、累计轮次、等待与 GC 条件。比较前后版本时固定这些条件;不同浏览器指标和单位不能直接比较。
35
+
36
+ ## 采样与指标
37
+
38
+ 单轮:实际打开宿主菜单 → 等待页面及目标业务请求成功、数据行数符合场景、加载状态结束 → 固定稳定等待 → 实际关闭 → 等待卸载和在途任务结束 → 固定清理等待 → 到采样点执行固定 GC。
39
+
40
+ 可参考一次有效实验的等待配置:加载完成后 1000ms、关闭清理后 1500ms、两次 CDP HeapProfiler.collectGarbage 间隔 300ms。这只是可调整起点;不能只依靠固定 sleep 判断请求/页面完成。所有前后组使用相同配置。
41
+
42
+ 用可用且授权的浏览器工具采集 CDP HeapProfiler/Runtime/Memory 指标。UI 自动化负责用户动作,CDP负责取证;遵守所用工具的会话和清理规则。工具无法执行 GC/快照时说明限制,不伪造等价验收。
43
+
44
+ ## 指标和快照
45
+
46
+ 建议每条采样保存:
47
+
48
+ ```
49
+ phase, scenario, buildIds, rowCount, cycle, timestamp,
50
+ usedHeapBytes, backingStorageBytes, documents, domNodes, listeners,
51
+ activeApps, cachedApps, loadingApps, unmountFailures,
52
+ reactCreated, reactDestroyed, pendingRequests,
53
+ pollingTasks, workers, intervals
54
+ ```
55
+
56
+ 不可观测字段标记 unavailable 和原因,不填 0。paused 任务与已 stop 任务分别定义;Worker 创建失败后的定时器兜底也要观测。区分 JS 堆、backing storage、进程 RSS,不混加或混用单位。
57
+
58
+ ## 交接记录格式
59
+
60
+ 报告中保留以下字段;未知项明确写 unknown / unavailable 及原因,未执行写 not-run,不填通过或零。可保存为 Markdown 或结构化文件,证据路径使用可解析的相对路径或完整路径。
61
+
62
+ ```yaml
63
+ schema_version: 1
64
+ scope: # 主子应用目录、菜单、目标;仅子应用可修改,主应用只读
65
+ host_baseline: # 本地仓库路径、release/1.0.2-HF1、实际commit SHA、工作区检查结果
66
+ versions: # HEAD、工作区差异、lock/补丁、构建命令/OS/工具版本、产物哈希
67
+ runtime: # 本地主子入口、浏览器/扩展、Mock服务与路由隔离、HMR、当前生命周期策略
68
+ mock: # fixture版本/哈希、生成依据与假设、空表/有数据/延迟切换、启动和停止命令
69
+ scenarios: # 数据规模与结构、权限/布局/轮询、每组操作及实际累计轮次
70
+ sampling: # 预热、加载完成条件、关闭确认、等待、GC、单位、探针版本
71
+ artifacts: # 原始samples、快照、对象分析、保留链、日志和失败记录的路径
72
+ findings:
73
+ status: # 证据不足/观察范围内未见持续增长/持续增长待定位/已证实累积
74
+ severity: # 未定/低/中/高,附业务影响和依据
75
+ confidence: # 低/中/高,附可复现性和证据范围
76
+ facts: # 已证实事实,每项指向证据
77
+ hypotheses: # 尚待验证的解释
78
+ limitations: # 缺失条件、未覆盖数据/菜单/平台、观测工具可能造成的影响
79
+ next_steps: # 最小补证或修复方向,不代替用户授权
80
+ ```
81
+
82
+ 不记录明文 token、Cookie 或账户密码。快照证据可在受控本机保留,交接报告优先输出标量及对象类别。保留失败尝试并标注作废原因,不用重跑覆盖失败事实。
83
+
84
+ ## 修复端接收规则
85
+
86
+ 先检查证据与当前源码、实际产物、数据和操作条件是否相符。相符则复用基线、Mock和驱动,只补缺口;已过期或条件变动时保留旧证据并重建受影响的配对组。评估提出的嫌疑不是既定根因,修复前仍需确认实际保留路径。
87
+
88
+ 已有明确修复授权时无需把评估转交设置成新的审批关口。只有评估授权时,交付报告即完成当前任务,不据此推定允许业务改造。不存在评估报告时,修复 skill 自行建立必要基线,不强制先调用另一 skill。
@@ -0,0 +1,55 @@
1
+ # Garfish + React 交易子应用案例
2
+
3
+ 以下是一次本机受控 Mock 实验的事实,不是其他应用的预设根因或性能阈值。
4
+
5
+ ## 已有销毁正确,代码仍累积
6
+
7
+ gz-zd-web 宿主当时已执行完整 unmount,重开 loadApp({cache:false})。oms-service-web 子应用当时已安装样式 WeakMap 和 React bridge 手工补丁。空表交易管理预热约 50.63 MB,30 轮关闭态约 461.92 MB;ModuleMap 条目第 5→30 轮为 53→178,旧 body 第 30 轮 31 个。有数据关闭态的行对象已为 0,因此不能认定每轮泄漏一份交易数据。
8
+
9
+ 引用链包括 NativeContext/Window → Modulator → ModuleMap,以及 React createRoot 闭包 → FiberRoot.containerInfo → detached root/body。新实例生成的新 Blob ESM URL 形成新的模块身份;revoke 并不卸载已执行的原生模块。
10
+
11
+ 将页面及所有库都打包为每次执行的 IIFE 仍失败:约 58→87→115 MB(0/5/10 轮)。全局 handle → 匿名回调 toString → IIFE 上下文 → Garfish currentApp 持有旧容器。该匿名 handle 的具体所属方未确证,不能把所有保留都归咎于 Blob。
12
+
13
+ ## 最终方案及适用条件
14
+
15
+ 子应用生产构建拆为:
16
+
17
+ - 一个内容哈希稳定 URL 的原生 ESM 依赖运行时(React/表格/组件库等),同一构建、同一文档加载一次。
18
+ - 每次挂载新执行的经典 IIFE 页面入口,业务源码、store、请求配置和 provider 独立创建。宿主继续 cache:false 和完整 unmount,不缓存已关闭页面实例。
19
+
20
+ 异步 provider shim 先供 Garfish 发现,等待共享运行时加载后创建真正 provider。实现需要适配当前打包器的默认导出/命名空间互操作、入口签名、资源 URL、CSS、入口与依赖哈希关联;不是简单把 script type 改掉。独立页面与宿主加载两种资源路径均需验证。
21
+
22
+ 原生 ESM 没有主动逐模块卸载 API。容量边界是“一份构建/文档”;在同一文档热加载多个构建 URL 会保留多份。若业务要求同文档多版本淘汰,这个方案不能直接满足,需另行论证。不能用定期刷新补救菜单增长。
23
+
24
+ 其他修改:
25
+
26
+ - 宿主注册只存代码元数据,每次 load 传当前会话 props;退出擦除旧注册 props。Garfish 同名注册可能忽略后续注册,需检查所用版本。
27
+ - 将 util 的容器 Map→WeakMap 和 bridge 每次 provider 的 opts/domElements/renderResults/updateResolves 隔离补丁纳入可重放脚本。该案例 util 1.12.0/1.14.0、bridge 1.19.8,不能直接应用到其他版本。可重复 destroy 防护不是多调用 destroy 的理由。
28
+ - Vite 内联 Worker 解决宿主与子应用跨端口 Worker 加载;保留正常轮询与 owner 清理,验证真实线程确实运行。
29
+ - 开发/生产共用受控服务端 Mock,生产无浏览器 MSW/HMR。开发残留 HMR ping 被单独分类,未计为生产验收。
30
+
31
+ ## 证据与边界
32
+
33
+ 六组各 30 轮后约 55–58 MB;1000 条交替组延长到 100 轮约 58.14 MB。后期 DOM/监听稳定;模块条目固定 24;实际业务行、标记派生容器和活体表格 API 释放,Worker/轮询/请求归零。
34
+
35
+ 仍残留 2 个已销毁 App 和 2 个空 body 壳,30→100 轮不累积:一条为 Garfish 性能插件 domQuerySelector 闭包 → 已完成加载 Promise.result → 首个 App;另一条为共享 React 最后删除 Fiber 的 rootComponent 闭包 → Garfish env → 最后 App。active/mounted=false、provider undefined、appInfo.props 无键。未声称全部框架对象 GC,也未声称所有旧用户字符串安全擦除。
36
+
37
+ 本案例原始报告(若在当前工作站仍存在,可读取核对;技能运行不依赖该路径):
38
+ `gz-zd-web/docs/performance/2026-11-04-memory-lifecycle.md`
39
+
40
+ 原仓库 `mock-local/memory/` 内有采样、堆解析和回归驱动。它们依赖具体页面/端点/对象字段,移植时先检查并适配,不盲目执行。技能不携带大型堆文件、业务数据或机器专属浏览器会话。
41
+
42
+ ## 后续部署故障:Windows 路径被误判为依赖(2026-11-05)
43
+
44
+ 用户报告主子应用均用 `npm run build`,部署后加载超时。截图中子应用 `app-*.js` 仅 742 字节,runtime 也已返回 200,JavaScript MIME 和允许跨域响应头正常;本地验收入口约 741 KB。尺寸差异提示业务入口可能未进入预期页面包,但仅凭截图不能确认响应正文或完整因果链。
45
+
46
+ 源码级复现确认:插件原来用 `!id.startsWith('/')` 等条件判定裸依赖,`C:/repo/src/main.tsx` 和反斜杠形式均被 external 判为 true,POSIX 同类路径为 false。错误分类会将业务入口送入共享运行时,绕过页面构建的 provider 注入,与“资源成功但 provider 等待超时”的现场现象高度吻合。部署清单若包含 Windows 项目入口路径,可进一步确认现场命中该缺陷。
47
+
48
+ 修复在 `scripts/shared-dependency-runtime.mjs`:同时排除 POSIX/Windows 绝对路径;入口匹配统一分隔符;记录 applicationEntryObserved,入口未经转换则构建直接失败。新增路径分类、provider 注入和缺失入口失败测试;产物测试拒绝共享依赖清单中的绝对路径。
49
+
50
+ 已验证:3 项针对性回归测试、macOS `npm run build`、实际产物检查、定向 lint 和 diff 检查通过,入口仍约 741 KB。此记录写入时,尚未完成 Windows 实机构建与远端部署复验;不能将源码缺陷已修复写成部署故障已验收解决。原有 postMessage 警告也未在此次修复中处理。
51
+
52
+ 教训:共享运行时改造必须验证业务代码与依赖的分类边界,以及 provider 最终可用性;不同操作系统的路径是构建输入的一部分。本机内存验收通过不能替代部署平台兼容性验证。
53
+
54
+
55
+ 后续用户反馈:Windows 路径修复后部署成功并能手动开关页面。其截图显示交易管理约 30 次期间快照由 132→144→159→177 MB,随后交易审批约 10 次到178 MB;各快照准确轮次、GC和数据条件未完整提供。这支持部署加载已恢复的用户反馈,但不足以判定远端内存已形成平台;不能把原有本地 Mock 验收扩展为远端已通过。该记录也用于区分“修复加载成功”与“内存稳定性已验收”。