---
name: quant-buddy-view
slug: quant-buddy-view
author: guanzhao
version: 0.6.70
description: |
  QBV / quant-buddy-view（用户可能写成 /quant-buddy-view、/qbv、qbv 或 QBV）用于把量化数据做成「公开可分享、实时取数」的网页看板/落地页。
  Use this skill when the user asks to create, update, publish, verify, retrofit, or reuse a Quant Buddy dashboard/static page/template, including shareable pages, public URLs, formula packages, share shell, cover/essence cards, poster/share behavior, single-stock profile pages, valuation/financial profile pages, index-anomaly boards, multi-factor screeners, and commodity daily pages.
  配合 quant-buddy-skill 使用：简单单一 A 股综合分析可在 trace begin 后直接用 static_page.py new_asset_page 返回实时页面；用户给出既有 QuantBuddy 活页 URL 并要求解读时，直接用 static_page.py interpret 读取该页实时数据，不下载 HTML、不暴露签名，也不进入模板或建页流程；其他固定页面请求先用 templates/template 选择带 recommend 标签的在线范式页。自建实时页先按数据性质选择通道：普通行情、估值和财务优先 Data Grant，自定义计算才验证并注册 Formula Package；两类凭证可在同页混用，随后替换凭证/文案、浏览器验收并发布。默认不从本地历史样板目录或低质 HTML 骨架起步。
  用户显式唤起 /quant-buddy-view、/qbv、qbv 或 QBV，且请求不是纯咨询/代码维护/文档解释时，默认视为可分享活页任务：简单单一 A 股分析走 new_asset_page 快速终态；其余请求查官方精选+社区范式卡判定 direct/fork/unmatched。默认 direct 先交付现成链接、fork/unmatched 用 new_page 返回首链；当 config.json._channel=feishu-group 时，所有分支都禁止提前发送链接，只在终态交付 playground 链接。
  Do not use this skill for one-off 行情查询、普通股票涨跌幅/估值问答、选股/回测探索；those belong to quant-buddy-skill unless the user explicitly wants a reusable/shareable page.
runtime: python
primaryCredential: quant-buddy API Key
metadata:
  version: 0.6.70
  author: guanzhao
  category: quant-finance
  tags: [quant, dashboard, formula-package, static-page, publish, visualization]
  runtime: python
  primaryCredential: quant-buddy API Key
  requiredCredentials:
    - quant-buddy API Key
  requiredConfigPaths:
    - config.json
  networkEndpoints:
    - https://www.quantbuddy.cn/skill
    - https://www.quantbuddy.cn/user
    - https://pages.quantbuddy.cn
requiredCredentials:
  - name: quant-buddy API Key
    required: true
    sensitive: true
    storage: config_file
    path: config.json
    field: api_key
    description: quant-buddy 平台 API Key。默认存储于 skill 目录下 config.json 的 `api_key` 字段。优先级（高到低）：① 调用方在工具调用参数里显式传入的 `api_key`（如 Playground 场景，仅当次调用生效，不落盘）；② 环境变量 QBV_API_KEY（同一档的显式覆盖通道，专给"这次调用要用哪个 key"、但不方便/不想改现有 @file 参数去塞 api_key 的场景，比如 `publish_workflow.py @publish-plan.json`——该 plan 文件按设计不含凭证）；③ config.json / config.local.json 的 `api_key`；④ 环境变量 QUANT_BUDDY_API_KEY（仅①②③都为空时才兜底，不是常规覆盖手段，语义与 QBV_API_KEY 完全不同，不要混用）。仅作为 HTTP `Authorization` 头发送给 networkEndpoints 中声明的 quantbuddy 域名用于鉴权；Formula Package 与 Data Grant 的页面内实时取数都凭 signature，不需要 api_key。
    how_to_get: "https://www.quantbuddy.cn/login"
requiredConfigPaths:
  - path: config.json
    required: true
    description: 仅包含 quant-buddy api_key 与公开端点配置，由本地脚本读取。
requiredEnvVars:
  - name: QBV_API_KEY
    required: false
    sensitive: true
    description: 可选，本次调用的显式 api_key 覆盖（与工具调用参数里的 `api_key` 字段同一优先级），仅本进程生效、不落盘。适用于"手上是一份现成的 @file 参数（如 publish-plan.json），不想现改这份文件去塞 api_key"的场景；不要和 QUANT_BUDDY_API_KEY 混用，两者优先级和用途完全不同（见上面 api_key 字段的优先级说明）。
  - name: QUANT_BUDDY_API_KEY
    required: false
    sensitive: true
    description: 可选。仅在 config.json / config.local.json 都没有 api_key、且没有更高优先级的 api_key/QBV_API_KEY 覆盖时才兜底生效，不是常规覆盖手段。多步任务里想让某次调用用别的 key，请用 api_key 参数或 QBV_API_KEY，不要指望设置这个环境变量会覆盖 config.json 已有的默认 key。
  - name: QBV_AGENT_MODEL
    required: false
    sensitive: false
    description: 可选。宿主明确知道当前 Agent 的真实运行模型时可注入；优先级低于调用参数里的 agent_model、高于 task-scoped Trace Context。拿不准时留空，禁止猜测，也不得为了补该字段询问用户或阻断活页流程。
networkAccess: true
networkEndpoints:
  - https://www.quantbuddy.cn/skill
  - https://www.quantbuddy.cn/user
  - https://pages.quantbuddy.cn
runtimeRequirements:
  python: "3.8+"
  packages: []
---

# quant-buddy-view · 量化看板发布

把「已验证的量化数据与公式」沉淀成一个**公开可分享、实时取数**的网页看板/落地页。本技能不做一次性行情查询或回测探索；默认执行路线是：
> **feishu-group 渠道**：打包渠道为 `feishu-group` 时，direct/fork/unmatched/update 等所有分支禁止发送非终态链接；终态 contract 统一把 `pages.quantbuddy.cn/pages/<owner>/<page_id>.html` 转成 `www.quantbuddy.cn/playground/<owner>/<page_id>`，内部发布与验收仍使用原始托管 URL。

**最高优先级：既有活页解读。** 用户给出 `pages.quantbuddy.cn/pages/...` 的 QuantBuddy 活页 URL，且意图是“解读 / 分析当前活页 / 看这页数据”时，先且只运行：

```bash
python scripts/static_page.py interpret '{"url":"用户提供的页面 URL"}'
```

这是只读数据路径，**不要**运行 `trace_context.py`、`templates`、`template`、`direct_deliver`、`new_page`、fork、`download`、浏览器或 HTML 搜索，也不要创建、更新、发布页面。它调用 `getPageDetail?need_data=true`；服务端使用页面绑定的公式包和 Data Grant 取最新数据，并附加 `interpretation_bundle`，不返回 signature。直接按用户的自定义要求解读详情与 `interpretation_bundle.runtime_data`。未指定格式时，依次输出一句话结论、关键指标及变化、风险/异常、3 个继续追问方向。详见 [workflows/interpret-existing-page.md](workflows/interpret-existing-page.md)。

若 `interpretation_bundle.runtime_data.grants[].data.mode="csv"`，先返回的 `csv_fields[].csv_url` 是短期下载链接而非可直接计算的数据。必须紧接着运行一次 `python scripts/static_page.py interpret_csv '{}'`：它只下载该次 `interpret` 已返回的 CSV、保留链接并补出 `results[].fields[].series`，然后再计算和解读；禁止重跑 `interpret`、另查数据接口或把 CSV 链接给用户。

0. 除上述既有活页解读分支外，在任何后端请求前运行 `scripts/trace_context.py begin`，保存唯一 `task_id` 并在后续命令中复用。这步本身就是后端写入调用，必须和后续命令带同一个身份（`QBV_API_KEY` 环境变量或参数里的 `api_key`），不带会被记成 skill 默认账号。
1. 若用户只是要**简单分析一只 A 股并返回页面**，且没有定制栏目/版式、额外指标/公式/图表、对比或多标的要求，直接运行一次 `scripts/static_page.py new_asset_page`。成功结果包含完整数据草稿 `agent_reply_markdown_draft`；当前 Agent只需依据用户原问题和草稿前五章数据补写综合观察，不再查 templates，也不另跑 QBS 验证或注册 Grant。
2. 除上述快速场景外，运行一次 `scripts/static_page.py templates`，查询统一 public 命中池（服务端一次返回官方精选+社区）。
3. direct 只有在范式、范围和全部请求维度三轴均有证据时成立；`direct_deliver` 必须提交 `dimension_check`。缺维度改走 fork + `same_paradigm_augment_dimension`。
4. fork/unmatched 调用 `new_page` 时由 Agent 根据 `items_summary` 显式传 `routing_decision`；fork 还必须声明 `borrow_mode=inherit|inherit_augment|compose`。fork 一旦判定只能继承、增强继承或 Compose，禁止改判 unmatched。
5. `new_asset_page` 成功后，按 `agent_summary_request` 用当前 Agent补写草稿中的唯一 `summary_marker`，保持其余内容不变并立即发送；direct、fork/unmatched 仍按 `agent_reply_contract` 和回复模板生成证据绑定草稿，再运行返回的 `reply_validation_command`，只有 `valid=true` 才最终回复。

> **多轮追问**：首次用户消息运行 `scripts/trace_context.py begin`；同一 `task_id` 的每条后续用户消息先运行 `scripts/trace_context.py beginTurn`。正常 Agent 必须同时传本轮可选 `agent_intent`：简洁展开上下文指代并写清对象、动作、约束和期望页面/产物，推荐 20～160 字；不得复制用户原话、输出内部推理或提前编造结论。老调用方可省略并按 `null` 继续。一轮内所有 QBV/QBS 工具共享同一 `turn_id`。Turn 是审计旁路：服务端记录失败会返回 `tracking_recorded:false`，但不得阻断建页、更新、取数或发布；业务上下文继续切换到真实 `user_query` / `agent_intent`，attempted `turn_id` 不保存、不传播，后续按无 Turn 模式继续。更新既有活页必须继续复用原 `page_id` 与公开 URL。

> **QBS 并行 Handoff**：收到 `qbs_qbv_handoff_v1` 时运行 `scripts/trace_context.py beginHandoff`（兼容 `begin-handoff`），传入 Handoff object 或绝对 `handoff_file`。必须原样复用其中真实 `task_id + turn_id + source_skill_id`，不得再次 `begin/beginTurn`、不得在 QBV 重做 QBS 路由分类。`create/existing_page` 之后仍进入本 Skill 完整 SOP，由 QBV 判断 direct/fork/unmatched、查询 ownership 并执行本人原位更新或他人复制；高风险持久状态未确认时 `beginHandoff` 必须拒绝。


## 何时用本技能 vs quant-buddy-skill

- **探索/一次性查询**（"茅台今天涨跌幅"、"跑个均线金叉回测看看"）→ 用 **quant-buddy-skill**。
- **要一个能反复看、能发给别人、数据会自动更新的页面** → 探索清楚后切到 **quant-buddy-view**。

## 新会话路由：单股快速返回 / 其余查范式卡

先建立 Trace Context。`begin` 是**真实的后端写入调用**（落审计表），和后续命令一样需要本次任务的身份——必须与后续命令用同一个 key，否则这一步会被记到 skill 默认账号名下，任务链路从第一条记录起就归错人：

```bash
# 身份走环境变量（exec 日志里会脱敏）；不要把 key 拼进命令串，命令是原样记录的
QBV_API_KEY=<本次任务的 key> python scripts/trace_context.py begin '{"user_query":"那和五粮液比呢？","agent_intent":"延续上一轮贵州茅台分析，对比五粮液的盈利能力、估值水平与主要风险。","agent_model":"当前真实运行模型（明确知道时才传）"}'
```

`agent_intent` 与本轮 `user_query` 绑定：首问、每次追问分别保存，追问要展开“它/上一个/继续”等指代；缺失、空白或旧 Trace 文件均按 `null`，不能从 `user_query` 伪造。QBS Handoff 继续使用 `qbs_qbv_handoff_v1`，可选携带同一 Intent；Intent 差异不得制造第二个 Turn、拒绝 Handoff 或改变 Job 身份。

`agent_model` 是纯可选审计字段：明确知道当前 Agent 的真实运行模型时建议传入；不确定时直接省略，禁止猜测，也不要询问用户。宿主也可通过可选环境变量 `QBV_AGENT_MODEL` 注入。模型名按“显式参数 → `QBV_AGENT_MODEL` → 当前 `task_id` 的任务临时上下文 → 空”解析；缺失、纯空白或上下文读写失败都不得中断任务，非空值会通过 `x-agent-model` 自动贯穿后续命令与 QBS bridge。

保存返回的 `task_id`，并把它加入本次任务后续每个 `static_page.py`、`formula_package.py`、`data_grant.py` 参数。脚本会通过 `x-task-id` 请求头透传，使后台能从提问一直聚合到最终活页链接。`new_asset_page` / `templates` / `upload` / `update` / `publish_final` / `publish_verified` 缺少 Trace Context 时必须停止执行。QBV 编排中的 quant-buddy-skill 工具统一通过 `scripts/qbs_bridge.py <tool> @params.json` 调用，并显式传同一 `task_id + user_query`；bridge 会用 task-scoped session 继承 task_id，禁止生成第二个 session id。

> `build_dashboard.py` 也属于上述“后续每个命令”：只要 spec 含 `upload:true` 或 `update_page_id`，必须写入同一 `task_id`。成功结果会返回 hash-bound `reply_draft_file + reply_validation_command`；公网验收后必须写草稿并运行该命令，只有 `valid:true` 才能最终回复，之后停止工具调用。

**具体资产证据闸门**：除 `new_asset_page` 固定场景外，只要用户点名具体资产，就在 Trace 后、解释资产身份或提交 `routing_decision` 前，按「Trace → 资产映射 → 最小接口验证 → 页面路由」的顺序完成验证：调用 `scripts/qbs_bridge.py resolve_asset_data` 得到平台 ticker 映射，并按页面实际需要探测所需数据角色是否可取数，只记录接口成功/失败、可用字段和结构化错误。页面结构与 direct/fork/unmatched 判断只依据"用户所需能力 × 已验证的平台能力"，不得依据 Agent 对公司上市状态、所有权、资产名称或市场惯例的记忆。验证前不得引入"上市/未上市、公开/私营、代理资产、无行情、只能静态"等限制性前提；若用户没有询问这些身份属性，也不要把它们扩展成分析主线。

`resolve_asset_data` 的输入合同必须直接按下面形状写入新的 `output/*.json`，不要先猜 schema、不要把多只资产拼成一个 `asset` 字符串，也不要为每只资产各写一份参数文件：

```json
{
  "task_id": "<同一 task_id>",
  "user_query": "<当前用户原问题>",
  "assets": ["贵州茅台", "五粮液", "泸州老窖"],
  "required_roles": {
    "snapshot": ["close", "pct_chg", "pe_ttm", "pb", "market_cap"]
  },
  "optional_fields": ["turnover_rate"]
}
```

- 单资产用 `"asset":"贵州茅台"`；多资产用 `"assets":[...]`，二者不能并存。多资产由 bridge 在**一次 CLI 调用**内逐资产验证并聚合收据。
- `required_roles` 只需写实际需要的 role；省略的 `profile/snapshot/report/formula` 自动视为空数组。每个 role 的规范值是字符串数组，也兼容 `{"fields":[...]}`。
- 多资产探测阶段的 `formula` 必须留空或省略；跨资产公共公式只在探测后用 `validate_package_set` 验证一次，禁止每个资产重复验证/注册同一公式包。
- **`output/` 是跨会话残留的 scratch，不是示例库**：禁止 Grep/Read 旧 `output/*.json` 来拼本次参数，尤其禁止复制其中旧 `task_id`、旧凭证、旧公式或损坏 JSON；参数形状只从当前 `SKILL.md` / `tools/*.md` / `workflows/*.md` 获取。
- 含双引号的公式必须写成合法 JSON 转义；优先使用无嵌套引号的等价公式（如 `mt_close = 收盘价(贵州茅台)`）。写入后直接执行对应 CLI，让 JSON parser 作为反馈，不要读取旧 scratch 文件“找范例”。
- 多资产累计收益/回撤优先走标准看板：同一组价格 `outputs` 分别配置 `transform:"cumulative_return_pct"` 与 `transform:"drawdown_pct"`，估值另用 Data Grant table。此能力已由 `build_dashboard` 内置，禁止为它 Grep/Read `assets/data-kernel.js` 或手写 bespoke SSE/Grant runtime；详见 `workflows/dashboard-end-to-end.md` 的最短路径。


### 已有 URL 修改按写权限原位更新或 Fork

只有用户明确要求“解读/查看当前页面”且不要求修改时，才使用**不带 `task_id`** 的纯只读 `interpret`，读取后即可按返回证据回答，不进入建页流程。

用户要求修改已有 QuantBuddy URL 时，先 `trace_context.py begin`，再带同一 `task_id` 调用 `static_page.py interpret`。必须按返回的 `existing_page_route.mode` 分流，不能把所有已有页一律判成 Fork：

- `mode="in_place"`：调用者是 owner/page admin，或旧版详情合同返回 `resource_role="existing_page"`、由 `updateStaticPage` 在写入时做最终权限校验。保持原 `page_id`、公开 URL、包/Grant、Share Shell 与运行时身份，使用 `static_page.py update`（以及需要时的 `update_progress` / `publish_verified`）写回原页。禁止 `new_page`、`new_asset_page`、`upload` 创建替代链接，也不需要再次查询 `templates`。若 `chart_edit.py` 返回 `LEGACY_PAGE / NO_RENDER_JS_MARKER`，而用户已明确要求修改本人页面并保持原链接，则必要的技术性结构升级已获授权：立即按 `workflows/edit-existing-chart.md` 的 legacy fallback 下载、最小重建、浏览器预检并 `update` 同一页，不得二次询问是否升级，也不得停在本地 HTML。只有缺失信息会改变业务语义时才询问。若服务端返回 `FORBIDDEN`，停止写入并转入下述 Fork 路径，不得伪造 `is_page_admin`。
- `mode="fork"`：当前详情明确 `can_update_in_place=false`，或该页是不可直接写入的 `source_template`。依次执行 `templates(recommend="all") → new_page(mode=fork, source_template_id=<interpret 返回>) → fork_prepare`；templates 只补齐范式池凭据，不能覆盖 interpret 已绑定的来源。

可信权限字段由服务端 `getPageDetail` 返回：`can_update_in_place` 与 `access_role=owner|page_admin|reader`。客户端不得相信调用参数里自报的 `is_page_admin`；旧服务端尚未返回 capability 时，只允许尝试写回 interpret 绑定的同一个 `page_id`，并以 `updateStaticPage` 的 owner/page-admin 鉴权结果为准。

Fork 路径在决策绑定前禁止 `new_asset_page`、`build_dashboard`、bespoke `upload` 或任何 regenerated page；不得改判 unmatched 或偷换来源。只有 `fork_prepare` 明确返回结构化不可复制错误后，才允许评估降级，并显式声明 `page_context_mode=regenerated` 与 `source_page_context_inherited=false`。

### 从 QBS 并行交接进入（薄适配，不改变 QBV 独立 SOP）

当父任务提供 `qbs_qbv_handoff_v1` 文件时，不再执行 `begin`，而是：

```powershell
python scripts/trace_context.py beginHandoff '{"handoff_file":"D:/.../handoff.json"}'
python scripts/qbs_handoff_adapter.py evaluate '{"handoff_file":"D:/.../handoff.json","qbv_job_id":"qbvjob_xxx","qbv_job_file":"D:/.../job.json"}'
```

`trace_context.py` 原样复用 QBS 的 `task_id + turn_id`；Adapter 校验可选 `qbs_computation_capsule_v1`，并在发现对应 `qbs_qbv_job_v2` 时确定性把 Job 从 `queued` 写为 `running`。QBV standalone 没有该 Job 时为无副作用 no-op：

- `coverage=covered`：禁止再次调用 `resolve_asset_data` 或其它 QBS 工具重算 `covered_roles`；直接消费胶囊里的资产映射、合同、artifact、字段映射、结论和收据，然后继续 QBV 页面 SOP。
- `coverage=partial`：只允许通过 `qbs_bridge.py` 补 `missing_roles`，不得重复已覆盖 role。
- `coverage=unusable`：无损回退本节原有 Trace → `qbs_bridge` → 路由流程，不得降低验证门禁。
- Adapter 返回 `formula_runtime_action=register_exact` 时：把 `formula_runtime_contract.formulas` 按原顺序、原字面注册为 Formula Package，并按合同中的 `reads` 首次查询；禁止缩写指标名、合并公式、重新推导或再次调用 QBS 验证 covered 公式。fingerprint、左值或 reads 校验失败时按 `coverage=unusable` 安全回退，不得注册被篡改合同。旧 Handoff 没有 `formula_runtime_contract` 时保持原 standalone/兼容流程。

这里跳过的只是**本轮重复计算**。direct/fork/unmatched、本人原位更新/他人复制、Grant/Package 注册、运行时首次查询、页面构建、Card Runtime、发布和公网验收仍由 QBV 完整执行。QBS Job 只做旁路审计：`publish_verified` 同时取得 `published=true + verified=true + page_id + public_url`，或 `direct_deliver` 取得字段一致的强终态 `direct_finalize` contract 后，会自动写回 `completed`；无法继续且确定终止时执行 `python scripts/qbs_handoff_adapter.py fail-job '{"qbv_job_id":"qbvjob_xxx","qbv_job_file":"D:/.../job.json","failure_code":"<CODE>","retryable":true}'`，不得手改 Job JSON。用户直接使用 QBV 时没有 Handoff，继续走原 SOP，不依赖 QBS 胶囊。`source_skill_id=null + source_skill_id_status=unavailable` 是合法审计状态，不得阻断页面流程，也不得猜测历史 `skill_*`。

### 单一 A 股简单分析快速通道

用户只要求分析一只 A 股并给出可分享页面，且**没有**定制栏目/版式、指定额外指标/公式/图表、对比、多标的、指数或港美股要求时，直接执行：

```bash
python scripts/static_page.py new_asset_page '{"task_id":"task_xxx","asset":"贵州茅台","user_query":"分析贵州茅台"}'
```

该命令调用服务端固定场景，并在内部读取 SHA256 绑定 evidence、生成前五个数据章节、上报终态和清理临时文件。数据章节按有数据才生成表格、整篇最多五表；计算维度以 stock profile 的稳定画像维度为主证据、有效收盘价 CSV 的日涨跌/均线/价格位置为补充，两路均无可核验字段时才整节省略，且后续可见章节自动连续编号。消息面章节暂不输出。成功结果包含 `agent_reply_markdown_draft + agent_summary_request`：草稿第一至第五章就是交给当前 Agent的完整可见证据，第六章只有唯一 `summary_marker`。Agent必须结合本轮真实用户问题，用自己的语言直接回答用户目的，只引用草稿已有数据，提炼结论和关键依据；走势类问题使用条件式判断，财报点评聚焦报告表现，其他问题同样按原意组织，不需要关键词分类器或专用生成器。完成后只替换 marker，不改前五章、免责声明和最终链接块，不运行 validator 或其它工具，立即发送完整 Markdown。公开链接和“若效果不满意，页面可进一步升级”仍是最后两行。CSV 单项失败只删除对应字段并写 warning；完全没有可核验证据或草稿生成失败时 fail closed，不得退化成一句链接或重复调用。后续若用户要改这张自有页面，继续使用 `update` 保持同一个 `page_id` / URL。

不满足上述窄条件时，**只运行一次 `scripts/static_page.py templates`**。它调用统一 public 列表，由服务端完成官方精选+社区的去重、排序和分页；不要再手工重复调用。返回值是 `item_count` + 覆盖全部候选的 `items_summary`（不再是原始 items 全量打印），完整候选落盘在 `full_result_file`；正常路由判断只需要读 `items_summary`，不需要也不应该去读 `full_result_file`。

- **① 直接命中**（范式匹配、范围一致，且候选真实 runtime 输出覆盖用户请求的每个维度）：
  - `templates` 一旦给出精确命中，普通渠道的**下一条用户可见消息必须立即发送现成 `download_url/public_url`，中间不允许任何工具调用**。推荐文案：`已直接命中现成活页：[标题](URL)。我继续核对实时数据并补充分析。`；若 `agent_reply_hint.delivery_policy.emit_intermediate_url=false`（即 `feishu-group`），禁止发送该 URL，直接继续。
  - 普通渠道发出链接后、`feishu-group` 不发链接而是立即运行一次：`python scripts/static_page.py direct_deliver '{"task_id":"task_xxx","page_id":"page_xxx","template_revision":"sha256","dimension_check":{"coverage":[{"dimension":"用户维度","covered_by":["card_required_outputs:真实输出"]}]}}'`。标题和简介只能作辅助证据；每个维度至少需要 `card_required_outputs`，或由 runtime 合同派生的 `page_context.primary_outputs` 权威证据。
  - 不 `new_page`、不注册、不 fork、不研究脚本源码、不先跑 `--help`。`direct_deliver` 的公式结果固定为 summary；grant 完整结果只写 `%TEMP%`，最终回复不得暴露本地路径或凭证。
  - 只有返回 `agent_reply_contract.terminal=true` 且 `operation=direct_finalize` 才允许最终收口；失败时说明具体错误，不得用已发送的链接绕过终态门禁。回复模板和 `page_context` 沿用原页。
  - `direct_deliver` 会返回真实 contract、草稿、校验参数的 `%TEMP%\qbv_<完整 task_id>_*` 文件路径及 `reply_validation_command`。只把 Markdown 写入返回的 `reply_draft_file`，执行返回的命令一次；`valid=true` 后立即最终回复，禁止再次校验、运行 `--help`、扫描临时目录或继续搜索 memory。成功校验会统一清理 contract、draft、params 和 grant 临时结果。
  - 公网浏览器验收成功后的下一步必须是最终回复；不得再调用 Read/Grep/Bash/浏览器或进入新的研究轮次。若浏览器验收是最后一个可用工具轮次，也必须用已验证 contract/URL 直接收口。
  - 用户之后说"要改这个页面内容" → 转 ② fork（官方/社区链接不能直接改，只能新建自己的链接后改）。
  - 边界：范式匹配但**标的/股票池/指数/市场范围不一致**（如命中的是茅台估值页、用户问的是宁德时代；命中沪深300异动页、用户问中证500）不算直接命中，落到 ②。只有资产无关且市场范围一致的全市场范式，才可不依赖具体标的直接命中。
- **② fork**（范式命中但标的不符，或用户要改内容）：
  - 先运行 `new_page`，传 `routing_decision:{"mode":"fork","source_template_id":"page_xxx","reason_code":"same_paradigm_different_asset","borrow_mode":"inherit"}`。`inherit_augment` 用于模板结构可沿用但缺分析维度；`compose` 用于合同无法逐项继承、但布局/样式/渲染函数/公式思路或 Grant 形状仍可借鉴。
  - `fork_prepare` 是一次性 task 绑定：重复执行返回 `FORK_ALREADY_BOUND`；确需整体重建必须传 `force_rebuild:true + rebuild_reason`，同 task 禁止换来源模板。
  - `fork_prepare 返回 publish_command 后` 即进入发布收敛阶段：只填写返回的 review 文件并执行该命令，禁止读取 `scripts/*.py`、运行 `--help` 或探索 `publish_workflow.py` / `fork_runtime_contract.py` 实现；命令失败只按结构化错误修正输入。已创建首链时必须完成 terminal 或明确失败收口，不得让进度页长期停留在 running。
  - `inherit_augment` 向 `fork_prepare` 传 `augmentation_spec`，新增 package/grant 角色与来源角色物理隔离。新增公式必须通过 QBS 验证，marker 必须恰好出现一次且输出必须被实际渲染。
  - `compose` 先运行 `intent_profile` 做 user_term/platform_dimensions/method_terms 三层映射，再用 `research_templates` 提取 credential-free 的栏目 HTML、CSS、渲染函数及合同形状，最后 `fork_compose` 提交借鉴清单。收据及 SHA256 绑定后才允许发布；全部 original 的零借鉴 Compose 被拒绝。 `fork_compose` 必须传 `borrow_plan.modules`（不是顶层 `borrowed_refs`），并逐项认领 intent profile 的每个 `user_term`；优先复制 `research_templates.templates_summary[].fork_compose_example` 后修改，遇到 `COMPOSE_BORROW_PLAN_REQUIRED` 必须按返回示例重试，不得停在 running 进度页。
  - Compose 参数必须一次写完整：`intent_profile` 至少传 `{"task_id":"task_xxx","asset_scope":{"kind":"sector","name":"目标资产组","market":"A股"},"dimensions":[{"user_term":"实时行情","platform_dimensions":["close","pct_chg"],"method_terms":["横向比较"]}]}`；`research_templates` 传 `{"task_id":"task_xxx","template_ids":["page_source"]}`。任一结构化错误若返回 `example_intent_profile`、`example_research_templates` 或 `fork_compose_example`，必须直接复制该完整示例后修改并重试，不能逐字段猜测。  - **资产替换的职责分工**：Agent 说清楚"换成哪只标的"，脚本负责"这只标的在页面里写成什么样"。来源主资产由脚本从模板公式词频 + 标题推导，代码的实际写法（`SH600900` / `600900.SH` / 裸 `600900`）由脚本扫描来源 HTML 得出，只替换真实存在的写法——不要去猜来源 HTML 里代码写成什么样，你看不到那个文件。多资产/指数类范式推不出主资产时返回 `FORK_SOURCE_ASSET_AMBIGUOUS`（报错自带候选名与可照抄的调用），用 `source_asset` 显式指明后重试。`asset_replacements` 仅作可选覆盖。替换后主资产若仍有残留，在写出工作 HTML 前就返回 `FORK_SOURCE_ASSET_RESIDUAL`，不会等到发布后才发现。
  - Agent只在 `fork_prepare` 生成的 `review_update_params_file.decisions` 中填写 `required_decisions` 声明的业务决策：规则性同业矩阵填 `target_slots`，复杂跨资产公式填 `target_formulas`，标签替换填 `page_label_replacements`。`decisions` 已按角色预生成嵌套占位骨架（`{"roles":{"<role_id>":{...}}}`），只需要在骨架里补全空值，不要新增/改写顶层字段，也不要把 `required_decisions` 里的扁平 `decision_id`（如 `roles.package.package_001.target_formulas`）当成提交用的 key。禁止直接编辑标准 fork HTML/review。
  - Grant按来源角色完整继承 `kind/query_type/fields/dimensions/window_days/result_mode` 与 CSV/inline 合同，只允许自动修改 manifest 声明的资产范围字段；其他变化必须填写 `contract_change_reason`。
  - 继承 Grant 的数据级失败可降级并继续发布存活角色；鉴权/配额/协议等系统级失败仍阻断。若页面仍用 `queryDataGrant` 无条件消费失败 Grant，返回 `GRANT_DEGRADATION_UNSAFE`，不得用空凭证假降级。
  - 先运行 `fork_prepare` 返回的 `review_update_command`；只有 `review_state.status=complete` 且生成 review receipt 后，才运行 `publish_command`。发布器从同一 canonical package/Grant 合同派生 QBS 验证与注册，自动检查 required outputs、公式左值、reads、PE/PB 水位公式具有明确算法与正整数窗口、Grant fingerprint、Marker 唯一性与 Card Runtime 结构，并让一次注册结果扇出到页面/Card全部位置。
  - `fork_manifest_v2` 禁止手工传 packages、grants、Marker 或完整 workflow JSON，出现 `MANUAL_RUNTIME_BINDINGS_FORBIDDEN` 时回到生成的 publish plan，不要写临时替换脚本。v1 prepared task 继续按旧接口发布。

  - 这不是建议——`publish_verified` 服务端会按 fork manifest 里的凭证数量强制核验：手工分步调用 `publish_verified(task_id, page_id, html_file, source_template_id, fork_manifest_file, validation_receipt_files)` 只有在这个页面**零凭证**（纯静态改造）时才会放行，否则直接拒绝并返回 `error:"PUBLISH_WORKFLOW_REQUIRED"`；出现该错误时改走 `publish_workflow.py`，不要绕过。
  - 回复 = 回复模板格式 + **自己的新链接**（数值同样用自己的包/grant query 填）。
- **③ 未命中**（无匹配范式）：Agent 根据 `items_summary` 调 `new_page` 时传 `routing_decision:{"mode":"unmatched","closest_template_id":"page_xxx","reason_code":"required_capability_missing","reason":"候选缺少用户要求的核心能力"}`；存在候选却只因标的/范围不同而判 unmatched 会被提示改走 fork。记录成功后继续 `build_dashboard` / bespoke 自建 → 其余同 ②；`feishu-group` 同样不发送进度链接。

> 后续追问：自己的链接 → `update` 同 `page_id`；命中的官方/社区链接要改 → 只能转 ② fork 成自己的链接后再改。

## 默认路由

- **简单单一 A 股综合分析**（无定制、额外指标/图表、对比或多标的要求）：`trace_context begin` 后直接 `new_asset_page` 返回自有实时页面。
- **其他固定页面形态**（定制个股页、成分股异动榜、多因子选股看板、商品日报等）：先 `templates` 查询官方精选+社区命中池；direct 直接用列表 URL + revision，fork 才读取和改写模板详情。
- **宽宝活卡 / 精华卡 / 封面卡（范式卡 artifact）**：把页面精华做成独立 **card runtime artifact**（`embedded-card-v1`：页面内嵌 `<template data-qb-card-template>` + `data-qb-card-manifest` + `QBCardRuntimeV1` runtime），供官网卡片流在空白宿主中**独立 hydrate**。静态首帧 `card_snapshot_url` 由 `skill_server` 按 artifact hash 生成，是页面封面的唯一来源（整页缩略图能力已下线）。按 [guides/essence-cover-card.md](guides/essence-cover-card.md) 生成；已发布页优先用 `preserve_visual:true` 只升级协议。完整重建必须显式传 `visual_contract`，否则 `CARD_VISUAL_REQUIRED` 停止；用 `verify_page.mjs --card-runtime-only --require-card-visual-contract` 验收新 artifact。卡片必须官网浅色系、固定信息骨架、可变核心可视化；不再用旧的 `?cover=1` URL 模式。
- **没有合适在线模板**：再走 `workflows/dashboard-end-to-end.md`，用 `build_dashboard` 生成声明式实时看板。
- **声明式看板也不够**：才走 `guides/bespoke-page.md` 写 bespoke 主体 HTML，并用公共 shell 编译成自包含页面。
- **改一个已有图表**（叠加/去掉一条线、改时间窗口、查真实数据）：优先 `workflows/edit-existing-chart.md` +
  `scripts/chart_edit.py`，只动被要求的那一处、不重新验证/计算页面上其它无关系列；只有目标页面是 legacy
  （`chart_edit.py inspect` 判定，多为本次改动之前生成的老页面）或改动本质上要求整页重算/换版式，才落回
  下面的整页重建。
- **改造已发布/已生成页面**：优先 `scripts/retrofit_share_shell.py`，再 `static_page.py update` 保持同一个 `page_id` / URL；正式 update 应传具体 `change_note`，版式变化显式传 `change_aspect:"layout"`，其它类型可让服务端推断。
- **Share Shell revision 4 页面问答边界**：可见页头由官网 `/embed/live-page-header` iframe 托管，活页 Parent Bridge 只执行刷新、收藏、分享、认证导航和移动 WebAgent 动作、页面问题携题自动发送并校验 `qb-live-page-header-v1` / `qb-web-agent-v1`；官网 WebAgent Preview 注入 `qb-live-page-embed-context=webagent-preview` 时不得加载页头或预加载收藏 iframe。官网只改页头视觉不要求逐页刷新；Parent Bridge、通信协议或能力契约变化才提升 revision。
- **用户可见链接策略**：普通渠道 direct 在 `templates` 命中后、下一次工具调用前发现成 URL，fork/unmatched 在 `new_page` 返回后立即发首链；`feishu-group` 看到 `delivery_policy.emit_intermediate_url=false` 后禁止发送任何非终态 URL，只在 validator 通过后发送 terminal contract 的 playground `public_url`。进度页仍用 `update_progress` 和 `publish_final` 更新同一 `page_id`；未显式传 `change_note` 时，版本修改描述按“状态 + 中文阶段标题 + 用户可见 message”自动生成，正式发布版本默认记录“完成发布：正式活页内容已发布”。
- **Agent 回复模板**：活页 metadata 可带 `agent_reply_template` 指向本技能 `reply-templates/` 下的回复骨架。`reply-templates/` 是 Agent 最终回复格式，不是活页 HTML 页面模板；不要和在线 `templates` / `template` API 混用。
- 本 skill 不再内置本地页面样板，不能从本地历史样板目录或低质 HTML 骨架起步。

## Agent 回复模板（`agent_reply_template`）

活页用同级 `page_context` 描述用途/模块/输出，用 `agent_reply_template.template_ref` 指向 [reply-templates/](reply-templates/) 的 Markdown 骨架。字段契约、hybrid 规则和发布继承见 [tools/static_page.md](tools/static_page.md)。

- `page_context` 不得包含实时数值、api_key、signature、Bearer token 或本地路径；fork 后必须按最终页面重建，direct 才沿用原页。
- 读取型命令返回 `agent_reply_hint.terminal=false`；`new_page/update_progress` 也不是终态。成功的 `new_asset_page/direct_deliver/direct_finalize/upload/update/publish_final/publish_verified` 可返回 `agent_reply_contract.terminal=true`；其中 `new_asset_page` 返回含唯一综合观察 marker 的 `agent_reply_markdown_draft` 和面向当前 Agent的 `agent_summary_request`。
- fork/unmatched 遇到必须由用户决定的口径时，用同一 `task_id/page_id` 进入 `waiting_input`，用户回答后继续原任务；不要重新建 Trace 或首链。`feishu-group` 的 waiting hint 不含 `public_url`，提问时也不得附带进度链接。
- fork 必须使用 `fork_prepare` 绑定来源和 manifest，最终 `publish_final` 保持首链 URL、移除来源凭证并保留必需栏目/输出/Card Runtime；详细门禁见 [workflows/new-session-paradigm-routing.md](workflows/new-session-paradigm-routing.md)。
- prepared fork task 禁止 `build_dashboard`；v2只填写生成的 review-update 决策文件，依次运行 `review_update_command` 和 `publish_command`。只有旧 v1任务继续使用手工 `fork_validate` 路径。
- 带 `task_id` 的进度从 `package_register` 起必须传同任务的结构化验证证据：实时页提交 `route_receipt`、`grant_receipts`、`formula_receipts`，且 `selected_routes` 必须逐项对应实际注册凭证；自由文本 `validation_not_required_reason` 不再放行。纯静态内容只能用 `static_content_only`；资产实时探测全部数据级失败时只能凭 `live_data_route_receipt_v1` 使用 `static_after_live_probe`。
- `new_asset_page` 的最终回复只允许把 `agent_reply_markdown_draft` 的唯一 `summary_marker` 替换为 Agent撰写的综合观察；不得改写、删减或重排其它内容，也不得把 marker 发给用户。综合观察首句直接回答本轮用户目的，后续只选最相关证据解释，避免复述全部五章；没有足够证据时明确说明边界，不得补造事实。该分支不返回 evidence 路径或校验命令。其他终态回复必须按回复模板输出并且只能使用 contract 的 `public_url`；`feishu-group` 下该字段必须是 `https://www.quantbuddy.cn/playground/<owner>/<page_id>`。**只要终态回复包含 `public_url`，必须把 `可分享实时活页：[{public_url}]({public_url})` 作为最后倒数第二行，最后一行固定为“若效果不满意，页面可进一步升级”；链接不得在正文、章节或免责声明中提前出现。**一般模板依据 `reply_render_policy` 与 `reply_data_availability` 删除结构性不存在的字段、整列、整行和空可选章节。`single_stock_deep_dive_v1` 还必须读取 SHA256 绑定的 `reply_data_evidence_file`，保留全部七节标题，有数据的模板字段全部输出，整节无数据使用标准说明；只有有效结构中的偶发缺值才写 `--`。若 `delivery_policy.max_markdown_tables` 存在，整篇不得超过该表格数，超出的结构改用列表或行内文本且不得丢数据。validator 返回 `valid=true` 后原样发送 `validated_markdown`，不得再次压缩或改写，也不得暴露原始托管 URL、本地路径、凭证或内部日志。
- 除 `new_asset_page` 外，最终回复前只运行一次发布器返回的 `reply_validation_command`。`reply_validation_env` 是进程内执行专用值，CLI 与持久化报告只允许返回 `[REDACTED]` 和 `reply_validation_env_keys`，禁止输出真实凭证。若发布时显式设置了 `QBV_API_KEY`，validator 命令必须继承同一个现有环境变量；未显式覆盖时由 `config.json/config.local.json` 解析默认账号，发布器返回中不携带默认配置 key。禁止把 key 拼进命令串或另写参数文件。validator 必须读取发布器生成的 `contract_file + contract_sha256`，不得手工重建精简 contract。direct 使用 `direct_deliver` 返回的完整 task ID 路径和命令，成功后自动清理。`valid=true` 后不再执行任何工具调用。
- 没有 terminal contract 禁止完成任务。唯一例外是成功的 `waiting_input` checkpoint。
- 性能门槛：普通渠道模板命中到首链不超过 5 秒；所有渠道 terminal 到最终回复不超过 45 秒，完整活页任务以 10 分钟内完成为常态目标，用户可见消息间隔不超过 60 秒。回复证据补读不设额外人工截止时间，但必须按模板字段过滤、相同模式批量读取且每批最多10个；禁止公式重算和 package/grant 重查。
- 逐指标声明最新可得日期和实际覆盖范围。未做浏览器验收时，只能声明公开 URL 和实时接口可访问。

## 前置依赖：公式必须先验证

本技能运行时自包含：注册/生成/发布只凭本技能 `config.json` 的 `api_key`。但注册公式包前，每组公式必须先在 quant-buddy-skill 里用 `runMultiFormulaBatchStream` 跑通确认出数；服务端试读只是兜底，不替代这一步。

如果当前环境没有 quant-buddy-skill，Agent 不要跳过验证或直接注册公式包。

普通已安装 skill 用户先检查全局 skills；缺失时运行安装命令，已安装但需要刷新时运行更新命令，二选一，不要连续执行：
```bash
npx skills list -g --json
# 未安装时
npx skills add pseudo-longinus/quant-buddy-skills -g --all
# 已安装、需要刷新时
npx skills update pseudo-longinus/quant-buddy-skills -y
```
- Windows 上若 symlink / `EPERM` 报错，在 `add` 命令末尾追加 `--copy` 重试。
- 在源码 checkout 或 junction 调试本 skill 时，不要运行上面的 bundle 级 `add --all` / `update` 覆盖当前 `quant-buddy-view`。QBV 解析活动 QBS 的固定优先级是 `QBS_SKILL_ROOT`、同级 `quant-buddy-skill/`、同级 `quant-buddy-skill__skillhub/`；以 `scripts/call.py` 存在为准。不得用 glob/递归扫描，也不得把 `quant-buddy-skill-backup-*` 当成可运行 skill。
- 安装后必须确认 quant-buddy-skill 的 `config.json.api_key` 或 `QUANT_BUDDY_API_KEY` 可用；只报告“已配置/未配置/鉴权成功或失败”，不要打印 key 或完整 config。若鉴权失败，停下来说明 blocker，不要继续注册公式包。
- 若只是上传/改造一份真正不含资产、市场数据和来源凭证的纯静态 HTML，可继续使用本技能并声明 `static_content_only`。资产实时页面必须通过 `qbs_bridge.py resolve_asset_data` 完成统一探测；只有 `required_roles.formula` 非空时才运行公式验证，普通行情、估值和财务不得为了触发公式包而改写成公式。

推荐让两个 skill 同级安装，便于验证公式和迁移旧公式包凭证：
```text
<skills 目录>/
  quant-buddy-skill/ 或 quant-buddy-skill__skillhub/  ← 探索 / 公式验证（runMultiFormulaBatchStream、confirmDataMulti）
  quant-buddy-view/ 或 quant-buddy-view__skillhub/    ← 本技能：注册 Formula Package / Data Grant、生成看板、发布
```

旧凭证迁移见 [tools/formula_package.md](tools/formula_package.md)。

## 入口选择（先判断类型）

固定页面先查在线范式卡；direct 用 `direct_deliver`，fork 才下载和改写来源 HTML。不要从本地历史样板或低质骨架起步。

| 类型 | 展示名 | 入口 | 什么时候用 |
|---|---|---|---|
| 单股快页 | A 股个股综合分析 | `scripts/static_page.py new_asset_page` | 简单分析一只 A 股并返回页面；无定制/对比/额外指标要求 |
| 页面模板 | 官方精选 + 社区 | `scripts/static_page.py templates` | 其他固定页面形态；direct 直接交付，范围不一致才 fork |
| 回复模板 | Agent 回复骨架 | [reply-templates/](reply-templates/) | 活页 metadata 的 `agent_reply_template.template_ref`；用于约束 Agent 最终 Markdown 回复格式，不生成 HTML |
| 封面组件 | 宽宝活卡 / 精华卡 | [guides/essence-cover-card.md](guides/essence-cover-card.md) | 独立 4:3 `embedded-card-v1` artifact；按指南实现和验收 |
| 通用流程 | 标准实时看板 | [workflows/dashboard-end-to-end.md](workflows/dashboard-end-to-end.md) | 用户要“做成可分享看板/链接”，但没有指定固定页面模板 |
| 增量维护 | 单图表增删改查 | [workflows/edit-existing-chart.md](workflows/edit-existing-chart.md) | 自己的已发布页面要加/删一条线、改时间窗口、查真实数据——只改一个图表，不是整页重建 |
| 开发指南 | 自定义页面 | [guides/bespoke-page.md](guides/bespoke-page.md) | `build_dashboard` 做不出的自定义 HTML/CSS/SVG 页面，或迁移已有 HTML |
| 迁移工具 | 旧页套公共外壳 | [tools/retrofit_share_shell.md](tools/retrofit_share_shell.md) | 已发布/已生成 HTML 需要去掉旧二维码、旧页头、旧页尾，并保留同一个 `page_id` 更新 |
| 设计系统 | 活页 UI/UX 系统 | [guides/live-page-ui-ux-system.md](guides/live-page-ui-ux-system.md) | 新建或整体重构活页时，选择页面原型、主题 token、字体/密度、可组合模式和响应式转换；统一体验底线但保留页面身份 |
| 维护指南 | 浏览器批注与整页 UI refinement | [guides/browser-feedback-refinement.md](guides/browser-feedback-refinement.md) | 用户针对已有自有页面的字体层级、间距、章节导航、sticky/折叠、响应式或分享交互提出修改；保持同一 `page_id`、runtime 合同和页面视觉身份 |

- 简单单一 A 股综合分析优先走 `new_asset_page`；定制单标的画像/估值财务、指数成分异动、多因子工作台仍先匹配对应在线范式，范围不一致才 fork。详细页面契约由服务端固定场景或模板/构建脚本门禁，不在此重复。
- fork 后禁止沿用来源 `package_id/grant_id/signature`；必须验证并注册当前用户凭证。
- 所有页面复用 `assets/share-shell/`；分享壳、海报、Card Runtime 和迁移细则分别读取对应 `guides/`，不要手写重复组件。
- 公式注册与读取模式见 [tools/formula_package.md](tools/formula_package.md)，数据授权见 [tools/data_grant.md](tools/data_grant.md)，静态页命令和 metadata 见 [tools/static_page.md](tools/static_page.md)。
- 普通自有页面用 `update` 保持 URL；published template 用 `template` 判定，除非用户明确维护原模板，否则只读复用或 fork。

## 取数：实时页的两条通道

实时页可使用 Formula Package 或 Data Grant：公式包在 HTML 内嵌 `package_id + signature`，打开时调用 `queryFormulaPackage`；数据授权内嵌 `grant_id + signature`，打开时调用 `queryDataGrant`。两类凭证可以同页混用，彼此独立取数；底层数据更新后无需重建页面，访问者打开或刷新即可取得最新数据。spec 不需要写 `mode` 字段。

- **页面是"活"的**：数据不焊进 HTML，运行时实时取；构建期只取一次数做质量体检（数据健康 + 单标的文案一致性），不内联。
- **关联字段平级**：页面 metadata 中 `package_ids` 与 `grant_ids` 分别记录两类凭证；任一通道都可以单独支撑实时页，也可以同时存在。
- **通道按数据性质选择**：普通行情、估值和财务优先 Data Grant；确需计算、自定义公式口径时才使用 Formula Package，不得为了让页面成为实时页而强行改写成公式。
- **共同前提（均已满足）**：`queryFormulaPackage` / `queryDataGrant` 对页面域名 `pages.quantbuddy.cn` 放开 CORS，且两类 `signature` 都是允许嵌入页面的公开取数能力令牌。
- ⚠️ **协议必须一致**：页面发布在 `https://`，`config.json` 的 `endpoint` 也必须是 `https://`，否则浏览器会以 mixed-content 拦截取数。当前 endpoint 已是 `https://www.quantbuddy.cn/skill`。

## 数据授权（Data Grant）vs 公式包 —— 页面免 key 取数的第二条通道

> 脚本 [scripts/data_grant.py](scripts/data_grant.py) 已可用，`build_dashboard` 与 `assets/data-kernel.js` 已支持 grant 面板，与公式包同页混用。契约见 [tools/data_grant.md](tools/data_grant.md)、服务端设计见 `skill_server/docs/dataGrant相关文档/数据授权-技术设计文档.md`。选凭证类型时按下面取舍表对照。

数据授权与公式包**共用同一套签名免 key 心智**：页面 HTML 内嵌一个凭证（公式包是 `package_id + signature`，数据授权是 `grant_id + signature`），访问者打开页面时免 key 实时取数。区别在钉死的是什么——公式包钉死"一组公式 + 读取模式"（会重算，走 SSE）；数据授权钉死"一次平台直取数请求"（无重算，普通 JSON）。

**取舍规则（选凭证类型时对照）**：

| 页面要展示的数据 | 用哪条通道 | 凭证 |
|---|---|---|
| 算出来的指标 / 回测净值 / IC / rankIC / 时序 / 自定义公式口径 | **公式包** | `package_id` |
| 平台白名单直取的行情 / 估值 / 财务 / 资金流（收盘价、涨跌幅、PE/PB…） | 数据授权 `fast_query` | `grant_id` |
| 个股预计算画像卡（估值/财务质量等维度画像） | 数据授权 `stock_profile` | `grant_id` |
| 已上线维度分的 TopN / 榜单 / 异动名单（动量反转、趋势结构…） | 数据授权 `composition_select` | `grant_id` |

- 一句话：**要"算"的用公式包；平台"直取/直选"的有界数据用数据授权**。原公式包 RANK 角色仍保留给"算指标"型多因子选股，不被 composition_select grant 取代。
- **资产实时页面统一入口**：先调用 `scripts/qbs_bridge.py resolve_asset_data @params.json`，显式声明 `required_roles.profile/snapshot/report/formula` 与 `optional_fields`。路由按 `stockProfile → fast_query(snapshot) → fast_query(report) → 必需公式` 执行；普通行情、估值和报告期财务优先 `fast_query`，不得为了触发公式包而改写普通字段。
- **禁止先验假设**：`resolve_asset_data` 探测前不得依据 Agent 对公司上市状态、所有权、资产名称或市场惯例的记忆推断平台是否可取数，也不得据此提前选择代理资产、静态说明、数据通道或页面结构；未验证前只能说"我先验证平台资产映射和数据可用性"。
- **按业务覆盖判成功**：顶层 `success:true` 不代表页面数据完整。目标资产必须存在，required fields 必须有有效值，且不得出现在 `asset_errors` / `field_errors`；optional field 失败只记 warning。部分核心角色成功返回 `incomplete`，不得冒充完整页面。
- **静态回退是硬门禁**：`ASSET_NOT_FOUND`、`DATA_UNAVAILABLE`、空结果、目标资产/required field 缺失等数据级失败可以继续下一独立通道；鉴权、配额、task/session、网络、超时、协议和服务错误必须返回 `blocked`。只有 `live_data_route_receipt_v1` 证明所有核心实时角色都已探测且均为数据级失败，才允许 `static_after_live_probe`；`static_content_only` 仅限没有资产、市场数据和来源凭证的纯静态内容。
- **发布只认证据**：Grant-only、formula-only 与混合页面均可发布，但必须提交 route/grant/formula 结构化收据，并让 route receipt 的 `selected_routes` 与实际 Grant/公式收据逐项对应；禁止自由文本 waiver。
- **两套并存**：探索/验证仍在 quant-buddy-skill 用 api-key 跑三接口（fastQuery / stockProfile / selectByComposition）；本技能只负责把验证过的请求注册成 grant 嵌页。api-key 那套一行不改。
- **硬门槛同公式包**：注册任何 grant 前，先在 quant-buddy-skill 用 api-key 跑通对应接口、确认命中/出数，再回本技能注册。
- **固定场景例外**：`new_asset_page` 的三份 Grant payload 由 `skill_server` 固定生成并做结构/白名单校验，Agent 不接触也不自行注册，因此该快速通道不额外执行 quant-buddy-skill 预验证；页面打开时按 Grant 实时取数。
- **同源约束**：`access_dunhe=false`（页面绝不返回付费/敦和数据）、CORS/https 协议一致、signature 是公开凭证不打印给用户——与公式包完全一致。

## 硬规则

1. **中文参数走 @file 或环境变量**：Windows PowerShell 命令行直接传中文会被 GBK 截断。注册公式、写 spec 一律用 `@params.json`（UTF-8）或 `FP_PARAMS/BD_PARAMS/SP_PARAMS` 环境变量。
2. **公式必须先验证再注册（硬门槛）**：fork v2 由 `publish_workflow.py` 根据 review 解析出的最终 package 边界自动调用 `validate_package_set`，验证与注册从同一个 `{formulas,reads,begin_date}` 合同派生。Agent负责检查 review 中的公式语义、目标资产和同业映射，不得绕过生成的 plan 单独注册；复杂跨资产公式未审核、required outputs/reads 不一致，或 PE/PB 水位输出没有明确算法与正整数窗口时，发布器在网络调用前拒绝。fork 默认继承来源模板已经声明且能通过 QBS 的水位口径，不强制改成固定250日。
3. **验证参数也要换干净**：调用 `runMultiFormulaBatchStream` 时，`user_query` 必须反映当前用户请求和当前资产；若传 `task_id` 必须为本次新任务。复制示例时不能只替换 `formulas`，却留下“贵州茅台 factsheet”等旧 `user_query`，否则后台审计和回放会被污染。
4. **signature 是凭证**：不要打印到面向最终用户的对话里；看板会把它写进公开 HTML 供实时取数，发布前确认可接受。
5. **标签来源不要写 Agent**：显式传 `scene_tags` / `paradigm_tags` 时，`tagging_method` 用 `manual` / `migration` / `unknown`；需要 LLM 自动识别就调用 `scripts/static_page.py autotag`。不要再传 `tagging_method:"agent"`，也不要在 `tagging_meta.method` 里写 `agent`。
6. **失败要说清**：脚本返回 `code != 0` 时，向用户复述「卡在哪一步（命令名）+ 错误摘要」，不要以空白或纯日志结束。
7. **具体资产必须先验证，再解释或路由**：当用户请求涉及具体资产，尤其是美股、港股、ETF、特殊名称或中英文混写资产时，必须先执行上方"具体资产证据闸门"（`resolve_asset_data` 探测）；不要等到准备作负面判断时才验证。不得根据现实上市状态、所有权、公司常识、历史记忆或资产名称直觉，推断平台是否可取数，也不得据此选择代理资产、静态说明、数据通道或页面结构。资产库命中只证明 ticker 映射，`resolve_asset_data` 探测成功才证明对应数据能力；按用户实际需要验证行情/估值、画像、财务或其他数据，禁止为"更全面"无边界扩查。验证结果必须区分资产未映射、接口不支持、字段缺失和额度限制；单个字段缺失、窗口受限或额度限制不得扩大表述为资产不可用。只有工具证据与用户需求直接相关时，才在页面或回复中说明上市/私营、代理或市场身份；未验证前只能说「我先验证平台资产映射和数据可用性」。
8. **正文图片先上传后引用**：先用 `static_page.py image_upload` 获得目标 `page_id` 下的绝对 `https://pages.quantbuddy.cn/pages/assets/...webp` URL，再写入 HTML；禁止跨页复用托管 URL。图片必须带明确 `alt` 与 `width/height`；首屏和海报目标内不得 lazy，正文下方才可 `loading="lazy"`。标准 image panel 默认启用当前页大图预览，装饰图才设 `zoomable:false`；不要用新窗口打开图片 URL。fork 必须按 manifest 的 `images[]` 上传到目标页并替换 marker，不能保留来源图片 URL。
9. **`templates` 摘要必须覆盖全部候选，落盘失败不能裸奔**：`items_summary` 的条目数量必须等于 `item_count`（完整候选去重后的真实数量，不是服务端可能未重算的 `total`），不允许只看其中一部分候选就判定 `unmatched`；一旦返回 `error:"TEMPLATES_PERSIST_FAILED"` 或 `error:"TEMPLATES_RESPONSE_SHAPE_UNEXPECTED"`（落盘失败或响应结构异常），必须先向用户说明「范式候选未能完整确认，暂缓路由判断」，禁止在这种不完整信息下判定为 `unmatched` 走自建，也**不得通过重复调用 `templates` 来补救**（每个任务仍然只能调用一次这条硬规则不变）；确需重试仅限明确的瞬时网络失败，且只重试一次。
10. **Card Runtime 先做零副作用结构预检**：含 Card Runtime artifact 的 HTML 必须由 `publish_workflow.py` 在 QBS 验证、注册、图片上传和发布前用假凭证执行 `verify_page.mjs --card-runtime-structure-only`。正文与 Card 共用凭证时用 marker 数组扇出；每个数组元素仍须全局唯一并在 HTML 中恰好出现一次。禁止空 manifest 凭证；普通 `<img>` 必须有非空 `src`，仅显式声明 `data-qb-runtime-src` 且等待运行时赋值的预览图可以暂时为空；同时禁止注册等价的重复 Card package/grant。
11. **普通建页前必须先查范式卡并显式确认路由**：`new_page` 会校验完整候选并绑定路由。fork 必须声明借鉴度，且一经判定不得改判 unmatched；继承不成立时只能在 fork 内降为 Compose。`build_dashboard` 对 fork/inherit* 禁止整页构建；fork/compose 仅在 `fork_compose` 绑定后允许 `emit:"panel_block"`。
12. **本地验收与公网验收分责**：`fork-local` 在本地 `file://`（origin=null）下用放开同源策略的测试浏览器跑真实取数渲染（`security_mode:"disabled-web-security"`），布局/占位符/运行时错误/图片/Card Runtime 门禁照常执行；`public-smoke` 保持浏览器默认安全策略，数据接口 CORS/`Failed to fetch`/运行时失败仍严格拦截。平台注入的 `/webapi/skill/track` 分析埋点是 fire-and-forget，其 CORS/网络失败降为 `non_core_console_warnings`，不再让成功页面发布失败；数据接口（`queryDataGrant`/`queryFormulaPackage`）的失败仍是阻塞性核心错误。
13. **Fork 数据通道必须继承来源合同**：fork 的目标是替换标的并保持来源范式运行合同，不是重新设计数据层。来源模板某一角色使用公式包，目标页同一角色继续使用公式包；来源使用 `fast_query` / `stock_profile` / `composition_select` 数据授权，目标页继续使用同 kind、同 query_type、同响应形状的数据授权。禁止仅因“财务数据通常可走 fast_query(report)”就在 fork 中把来源财务公式包改成 grant，也禁止反向把来源 grant 改成公式包。只有 `unmatched` / 明确从零重建时才重新做通道选择；此时平台白名单报告期财务优先 `fast_query(query_type="report")`。
14. **用户本地 HTML 活页化优先保真接入 QBS**：当用户说“把这个本地 HTML/页面活页化、标准化处理”，且来源页调用用户自己的非 QBS 服务接口时，不走在线模板 fork，也不要求 `qbs_qbv_handoff_v1`。执行顺序固定为“先快照保底、再同页渐进增强”：先创建或复用稳定 `page_id`，把来源页当前可见状态原样写入该链接；若来源含 `fetch/axios/XMLHttpRequest/EventSource/WebSocket` 等异步逻辑，必须先运行 `scripts/capture_rendered_html.mjs`（或等价浏览器捕获）得到渲染完成且已冻结旧脚本的快照，并传 `source_snapshot_html_file + source_snapshot_html_sha256`。随后再验证/注册 QBS：直取数据使用 Data Grant，需要计算的口径使用公式包；成功区域才声明 `data-qb-live-mode="live"`，并按通道增加 `data-qb-live-tag="qbs-formula-package|qbs-data-grant"`。未转换区域保持快照原 DOM，不要求、也不得为了声明静态而注入 `data-qb-live-mode="static"`。完整或部分成功后都在同一个 `page_id` 写回：成功区域显示右上角低干扰 `● LIVE`，失败区域继续显示首次快照；全部失败或第二次写回失败时，活页仍保留完整快照，不生成通用错误页、不创建替代链接。来源/快照缺失、不可读或 SHA256 不一致时，在首次托管写入前 fail closed。终态自动使用 `preserve_html_qbs_live_delivery_v1`，按 `transformation_status` 如实说明 complete/partial/failed，并单独回传 `page_id` 和公开链接。本阶段只增加成功区域的 div live 声明与标准可见徽标，不引入 `data-qb-block-id`、Block Runtime 或 Block 持久化。
15. **CHANGELOG 仅作为版本审计**：维护、升级或排查历史行为变化时，先阅读 `CHANGELOG.md` 中最新版本及与问题相关的历史条目；执行页面任务时，当前规则仍以 `SKILL.md` + `workflows/**` + `tools/**` + `guides/**` 为准。CHANGELOG 可能包含已被后续版本反转或废弃的旧口径，禁止用历史条目覆盖当前规则。


## 工具一览

> 参数约定：所有脚本参数是**一个 JSON 字符串**位置参数（或 `@params.json` / 环境变量），如 `list '{"scope":"test_all"}'`。命令行也兼容 `--scope test_all` / `--key=value` 直觉写法（仅简单参数；公式、spec 等复杂结构仍走 `@file`/环境变量以免 GBK 截断）。

| 脚本 | 命令 | 作用 | 文档 |
|---|---|---|---|
| `scripts/formula_package.py` | `register` / `query` / `list` / `revoke` / `refresh` | 公式任务包：注册取数能力；query 支持 `outputs` 与 `result_mode=full|summary|last_values`，direct 使用 summary | [tools/formula_package.md](tools/formula_package.md) |
| `scripts/data_grant.py` | `register` / `query` / `list` / `revoke` / `refresh` | 数据授权：把一次 fastQuery/stockProfile/selectByComposition 请求钉死成 `grant_id`+`signature`，页面免 key 直取有界数据（取舍见「数据授权 vs 公式包」） | [tools/data_grant.md](tools/data_grant.md) |
| `scripts/build_dashboard.py` | （单命令，`emit:"panel_block"` 走局部产出） | spec → live 实时取数看板 HTML；局部产出模式只生成带 marker 的图表 `<script>` 片段，供 bespoke 页面内嵌图表用 | [tools/build_dashboard.md](tools/build_dashboard.md) |
| `scripts/stock_comparison.py` | `apply @params.json` | 为 `stock_analysis_instance_v1` 原生收盘价图并入基准序列、双 Y 轴和数据表；保持 `#priceChart` 单一 owner，拒绝用通用 panel 二次接管 | [tools/stock_comparison.md](tools/stock_comparison.md) |
| `scripts/stock_window.py` | `apply @params.json` | 为 legacy `stock_analysis_instance_v1` 设置滚动展示窗口；生成受控 HTML 后必须浏览器预检并 update 原 `page_id` | [tools/stock_window.md](tools/stock_window.md) |
| `scripts/chart_edit.py` | `inspect` / `add_series`（支持 `axis:"right"` 双轴）/ `remove_series` / `set_window` / `query_data` | 已发布页面单个图表的增删改查：只动被要求的那一处，不重新验证/计算页面上其它无关系列 | [tools/chart_edit.md](tools/chart_edit.md) |
| `scripts/compile_bespoke_page.py` | （单命令） | **【shell 处理脚本】** bespoke 主体 HTML → 内联公共 share shell / logo / qr-mini / data-kernel 的自包含 HTML | [guides/share-shell.md](guides/share-shell.md) |
| `scripts/retrofit_share_shell.py` | （单命令） | **【shell 处理脚本】** 旧 HTML/已发布页面 → 删除旧二维码/旧页头/旧页尾，套入公共 share shell（`assets/share-shell/`），可原链接 update | [tools/retrofit_share_shell.md](tools/retrofit_share_shell.md) |
| `scripts/data_kernel_retrofit.py` | （单命令） | 按 `QB_DATA_KERNEL` marker 或严格旧内核指纹，只替换页面中的 data-kernel；零个/多个命中均拒绝写回 | [tools/data_grant.md](tools/data_grant.md) |
| `scripts/static_page.py` | `new_asset_page` / `templates` / `direct_deliver` / `new_page` / `update_progress` / `publish_final` / `publish_verified` / `upload` / `update` / `download` / `image_upload` / `image_list` / `fork_prepare` / `fork_review_update` / `fork_validate` / `retrofit_card_runtime` / 其他管理命令 | 简单单股快速返回、范式路由、正文图片、direct 确定性交付、首链进度、分级浏览器门禁和页面发布管理 | [tools/static_page.md](tools/static_page.md) |
| `scripts/qbs_handoff_adapter.py` | `@handoff.json` | 校验 QBS computation capsule 的 lineage、合同 fingerprint 与 artifact hash，输出 covered/partial/unusable；不负责路由、ownership、构建或发布 | 本节“从 QBS 并行交接进入” |
| `scripts/qbs_bridge.py` | `resolve_asset_data` / `<quant-buddy-skill tool> @params.json` / `validate_package_set` / `validate_grant_set` | 统一实时路由探测并继承 QBV→QBS task_id；按最终 package/Grant 合同生成 route/grant/formula fingerprint 绑定收据 | 本节“新会话路由” |
| `scripts/publish_workflow.py` | `@publish-plan.json` | manifest v2驱动 review/合同预检、package+Grant验证、每role一次注册、多Marker扇出替换和单次 `publish_verified`；v1 JSON兼容 | [tools/publish_workflow.md](tools/publish_workflow.md) |
| `scripts/validate_agent_reply.py` | （单命令） | 校验发布器 SHA256 绑定的终态 contract 与 Markdown 草稿，并检查公开 URL、章节结构和敏感信息；可在成功后清理任务临时参数文件 | — |
| `scripts/verify_page.mjs` | （单命令） | 发布前/发布后页面验收：标准三视口、h1、占位符、横向溢出、控制台核心错误；批注迭代可用 `--profile ui-refinement`、`--extra-viewport` 与 `--min-visible-font-px`；范式卡加 `--card-runtime` 或 `--card-runtime-only` | [guides/browser-feedback-refinement.md](guides/browser-feedback-refinement.md) |
| `assets/data-kernel.js` | （前端内核，非脚本） | 手搓 bespoke 页共用的「取数 + 清洗 + 容错」一份；内联进页面 `<script>` 用 | [guides/bespoke-page.md](guides/bespoke-page.md) |
| `assets/share-shell/` | （公共组件） | 所有落地页共用的页头、页尾、刷新按钮、分享海报弹层、海报 canvas、复制链接与复制/下载行为 | [guides/share-shell.md](guides/share-shell.md) |
| `assets/live-card.css` | （公共组件） | 范式卡 artifact 的浅色卡片样式源；由 `build_dashboard.py` 作为 `data-qb-card-style` 内嵌进 card runtime artifact | [guides/essence-cover-card.md](guides/essence-cover-card.md) |
| `scripts/card_runtime_retrofit.py` | （被 static_page 调用） | 为已发布/官方精选页重建独立 card runtime artifact（`embedded-card-v1`），可原链接写回 | [tools/static_page.md](tools/static_page.md) |
| `guides/essence-cover-card.md` | （开发指南） | 页面精华浓缩为独立 4:3 card runtime artifact（`embedded-card-v1`，空白宿主独立 hydrate），并明确 artifact、范式卡快照与整页封面的职责边界 | — |

> **三条生产路**：固定页面先复用在线模板；标准看板走 `build_dashboard`（声明式快路）；要自定义版式/SVG 的设计页才写 bespoke 主体 HTML。
> **stock 原生图表 owner 门禁**：`stock_analysis_instance_v1` 的 `#priceChart` 始终由页面原生 runtime 持有；禁止用 `build_dashboard.py emit="panel_block"` 注入第二个 renderer。增加沪深300等基准线时必须使用 `scripts/stock_comparison.py` 扩展原生 load/render/table 生命周期。
> **stock legacy 时间窗口门禁**：`stock_analysis_instance_v1` 缺少 `QBV_RENDER_JS` marker 时，改窗口必须使用 `scripts/stock_window.py apply`，不要生成或执行 `output/*.py` 临时补丁。转换后必须本地浏览器预检、`static_page.py update` 同一页并做公网验收。
> 数据层统一调 `assets/data-kernel.js`（`QB.query` 取数、`QB.series/lastValue/topValues` 解包清洗），别再每页各抄 `fetch`/解包、各踩"假 0/缺口"的坑。见 [guides/bespoke-page.md](guides/bespoke-page.md)。
> 发布前用 `scripts/verify_page.mjs <html_file> --require-browser` 检查桌面与 390px/320px 移动端，确保无 `QB_SHARED_` / `replace_with_signature` / `pkg_replace` 残留、存在 `<h1>`、无关键横向溢出和核心取数脚本错误。页面声明 `stock_analysis_instance_v1` 且在 `data_sources.benchmark_series` 或 `comparison.benchmark_series` 配置基准时，脚本还会在 runtime pending 归零后等待稳定窗口，并强制检查最终 canvas、个股/基准两条有效图表序列、共同交易日、基准右侧 Y 轴、双 Y 轴和数据表列；同时静态拒绝原生 runtime 与 panel_block 共同接管 `#priceChart`。不能只因 runtime ready 或首帧短暂出现就视为对比页已交付。含范式卡 artifact 的页面加 `--card-runtime`（或 `--card-runtime-only`）验收 artifact/manifest/独立 hydrate。Playwright 不在默认 Node 搜索路径时，可用 `QBV_PLAYWRIGHT_MODULE_ROOT` 指向包含 `playwright/` 的 `node_modules` 根目录；可用 `CHROME_PATH` 指定浏览器可执行文件，未指定时自动发现 Chrome/Edge。若机器没有 Playwright/Chrome/Edge，脚本会明确标记为 `static-only`，不能当完整浏览器验收。

## 配置

`config.json`：填入 `api_key`（从 https://www.quantbuddy.cn/login 获取）。可建 `config.local.json` 覆盖 `endpoint` / `api_key` 等（不入库）。环境变量 `QUANT_BUDDY_API_KEY` 仅在 config.json / config.local.json 都没有 api_key 时兜底，不是常规配置方式。本技能所有脚本的 `main()` 都支持工具调用参数里的 `api_key` 字段临时覆盖（仅当次调用生效，优先级最高，见 `scripts/common.py::configure_trace_context`）；同一优先级还有环境变量 `QBV_API_KEY`，用于"手上是一份现成的 `@file` 参数（比如 `publish_workflow.py @publish-plan.json`，该 plan 文件按设计不含凭证）、不想现改这份文件塞 api_key"的场景——**不要**为了临时换 key 去设 `QUANT_BUDDY_API_KEY`，那个只在 config.json 为空时才生效，config.json 已有默认 key 时设它不会有任何效果。
