Skill Hub · 诺亚 AI 能力中心 静态参考版

让每一个技能成为可信资产 —— 诺亚 Agent 生态的技能资产中心:统一注册、检测把关、按需复用。本站为 skill-hub.noahgroup.com 于 2026-08-21 的全量数据镜像。

镜像采集 2026-08-2166 个已发布技能18 个分类 · 3 条分发渠道含技能包 SKILL.md 全文
← 返回目录

nav-review-ugl-report

SKILL_203551471 · vv1.1 · Olive 海外运营 · Owner:Olive 基金运营 · 发布于 2026-08-10
调用 17 下载 0 点赞 0 浏览 0
简介
重算NAV未实现损益并与管理员对账,生成自包含交互式HTML复核报告。
触发词
UGL复核,未实现损益,生成报告,NAV对账
分发渠道
ARK Engine
功能测试
✅ 通过 · 业务评审:✅ 通过
技能包文件
nav-review-ugl-report/SKILL.md、nav-review-ugl-report/references/inv_ugl_review_html_template.html、nav-review-ugl-report/references/inv_ugl_review_template.md
使用示例:生成某基金本期的未实现损益UGL复核HTML报告。

SKILL.md 全文

Frontmatter

namenav-review-ugl-report
description|

NAV Review Stage 3.3 — Investment Unrealized G/L Review 报告生成与上传 (HTML 版)

⛔ 模板强制遵循规则(最高优先级,不可跳过)

生成 HTML 报告时,必须先 read_file("references/inv_ugl_review_html_template.html") 加载模板,然后严格按照模板的完整结构生成报告。禁止自行设计布局、样式或交互方式。 具体要求: 1. 三栏布局不可变更:左侧导航栏(240px, #2F3542 深色背景) + 中间复核面板(flex:1) + 右侧 Supporting 证据面板(500px, sticky, #F1F5F9),三者缺一不可 2. CSS/JS 必须从模板复制:不得自行编写替代样式或脚本,必须使用模板中定义的完整 CSS 和 Vanilla JS 交互逻辑(含 Lightbox) 3. Section 结构必须与模板一致:UGL Roll-forward → MV Verification (MV=Cost+UGL) → Period Movement → MV Sources → MMF UGL → Conclusion,不得省略、合并或重新排序 4. 紫色主题色(#9c27b0)不可变更:导航栏标题、Section 标题下划线、交互高亮等均使用模板定义的紫色主题 5. 交互行为必须保留.interactive-row + data-supporting-id 的点击联动、双击 Lightbox 预览等交互功能必须完整实现 6. 禁止降级为单栏/双栏布局:任何情况下不得生成简单的单页报告或传统文档式布局 违反上述任何一条即视为报告生成失败,必须重新生成。 ---

⛔ Supporting 证据来源限制(强制,最高优先级)

只有第三方/外部独立文件才能作为 Supporting 证据。基金自身内部系统生成的文件严禁作为 Supporting。 | 禁止作为 Supporting | 允许作为 Supporting | |---|---| | ❌ Investment Position Appraisal (IPA/N4) | ✅ 基金行政管理人报表(Citco Market Value Statement) | | ❌ Trial Balance (TB) / TB Detail | ✅ Citco Capital Account Statement | | ❌ Purchases and Sales Report | ✅ 交易合同/确认函(Contract Note) | | ❌ Nominal Ledger / Detailed General Ledger | ✅ 银行付款确认(Cash Payment Confirmation) | | ❌ Statement of Operations (SOFI) | ✅ 托管人报告 | | ❌ Change in Unrealized Gains Losses Report(N4系统生成) | ✅ 银行对账单(DBS Bank Statement 等) | 核对逻辑:中间面板的复核数据可引用 IPA/TB/Change in UGL Report 数字进行对比展示,但右侧 Supporting 面板只展示第三方证据截图来佐证这些数字的正确性。对于 Unrealized G/L 核对,典型第三方证据为:Citco Market Value Statement(确认每只持仓的 NAV Price 和 Market Value)。如果某个核对项无对应第三方文件,则该行不设置 Supporting 证据。 ---

⛔ Supporting 面板截图与计算过程规则(强制)

规则 A:高清截图 + 红框高亮关键数据

每个 .supporting-item 必须包含对应源文件的高清截图(base64 内嵌),且用红框高亮标注关键数字/字段: 1. 截图范围 — 按数据密度决定 2. 红框高亮规则 3. 截图分段标题:每张截图上方须有 <div class="sup-section-label"> 标注来源文件名和取数用途,如 "Citco MV Statement — SR13 Market Value (May-31)"

规则 B:计算过程 Supporting(涉及测算时强制输出)

当复核过程中涉及数值测算(而非简单读取对比)时,必须在该行对应的 .supporting-item 最下方新增一个计算过程区块。典型场景包括但不限于: 计算过程区块格式(混合样式)
<div class="calc-block">
  <div class="calc-title">📐 Calculation: [计算目的,如 "UGL Verification for SR13"]</div>
  <pre class="calc-steps">
Step 1: Identify Market Value
  MV = Units × NAV Price
     = 14,046.3646 × 1,074.11
     = 15,087,340.68 (from Citco MV Statement)

Step 2: Identify Verified Cost
  Cost = 12,878,128.21 (from verified_cost.json)

Step 3: Calculate UGL
  UGL = MV − Cost
      = 15,087,340.68 − 12,878,128.21
      = 2,209,212.47
  </pre>
  <div class="calc-result">✓ Result: USD 2,209,212.47 — matches N4 UGL</div>
</div>
CSS 样式(须写入 HTML <style> 中)
.calc-block {
  margin-top: 12px;
  border: 1px solid #E2E8F0;
  border-radius: 6px;
  overflow: hidden;
  background: #FAFBFC;
}
.calc-title {
  padding: 8px 12px;
  font-size: 12px;
  font-weight: 600;
  color: #1E293B;
  background: #F1F5F9;
  border-bottom: 1px solid #E2E8F0;
}
.calc-steps {
  padding: 10px 14px;
  font-size: 11px;
  font-family: 'Consolas', 'Courier New', monospace;
  line-height: 1.6;
  color: #334155;
  white-space: pre-wrap;
  margin: 0;
  background: #FAFBFC;
}
.calc-result {
  padding: 8px 12px;
  font-size: 12px;
  font-weight: 600;
  color: #0A8754;
  background: #E6F4ED;
  border-top: 1px solid #B7E4C7;
}
执行规则 ---

When to Use

本 skill 只生成 Investment Unrealized GL Review HTML 交互式报告 + iGowin 上传。HTML 即为最终交付物,不再生成 PDF。 ---

输入

输出

| 报告 | HTML 文件名 | iGowin Subject | |------|------------|----------------| | Investment Unrealized GL Review | Investment Unrealized GL Review - [基金名] [YYYYMMDD].html | Unrealized G/L 子科目 | HTML 交互式报告输出至 <target_dir>/ 根目录(用户双击可直接在浏览器中打开),并作为 iGowin 系统上传的最终交付物。中间脚本放 <target_dir>/.cortex/。 ---

外置文件索引

| 文件 | 路径 | 加载时机 | |------|------|---------| | Investment UGL Review 内容模板 | references/inv_ugl_review_template.md | 启动时 read_file(Section 结构 + 核对逻辑) | | Investment UGL Review HTML 模板 | references/inv_ugl_review_html_template.html | 启动时 read_file(固定 HTML/CSS/JS 三栏结构) | | iGowin 上传流程 | ../nav-review-report/references/igowin_upload.md | 上传阶段 read_file |
所有路径相对于 skill 目录:c:\Users\syy10787\.cortex\skills\nav-review-ugl-report\
---

UI 与格式约束

HTML 报告为最终交付物,必须采用响应式、现代化双栏交互式设计,提供极佳的屏幕核对体验:

1. 结构与排版布局

2. 交互体验设计 (Vanilla JS)

3. 颜色与视觉风格

---

执行流程

Step 1:加载 profile + 数据 + 模板

编写 Python 脚本加载数据:
import os, json, sys

BASE = r"<target_dir>"
PROFILE = json.load(open(os.path.join(BASE, ".cortex", "nav_profile.json"), encoding="utf-8"))
VERIFIED_COST = json.load(open(os.path.join(BASE, ".cortex", "nav_data", "verified_cost.json"), encoding="utf-8"))
⚠️ Guard:若 verified_cost.json 不存在,立即停止并提示用户先完成 Investment Cost Review,不允许 fallback 自行验证 cost。
⚠️ 必须 read_file 加载 references/inv_ugl_review_html_template.html,生成报告时严格遵循此 HTML 模板的三栏布局结构、CSS 样式(紫色主题色 #9c27b0)、交互式 JS 脚本。模板定义了完整的 Section 结构(TB vs N4 → UGL Roll-forward → MV Verification → Period Movement → MV Sources → MMF UGL → Conclusion),不得省略或改变顺序。

Step 2:生成交互式 HTML 报告

使用 Python 脚本拼装 HTML 字符串,将数据填入 HTML/CSS 模板中: 1. 渲染左侧导航栏:显示 4 个 Section 链接: 2. 渲染中间复核面板:根据 verified_cost.jsoninvestment_support.json 逐持仓/Series 循环生成表格行。行中插入 class="interactive-row" data-supporting-id="support-[持仓名]_ugl"。 3. 渲染右侧 Supporting 面板:在 .supporting-panel 中,针对每个持仓的截图,渲染 <div id="support-[持仓名]_ugl" class="supporting-item">,内部包含来自 .cortex/screenshots/inv_ugl/ 目录的图片。 ⚠️ 截图内嵌强制规则:所有截图必须以 base64 data URI 内嵌到 HTML 文件中,禁止使用相对路径或外部文件引用。实现方式:在 Python 脚本中,对每个 PNG 文件读取二进制内容并转为 base64 编码,然后以 data:image/png;base64,<base64_string> 格式写入 <img src="...">
   import base64

   def embed_image(base_dir, folder, filename):
       """读取截图文件并返回 base64 data URI"""
       path = os.path.join(base_dir, ".cortex", "screenshots", folder, filename)
       if os.path.exists(path):
           with open(path, "rb") as f:
               data = base64.b64encode(f.read()).decode("utf-8")
           return f"data:image/png;base64,{data}"
       return ""  # 文件不存在时返回空字符串
   
HTML 中使用:<img src="{embed_image(BASE, 'inv_ugl', 'prior_mv.png')}" alt="Prior MV"> > 理由:内嵌截图使 HTML 文件完全自包含,双击即可在任何位置打开查看,不依赖 .cortex/ 目录结构。上传至 iGowin 后也能直接在浏览器中完整展示,无需额外文件。 ⚠️ 截图清晰度强制规则:使用 PyMuPDF (fitz) 渲染 PDF 页面为 PNG 时,必须使用 4x zoomfitz.Matrix(4, 4)),确保文字和数字在浏览器中清晰可读。2x zoom 不足以保证小字清晰度。 ⚠️ MV & UGL Overview(Section 1)Supporting 强制规则:Section 1 每行的 Supporting 面板必须同时展示两类截图,缺一不可: 实现方式:每个 Section 1 行对应一个复合 .supporting-item,内部用分段标题(<div class="img-title">,样式见下文)将多张截图分组展示,每段标题清晰标注取数用途。所有 <img> 标签的 src 均使用 embed_image() 返回的 base64 data URI。 > 分段标题样式padding:8px 12px; font-size:12px; color:#475569; background:#F8FAFC; border-bottom:1px solid #E2E8F0; border-top:1px solid #E2E8F0; margin: 10px -15px;(首段无 border-top)。 > 强制原因:MV 不可自证,IPA/TB/FS Pack 不能作为唯一证据。Section 1 是 MV & UGL 整体陈述,必须同时呈现 IPA 取值和第三方原始 MV 证据,让审阅人一眼判断"IPA MV = 第三方 MV"是否成立。仅展示 IPA 截图等同于自证,违反 MV 取数强制规则。 其他 Section(Market Value Verification、Unrealized G/L Reconciliation 等)的 Supporting 面板按各自需求展示对应单一或少量截图,无需复合,但同样必须使用 base64 内嵌。 4. 嵌入交互式 JS 脚本(双击截图 → 全屏 Lightbox 预览 + 工具栏缩放 + 滚轮缩放 + 拖拽平移): CSS 新增(在 <style> 末尾添加):
   / ── Supporting 图片悬停提示 ── /
   .supporting-item img {{
       cursor: zoom-in;
       transition: opacity 0.15s;
   }}
   .supporting-item img:hover {{ opacity: 0.9; }}

   / ── Lightbox 全屏遮罩 ── /
   .lightbox-overlay {{
       display: none;
       position: fixed;
       inset: 0;
       background: rgba(0, 0, 0, 0.88);
       z-index: 9999;
       overflow: hidden;
   }}
   .lightbox-overlay.active {{ display: block; }}
   .lightbox-toolbar {{
       position: fixed;
       top: 12px;
       left: 50%;
       transform: translateX(-50%);
       display: flex;
       align-items: center;
       gap: 8px;
       padding: 6px 12px;
       background: rgba(255, 255, 255, 0.95);
       border-radius: 8px;
       box-shadow: 0 4px 14px rgba(0,0,0,0.35);
       font-size: 12px;
       color: #1E293B;
       z-index: 10000;
       user-select: none;
   }}
   .lightbox-btn {{
       border: 1px solid #CBD5E1;
       background: #F8FAFC;
       color: #1E293B;
       width: 30px;
       height: 26px;
       line-height: 1;
       font-size: 14px;
       border-radius: 4px;
       cursor: pointer;
       padding: 0;
   }}
   .lightbox-btn:hover {{ background: #E2E8F0; }}
   .lightbox-btn.reset,
   .lightbox-btn.close {{ width: auto; padding: 0 10px; font-size: 11px; }}
   .lightbox-btn.close {{ border-color: #FCA5A5; color: #B91C1C; background: #FEF2F2; }}
   .lightbox-level {{ min-width: 46px; text-align: center; font-variant-numeric: tabular-nums; }}
   .lightbox-stage {{
       position: absolute;
       inset: 0;
       overflow: auto;
       cursor: grab;
       display: flex;
       align-items: center;
       justify-content: center;
   }}
   .lightbox-stage.grabbing {{ cursor: grabbing; }}
   .lightbox-stage img {{
       max-width: 95vw;
       max-height: 95vh;
       transform-origin: center center;
       user-select: none;
       -webkit-user-drag: none;
       transition: transform 0.05s linear;
       box-shadow: 0 0 30px rgba(0,0,0,0.5);
   }}
   
HTML 结构(在 </body> 之前注入一次即可):
   <div class="lightbox-overlay" id="lightbox" aria-hidden="true">
       <div class="lightbox-toolbar">
           <button class="lightbox-btn" data-zoom="out" title="缩小">−</button>
           <span class="lightbox-level">100%</span>
           <button class="lightbox-btn" data-zoom="in" title="放大">+</button>
           <button class="lightbox-btn reset" data-zoom="reset" title="重置">Reset</button>
           <button class="lightbox-btn close" data-zoom="close" title="关闭">✕ 关闭</button>
       </div>
       <div class="lightbox-stage" id="lightbox-stage">
           <img id="lightbox-img" src="" alt="Zoomed view" />
       </div>
   </div>
   
JS 完整脚本
<script>
    document.addEventListener('DOMContentLoaded', function() {
        const rows = document.querySelectorAll('.interactive-row');
        const items = document.querySelectorAll('.supporting-item');
        const placeholder = document.getElementById('no-supporting-placeholder');

        if (items.length > 0) {
            activateSupporting(items[0].id);
            const firstRow = document.querySelector('.interactive-row[data-supporting-id="' + items[0].id + '"]');
            if (firstRow) firstRow.classList.add('highlighted');
        }

        rows.forEach(row => {
            row.addEventListener('click', function() {
                rows.forEach(r => r.classList.remove('highlighted'));
                this.classList.add('highlighted');
                activateSupporting(this.getAttribute('data-supporting-id'));
            });
        });

        function activateSupporting(id) {
            if (!id) return;
            const targetItem = document.getElementById(id);
            if (targetItem) {
                if (placeholder) placeholder.style.display = 'none';
                items.forEach(i => i.classList.remove('active'));
                targetItem.classList.add('active');
                targetItem.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
            }
        }

        const ZOOM_MIN = 0.5, ZOOM_MAX = 4.0, STEP_BTN = 0.25, STEP_WHEEL = 0.1;
        const overlay = document.getElementById('lightbox');
        const stage = document.getElementById('lightbox-stage');
        const lbImg = document.getElementById('lightbox-img');
        const lbLevel = overlay ? overlay.querySelector('.lightbox-level') : null;
        let scale = 1;

        const apply = () => {
            if (lbImg) lbImg.style.transform = 'scale(' + scale + ')';
            if (lbLevel) lbLevel.textContent = Math.round(scale * 100) + '%';
        };
        const setScale = v => { scale = Math.min(ZOOM_MAX, Math.max(ZOOM_MIN, v)); apply(); };

        function openLightbox(src, alt) {
            if (!overlay || !lbImg) return;
            lbImg.src = src;
            lbImg.alt = alt || 'Zoomed view';
            scale = 1;
            apply();
            if (stage) { stage.scrollLeft = 0; stage.scrollTop = 0; }
            overlay.classList.add('active');
            overlay.setAttribute('aria-hidden', 'false');
        }
        function closeLightbox() {
            if (!overlay) return;
            overlay.classList.remove('active');
            overlay.setAttribute('aria-hidden', 'true');
            if (lbImg) lbImg.src = '';
        }

        document.querySelectorAll('.supporting-item img').forEach(img => {
            img.addEventListener('dblclick', function(e) {
                e.preventDefault();
                e.stopPropagation();
                openLightbox(this.src, this.alt);
            });
        });

        if (overlay) {
            overlay.querySelectorAll('.lightbox-btn').forEach(btn => {
                btn.addEventListener('click', e => {
                    e.preventDefault();
                    e.stopPropagation();
                    const action = btn.getAttribute('data-zoom');
                    if (action === 'in')    setScale(scale + STEP_BTN);
                    if (action === 'out')   setScale(scale - STEP_BTN);
                    if (action === 'reset') {
                        if (stage) { stage.scrollLeft = 0; stage.scrollTop = 0; }
                        setScale(1);
                    }
                    if (action === 'close') closeLightbox();
                });
            });

            overlay.addEventListener('click', e => {
                if (e.target === overlay || e.target === stage) closeLightbox();
            });

            document.addEventListener('keydown', e => {
                if (e.key === 'Escape' && overlay.classList.contains('active')) closeLightbox();
            });

            stage.addEventListener('wheel', e => {
                if (!overlay.classList.contains('active')) return;
                e.preventDefault();
                const rect = stage.getBoundingClientRect();
                const px = e.clientX - rect.left + stage.scrollLeft;
                const py = e.clientY - rect.top + stage.scrollTop;
                const oldScale = scale;
                setScale(scale + (e.deltaY < 0 ? STEP_WHEEL : -STEP_WHEEL));
                const ratio = scale / oldScale;
                stage.scrollLeft = px * ratio - (e.clientX - rect.left);
                stage.scrollTop  = py * ratio - (e.clientY - rect.top);
            }, { passive: false });

            let dragging = false, sx = 0, sy = 0, ssl = 0, sst = 0;
            stage.addEventListener('mousedown', e => {
                if (e.button !== 0) return;
                if (e.target.closest('.lightbox-toolbar')) return;
                dragging = true;
                sx = e.clientX; sy = e.clientY;
                ssl = stage.scrollLeft; sst = stage.scrollTop;
                stage.classList.add('grabbing');
                e.preventDefault();
            });
            document.addEventListener('mousemove', e => {
                if (!dragging) return;
                stage.scrollLeft = ssl - (e.clientX - sx);
                stage.scrollTop  = sst - (e.clientY - sy);
            });
            document.addEventListener('mouseup', () => {
                if (!dragging) return;
                dragging = false;
                stage.classList.remove('grabbing');
            });
        }
    });
</script>
5. 将完整 HTML 内容写入 <target_dir>/Investment Unrealized GL Review - [基金名] [YYYYMMDD].html

Step 3:上传 iGowin

read_file("../nav-review-report/references/igowin_upload.md") 获取上传流程,然后执行 6 步(上传 HTML 文件本身,不再做 PDF 转换): 1. get_nav_packs(fund_name) → 获取 applicationNo 2. get_subject_names(application_no) → 找到 Unrealized G/L 类 subject_name 3. get_upload_url(file_name, content_type="text/html") → 拿到 uploadUrl + objectName 4. ⚠️ 上传前确认关卡(强制):调用 ask_questions 弹窗向用户展示完整上传摘要并取得明确确认。摘要必须包含:基金名、NAV 日期、流程单号、目标 subject、文件名、文件大小(MB)、报告核对结论(PASS / FAIL,及差异金额)。选项至少包含「确认上传」「跳过该报告」「全部取消」。未取得确认前,不得执行 Step 5 的 PUT 上传或 Step 6 的 save_check_result。即便此前在主 skill 入口已确认整体计划,此处仍须再次确认(理由见 igowin_upload.md 的"强制原则"段)。 5. PowerShell 上传(仅在 Step 4 通过后执行):Invoke-WebRequest -UseBasicParsing -Method Put -Uri $uploadUrl -InFile $htmlPath -ContentType "text/html" 6. save_check_result(object_name, file_name, subject_name, application_no)(仅在 Step 5 PUT 返回 200 OK 后执行) ---

完成检查表

---

截图质量与标注规则

截图清晰度要求(最高质量)

清晰度为第一优先级。所有内嵌 HTML 的截图必须确保数字、文字完全清晰可读。 | 参数 | 值 | 说明 | |------|-----|------| | DPI | 150 | HTML 内嵌 JPEG 渲染分辨率 | | JPEG quality | 85 | 高品质,文字无锯齿 | | 红框线宽 | 3px | PIL ImageDraw,高清图上清晰可见 | | 落盘 PNG | 300 DPI | 独立 PNG 文件最高清晰度 | | 宽表 zoom | | IPA 等宽表裁剪用 4x zoom | 不设单文件体积上限。如需控制体积,通过 clip_box 裁剪区域(只截关键数据行),禁止降低 DPI 或 quality
import fitz, io, base64
from PIL import Image, ImageDraw

def screenshot_for_embed(pdf_path, page_num, red_rects=None, clip_box=None, dpi=150, quality=85):
    """高清 JPEG 截图用于 HTML 内嵌"""
    doc = fitz.open(pdf_path)
    page = doc[page_num]
    mat = fitz.Matrix(dpi/72, dpi/72)
    pix = page.get_pixmap(matrix=mat, clip=fitz.Rect(*clip_box) if clip_box else None)
    img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
    if red_rects:
        draw = ImageDraw.Draw(img)
        scale = dpi / 72.0
        ox, oy = (clip_box[0], clip_box[1]) if clip_box else (0, 0)
        for box in red_rects:
            draw.rectangle([(box[0]-ox)scale-1, (box[1]-oy)scale-1,
                           (box[2]-ox)scale+1, (box[3]-oy)scale+1], outline='red', width=3)
    buf = io.BytesIO()
    img.save(buf, format='JPEG', quality=quality, optimize=True)
    doc.close()
    return f"data:image/jpeg;base64,{base64.b64encode(buf.getvalue()).decode()}"

截图红框标注规则

所有 Supporting 截图必须用红框高亮关键数字: 1. 定位方法:使用 page.get_text("words") 精确提取数字坐标 2. 标注内容:仅标注 MV、NAV/share、closing shares、period change 等核对关键金额 3. 红框样式:红色描边 3px(PIL),无填充,略扩展 3px 边距 4. 禁止:圈整行/大区域、不透明填充、标注非关键信息

数据层级标注强制规则 (Data Hierarchy)

报告中必须严格区分 Reference(被核对对象)Supporting(第三方证据): | Category | Documents | 标签色 | |----------|-----------|--------| | Reference(被核对) | TB, IPA, Change in UGL Report, Realized GL Report, Maples NAV Per Share, Statement of Operations | 紫色 #f3e5f5 | | Supporting(第三方证据) | SS&C / GP Investor Statement, Contract Note, Bank Statement | 绿色 #e8f5e9 | 强制规则 ---

关键注意事项

1. 本 skill 只负责 Investment Unrealized G/L Review,不涉及 Cash 或 Cost。 2. HTML 即最终交付物:不再生成 PDF,所有上传与展示统一基于 HTML 文件。 3. 所有截图必须以 base64 data URI 内嵌到 HTML 文件中,使 HTML 完全自包含,不依赖外部文件。禁止使用相对路径或外部文件引用。 4. 不要使用任何第三方繁重 JS 库,交互动作必须全部由精简的原生 Vanilla JS 编写。 5. Book Cost 必须来自 verified_cost.json,这是与 Cost Review 的强制契约,违反即 FAIL。 6. MV 不可自证:必须从第三方原始文件取数,IPA / TB / FS Pack 仅作参考。Section 1 Supporting 必须双覆盖(IPA 取值截图 + MV Supporting File 第三方原始证据截图),不允许只展示 IPA 截图。 10. MV 必须取截至期末当天(Period End Date)的定价数据(强制,最高优先级) 1. 首选:当期结单中 Price Date = 期末日的数据 → 直接使用 2. 次选:次月结单(subsequent month)中 Price Date = 期末日的数据 → 使用此数据 3. 无法获得:若所有可用文件中均无 Price Date = 期末日的 MV → 必须暂停并用 ask_questions 向用户确认:"[持仓名] 无法找到截至 [期末日] 的 MV 数据,最近可用日期为 [X],是否使用该日期数据继续?" 7. 逐持仓明细不可合并:同一底层基金的多个 Series 必须逐一单独列示。 8. 截图清晰度必须使用 4x zoom 渲染:PyMuPDF 渲染 PDF 页面时使用 fitz.Matrix(4, 4)(最高清晰度),2x zoom 不足以保证小字清晰度。双击 Supporting 截图打开 Lightbox 预览(顶部 −/倍数/+/Reset/✕ 工具栏 + 滚轮缩放 + 拖拽平移)让审阅人可进一步查看细节。 9. iGowin 上传前必须经过 ask_questions 强制确认PUT 上传与 save_check_result 都是不可逆操作,因此在调用 get_upload_url 之后、PUT 之前必须ask_questions 弹窗向用户确认(含基金名 / NAV 日期 / 流程单号 / subject / 文件名 / 文件大小 / 核对结论)。即使本 skill 被 nav-review 主 skill 调用、用户已在主入口确认过整体计划,此处仍须再次确认;本 skill 单独运行时同样适用。用户选择「跳过」则不上传该报告,「全部取消」则终止。不允许通过任何"批处理静默上传"绕过此关卡。 11. 持仓数据必须来自 N4 IPA 原始报告(强制,禁止虚构) 12. BM Resolution 截图必须完整展示估值金额(强制)