From 744117ac1d998bb85770552b471292f2da7765ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E9=BB=98=E6=B6=B5?= <21739308@qq.com> Date: Sat, 19 Sep 2026 08:07:55 +0800 Subject: [PATCH 01/11] =?UTF-8?q?=E6=98=8E=E7=A1=AE=E7=8B=AC=E7=AB=8B?= =?UTF-8?q?=E8=B7=9F=E8=B8=AASDK=E9=A6=96=E7=89=88=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E4=B8=8E=E5=AE=9E=E6=96=BD=E8=AE=A1=E5=88=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2026-09-19-pp-tracking-foundation.md | 78 +++++++++++++++ .../specs/2026-09-19-pp-tracking-design.md | 99 +++++++++++++++++++ 2 files changed, 177 insertions(+) create mode 100644 docs/superpowers/plans/2026-09-19-pp-tracking-foundation.md create mode 100644 docs/superpowers/specs/2026-09-19-pp-tracking-design.md diff --git a/docs/superpowers/plans/2026-09-19-pp-tracking-foundation.md b/docs/superpowers/plans/2026-09-19-pp-tracking-foundation.md new file mode 100644 index 0000000..cb78ed5 --- /dev/null +++ b/docs/superpowers/plans/2026-09-19-pp-tracking-foundation.md @@ -0,0 +1,78 @@ +# 独立 PP-Tracking SDK 首版实施计划 + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development 或 superpowers:executing-plans,按任务执行和审查。 + +**Goal:** 交付可本地使用和验证的独立跟踪SDK及统一风格Demo。 +**Architecture:** 先增加算法类标准,再用无依赖TypeScript实现两阶段关联和状态生命周期,React Demo消费同一公开API。 +**Tech Stack:** TypeScript 5.9、Vitest、Vite、React、Playwright、Python/NumPy参考。 +**Spec:** `docs/superpowers/specs/2026-09-19-pp-tracking-design.md` + +## Global Constraints + +- 门户根 `C:/Users/chenm/.codex/worktrees/segmentation-portal/chenmohan123.github.io`;SDK根 `F:/git/00_chenmohan/github/web-sdk-PP-Tracking`。 +- 文档、注释、提交中文;公开文档有等价英文。手动编辑用apply_patch;明确文件add,禁git add .。 +- pnpm始终加 `--config.verify-deps-before-run=false --config.manage-package-manager-versions=false`。 +- 规范源是门户standards/v1;不修改原门户工作目录,不嵌入检测SDK或虚构权重/GPU/Worker。 +- 生产runtime无依赖,Apache-2.0原创实现;不读/翻译上游Kalman/SORT源码实现算法。 +- 本轮本地实现及证据,不新增远程仓库/发布,不改npm身份配置。 +- 先标准后产品。每个任务遵守设计、只修改责任文件,独立验证提交;子代理不得派生子代理。 + +### Task 1: 纯算法标准与检查器 + +**Files:** standards/v1的README双语、schema、rules、runtime/demo/performance/docs契约、算法manifest模板和checklist;tools/sdk-standard-check的manifest/discover/rules和测试;skills/web-model-sdk-standard/references/scaffold.md。 +**Interfaces:** 生产1.2.0 `kind: algorithm` 与设计定义的algorithm字段;新DEMO-006状态复位、ALGORITHM-001规则;模型与算法分支互斥,旧清单兼容。 + +- [ ] 加失败用例:有效算法、缺algorithm、带model/cache、旧版kind算法、伪造kind、缺算法timing、缺复位标记、旧模型缺assets仍失败。 +```ts +expect((await scanRepository(algorithmRoot)).findings.filter(x=>x.level==='required'&&x.status==='fail')).toEqual([]); +// 将算法清单schemaVersion改为1.1.0后必须有CONFIG-001,不能获得模型规则豁免。 +``` +- [ ] 运行 `pnpm ... sdk:check:test` 确认新增回归会失败,再实现schema条件、适用规则、发现器和模板。不能让无效manifest跳过必需检查;skip需证据说明。 +- [ ] 更新标准双语入口及对应契约,模型要求保留;算法cold/warm指新实例/复用状态,无模型耗时。 +- [ ] 运行 `pnpm ... sdk:check:test`、`pnpm ... test`、`pnpm ... build`;提交 `扩展纯算法SDK标准并保持模型契约兼容`。 + +### Task 2: 独立跟踪核心与来源证据 + +**Files:** SDK新建AGENTS.md、package.json、tsconfig/vitest、scripts/build.mjs及check-package.mjs、src/{types,errors,kalman,assignment,tracker,index}.ts、tests/*.test.ts、tests/fixtures/math-reference.json及生成脚本、LICENSE、NOTICE、docs/zh-CN/algorithm.md与docs/en/algorithm.md。 +**Interfaces:** createTracker/update/reset/dispose和全部字段/默认值严格依设计;runtime={requestedBackend:'cpu',actualBackend:'cpu',executionMode:'main',runtimeVersion:'web-sdk-pp-tracking@0.1.0'};timings五项。 + +- [ ] SDK空目录先运行门户 `pnpm ... sdk:check -- --repo --format json --out reports/sdk-standard/pp-tracking-before.json`,保留预期缺失。 +- [ ] 初始化 `codex/tracking-foundation` 本地Git,先写数学/匹配/生命周期失败测试;不复制其他SDK的runtime。 +```ts +const a=createTracker({minHits:1}), b=createTracker({minHits:1}); +const frame={timestampMs:0,imageSize:{width:640,height:480},detections:[{box:{x:20,y:20,width:40,height:80},score:0.9,classId:0}]}; +expect(a.update(frame).tracks[0].id).toBe(1); +expect(b.update(frame).tracks[0].id).toBe(1); +expect(()=>a.update(frame)).toThrow(); +a.reset(); expect(a.update(frame).generation).toBe(1); +``` +- [ ] 实现8维Kalman,文档定义F、H、P/Q/R与dt,使用小型矩阵运算/Joseph covariance保证数值;独立Python/NumPy参考至少预测、连续更新、不同dt。 +- [ ] 实现确定性门限内全局分配(匈牙利或等价),低分不新建、类别隔离、tentative/tracked/lost/removed、时间上限/数目上限;校验和计算事务性提交,dispose幂等。 +- [ ] 补充非法输入/配置、引用隔离、capacity、big-gap、score边界、lost不可低分复活、贪心反例、门限反例测试。跟踪ID只在该实例,移除事件只返回一次。 +- [ ] 运行 `pnpm ... test`、`typecheck`、`build`、`check:package`;记录源码基础/限制和npm pack实际内容;提交核心。 + +### Task 3: 独立Demo、双语文档、示例及发布脚本 + +**Files:** SDK demo/{index.html,vite.config.ts,ui-tokens.json,src/*}、examples/vanilla与examples/react、README双语、docs双语六组指南、sdk-manifest.yaml、CHANGELOG、.github/workflows/{ci,pages,release}.yml、tests/browser.mjs及Demo数据测试。 +**Interfaces:** 消费Task2实际公开API,不改核心契约;manifest按Task1模板。Demo输入JSON={frames:TrackingFrame[]};文件5MiB、帧数3000、每帧100框上限。 + +- [ ] 阅读旋转Demo和PP-Detection样式文件作为布局参考,复用门户ui-tokens,不复制推理;实现原创合成序列与schema完整导入校验。 +```ts +// 导入失败保留现有序列与结果;回放每步使用frame.timestampMs,seek先reset再顺序推进。 +// 当前状态显示CPU / JavaScript / Main;丢失轨迹虚线,无权重/缓存/后端选择器。 +``` +- [ ] 实现品牌栏、紧凑控制、SVG结果/轨迹、播放暂停单步重播、文件导入、结果JSON下载、语言切换;结果优先显示,详情折叠,390px可用。 +- [ ] 从标准模板创建manifest/README/checklist,文档明确低分检测保留、seek/reset和ID非身份;全部未发布链接标记为计划地址,安装使用本地包方式,不声称npm已存在。 +- [ ] Vanilla示例真正调用构建包API,React引用Demo,构建两者;CI测试类型和打包及浏览器,Pages使用官方actions和最小权限;release要求先通过验证后以OIDC发布,首次发布尚待授权。 +- [ ] 浏览器自动覆盖中文/英文、单步/播放暂停/重播、低分/遮挡、导入成功失败、导出、无横向滚动、Vanilla;运行unit、typecheck:demo、build:demo、test:browser及sdk:check;提交完整Demo与文档。 + +### Task 4: 桌面验收证据与交付复核 + +**Files:** SDK scripts/evaluation/*、reports/2026-09-19-desktop/*、docs兼容/性能指南、manifest.verification;门户reports/sdk-standard/pp-tracking-after.json与计划验收记录。 +**Interfaces:** 消费已完成SDK,不能用静态声明替代运行;证据区分机制正确性和浏览器性能,无移动端/MOT精度承诺。 + +- [ ] 可复现合成序列性能测量至少10、50、100框,注明浏览器/OS/CPU/日期及样本数、p50/p95;包含cold创建与warm更新,固定输入可重跑,无绝对性能断言。 +- [ ] 记录真实浏览器主线程运行、两种语言、390px、Vanilla及导入导出,保留机器可读摘要和命令。截图放忽略目录,报告只列路径。 +- [ ] 校验npm pack、无runtime依赖和源码来源;运行全部SDK tests/typechecks/builds/browser与门户sdk:check/tests/build。 +- [ ] 更新verification只填实际测试的浏览器版本和设备,sdk:check远程skip如实保留;记录发布尚未执行及真实视频质量未测。 +- [ ] 提交证据和兼容说明,独立整分支审查后交付本地Demo及可审查代码。 diff --git a/docs/superpowers/specs/2026-09-19-pp-tracking-design.md b/docs/superpowers/specs/2026-09-19-pp-tracking-design.md new file mode 100644 index 0000000..b7aa7ab --- /dev/null +++ b/docs/superpowers/specs/2026-09-19-pp-tracking-design.md @@ -0,0 +1,99 @@ +# 独立 PP-Tracking SDK 首版设计 + +日期:2026-09-19。依据用户已确认的独立 SDK 路线及继续推进指令。 + +## 范围与来源 + +实现 `web-sdk-pp-tracking`,仓库目录 `F:/git/00_chenmohan/github/web-sdk-PP-Tracking`。 +这是单 SDK 工作;门户仅扩展标准,不连接检测 SDK、不新增 Workflow。 +首版为 TypeScript CPU 主线程算法,无模型、无推理依赖、无下载或缓存按钮。 +Worker 留待实测有必要时增加,首版只声明 `main`,不提供空实现。 + +采用 ByteTrack 论文的高/低分两阶段关联思想,独立编写恒速 Kalman、 +带未匹配选项的最小代价分配及状态机。不得翻译或复制前次评估发现来源 +义务不清晰的 ByteTrack/DeepSORT/Paddle Kalman 或 SORT/OC-SORT 实现。 +生产代码依据数学定义、论文机制和本设计编写,Apache-2.0 仅声明本项目 +代码许可;NOTICE 记录论文、算法差异和评估来源,不声称官方移植或逐值兼容。 +来源评估见 `reports/tracking/2026-09-18-feasibility/README.md`。 + +## API 和状态 + +公开 `createTracker(options?)`,返回 `update(frame, { signal }?)`、 +`reset()`、`dispose()`;更新同步,AbortSignal 只能在计算开始前取消。 +输入 `TrackingFrame` 为 `timestampMs`、`imageSize: {width,height}` 和 +`detections: {box:{x,y,width,height},score,classId}[]`。坐标为图像像素的 +轴对齐框;类别为非负安全整数,分数在 [0,1],尺寸正且所有数值有限。 +框须在图像范围内;不自动裁剪。时间戳为非负有限数,严格递增;seek +或图像尺寸变化必须 reset。所有校验在状态修改前完成,失败不得推进时钟或 ID。 + +结果返回 `generation`、`timestampMs`、`tracks`、`removed`、`runtime`、 +`timings`。轨迹含 `id`、`classId`、`box`、`state`、`observed`、 +`score: number|null`、`ageMs`、`hits`、`missedMs`;状态为 tentative、 +tracked、lost。`removed` 为仅本次移除的轨迹,state=removed。 +预测轨迹 `observed=false, score=null`,不得把历史置信度当成本帧检测。 +ID 为实例私有安全整数,同代次内不复用;reset 后代次+1,ID从1重新开始。 +不同实例互不影响;dispose 幂等,此后 update/reset 抛稳定错误。 +返回对象和输入对象均不与内部可变状态共享。 + +默认阈值:lowScoreThreshold=0.1、highScoreThreshold=0.5、 +newTrackThreshold=0.6;high>=low且new>=high。默认 minHits=2, +matchIouThreshold=0.3、lowMatchIouThreshold=0.2、maxLostMs=1000、 +largeGapMs=2000、maxDetections=100、maxTracks=200。 +数量上限为正安全整数且不超过500;minHits为1至100的整数。 +时间限制须为有限正数,largeGapMs>=maxLostMs。 + +所有阶段严格隔离类别。先用 tracked/lost 与高分框关联,再用未匹配的 +tracked 与低分框关联;lost 不以低分框恢复。再将 tentative 与剩余高分框 +关联;未匹配 tentative 当帧移除。剩余高分框达到 new 阈值时创建 tentative, +连续命中达到 minHits 后 tracked(minHits=1立即 tracked)。 +无匹配 tracked 转 lost;lost 超过 maxLostMs 在匹配前移除。单次时间间隔 +超过 largeGapMs 时先移除旧轨迹再处理新观测;不通过补空帧循环推进。 +容量满时跳过新建,结果报告 `droppedDetections`,不得驱逐有效轨迹。 +关联必须是确定性的全局最小代价匹配,门限内才可匹配,允许双方未匹配。 +测试须覆盖贪心错误和先全局分配再过滤导致有效匹配丢失的反例。 + +运动状态为 [cx,cy,w,h,vx,vy,vw,vh],dt以秒计。独立实现标准线性 +Kalman predict/update,协方差初值、过程和观测噪声须写入算法文档。 +输出宽高保持正数,数值失败以稳定错误报告,不污染已提交状态。 +不做外观 ReID、不保证交叉或掉头时身份正确,不把轨迹 ID 当真实身份。 + +## 标准扩展 + +规范升至1.2.0,继续接受1.0.0/1.1.0模型manifest。 +新增仅1.2.0可用的 `kind: algorithm`;缺省及 `kind: model` 保持模型规则。 +算法manifest必须有 algorithm(id、version、family、source、license、 +input、output、stateful),禁止 model/cache 字段;模型禁止 algorithm。 +旧版本不得借kind逃避模型检查。runtime继续报告实际后端与执行模式。 +算法性能要求 validationMs、predictionMs、associationMs、updateMs、totalMs; +不要求模型下载或缓存耗时。Demo使用 data-sdk-algorithm-info 和 +data-sdk-state-reset,保留runtime/timing标记。规则应明确模型/算法适用性, +不适用项以skip及理由记录;无效清单不得触发算法豁免。 +算法输入、来源、许可、状态生命周期和复位为必填契约。 + +## Demo 和文档 + +延续 PP-Detection:浅色背景、紧凑品牌栏、左侧输入/参数控制、右侧可视化 +结果、耗时和运行信息折叠;中文默认、可切英文,390px无横向滚动。 +提供原创合成检测框序列(直行、低分恢复、短遮挡、交叉掉头),SVG绘制 +框和ID/轨迹,播放、暂停、单步、重新开始、JSON导入、导出本轮结果。 +导入只读本地文件,限制5MiB、3000帧、每帧100框;完整验证后原子替换。 +选择示例、重播、seek均reset。播放用记录的timestamp,与浏览器帧率分离。 +输入和预测视觉可区分,切语言不清空状态。呈现算法限制,但不塞入操作长文。 +不做模型目录、检测/摄像头前置流程;不伪装真实视频测评。 + +双语README及quick-start、API、compatibility、troubleshooting、 +privacy/deployment、performance;Vanilla可运行示例及React Demo参考。 +打包只包括构建产物、类型、README、LICENSE、NOTICE,无React运行依赖。 +提供CI、Pages与发布workflow,但本轮本地实现不声称npm或Demo已经发布。 + +## 验收 + +先扩标准再实现产品。创建空SDK目录即运行sdk:check,保留before/after报告。 +单测覆盖数学参考、全局关联、低分恢复、遮挡/超时、类别隔离、实例隔离、 +reset/dispose、无效输入原子性、输出引用隔离、阈值边界和数量上限。 +用独立 NumPy 公式参考核对数值;可用先前机制输入评估行为差异,不能把 +官方输出逐值相等设为目标。真实序列暂未有明确授权样本,首版如实只报告 +合成机制验证,不声称MOT精度。桌面Chromium验证Demo、Vanilla、导入导出、 +中英文、空态、重播和390px布局,记录版本/日期/操作/限制。 +执行typecheck、test、build、check:package、build:demo、浏览器测试及门户 +checker/tests/build。交付本地可审查版本;远程发布须有明确发布范围。 From 063e8c215b1791d43d3ee704bcb83006ddc43611 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E9=BB=98=E6=B6=B5?= <21739308@qq.com> Date: Sat, 19 Sep 2026 08:31:29 +0800 Subject: [PATCH 02/11] =?UTF-8?q?=E6=89=A9=E5=B1=95=E7=BA=AF=E7=AE=97?= =?UTF-8?q?=E6=B3=95SDK=E6=A0=87=E5=87=86=E5=B9=B6=E4=BF=9D=E6=8C=81?= =?UTF-8?q?=E6=A8=A1=E5=9E=8B=E5=A5=91=E7=BA=A6=E5=85=BC=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../references/scaffold.md | 10 ++ standards/v1/README.en.md | 34 ++++++- standards/v1/README.md | 30 +++++- standards/v1/demo-contract.md | 19 +++- standards/v1/docs-release-contract.md | 13 ++- standards/v1/performance-contract.md | 14 ++- standards/v1/rules.yaml | 25 ++++- standards/v1/sdk-contract.md | 19 ++++ standards/v1/sdk-manifest.schema.json | 31 +++++- standards/v1/templates/demo-checklist.md | 12 ++- standards/v1/templates/release-checklist.md | 9 +- .../v1/templates/sdk-manifest.algorithm.yaml | 40 ++++++++ tools/sdk-standard-check/algorithm.test.ts | 98 +++++++++++++++++++ tools/sdk-standard-check/checker.test.ts | 4 +- tools/sdk-standard-check/src/discover.mjs | 19 ++++ tools/sdk-standard-check/src/manifest.mjs | 11 ++- tools/sdk-standard-check/src/rules.mjs | 11 ++- tools/sdk-standard-check/src/types.mjs | 3 + .../sdk-standard-check/standard-files.test.ts | 6 +- 19 files changed, 371 insertions(+), 37 deletions(-) create mode 100644 standards/v1/templates/sdk-manifest.algorithm.yaml create mode 100644 tools/sdk-standard-check/algorithm.test.ts diff --git a/skills/web-model-sdk-standard/references/scaffold.md b/skills/web-model-sdk-standard/references/scaffold.md index ca5d9e0..2a2e1aa 100644 --- a/skills/web-model-sdk-standard/references/scaffold.md +++ b/skills/web-model-sdk-standard/references/scaffold.md @@ -9,3 +9,13 @@ Vanilla baseline and React reference example. Keep the runtime framework-neutral Run the portal checker against the new local path before opening a release PR. Do not publish npm or create a GitHub Release as part of scaffolding unless the user explicitly requests that external operation. + +## 纯算法脚手架 + +纯算法选择 `standards/v1/templates/sdk-manifest.algorithm.yaml`,复制为 +`sdk-manifest.yaml`;模型继续使用原模型模板。按标准 1.2.0 填写 algorithm +的来源、许可、输入输出和 stateful,删除模型下载/缓存 UI 的假设。 +沿用双语 README 和公共 checklist,其中模型专属项不适用;算法专属项必须 +验证。状态生命周期、复位、Demo 标记和计时语义均以 standards/v1 契约为准。 +运行本地检查器,保留所有不适用项的 skip 理由与清单证据;无效清单不能 +获得算法豁免。仅声明实际已实现的执行模式,不预先添加 Worker 空实现。 diff --git a/standards/v1/README.en.md b/standards/v1/README.en.md index d86f2db..d01c3e3 100644 --- a/standards/v1/README.en.md +++ b/standards/v1/README.en.md @@ -1,9 +1,8 @@ # Web Model SDK Standard v1 [中文(默认)](README.md) is the primary entry point. This equivalent English -document uses standard version `1.1.0`. Version `1.1.0` remains compatible -with `1.0.0` SDK manifests because it adds repository governance rather than -runtime fields. +document uses standard version `1.2.0`, retaining compatibility with `1.0.0` +and `1.1.0` model manifests. Read the [runtime contract](sdk-contract.md), [single-SDK Demo contract](demo-contract.md), [portal boundary](portal-contract.md), [docs and release contract](docs-release-contract.md), @@ -12,7 +11,7 @@ Read the [runtime contract](sdk-contract.md), [single-SDK Demo contract](demo-co then the machine-readable [rules](rules.yaml), [manifest schema](sdk-manifest.schema.json), and [UI tokens](ui-tokens.json). -An SDK repository owns its framework-neutral runtime, npm package, current-model +An SDK repository owns its framework-neutral runtime, npm package, current-model or algorithm Demo, documentation, examples, benchmarks, CI, and Releases. The portal owns catalogs, categories, comparisons, introduction pages, and Workflow entry points. Cross-SDK execution starts only after compatible public contracts and a @@ -63,3 +62,30 @@ and path, an HTTP(S) `downloadUrl` with a host, positive byte count, and a An explicitly selected source must not be silently replaced after failure; only an `auto` policy may try sources in manifest order. A Git LFS pointer is not the browser-downloadable model payload. + +### Pure algorithm SDKs + +Only version `1.2.0` permits `kind: algorithm`. Omitted kind and `kind: model` +retain model requirements; an older version cannot claim algorithm exemptions. +Algorithm manifests require `algorithm` and prohibit `model` and `cache`; +model manifests prohibit `algorithm`. Required algorithm fields are nonempty +strings id, version, family, source, license, input, output, plus boolean stateful. +Input/output describe the contract or reference complete API documentation; +source records papers, code provenance, and implementation differences. Review +license obligations separately. + +Use the [algorithm template](templates/sdk-manifest.algorithm.yaml) and shared +checklists. Runtime reports the actual backend and execution mode; declare only +implemented modes. Required timings are validationMs, predictionMs, associationMs, +updateMs, totalMs. Cold means a new instance; warm means reused instance state. +Do not invent model download or cache timings. Demo markers are +data-sdk-algorithm-info, data-sdk-runtime-info, data-sdk-timing, and +data-sdk-state-reset. Document inputs, outputs, provenance, license, state +lifecycle, and reset semantics. + +Rules with appliesTo specify model or algorithm scope; omitted scope means both. +MODEL-001, CACHE-001, DEMO-004 are model-only. ALGORITHM-001 and DEMO-006 are +algorithm-only. Inapplicable rules retain skip status, manifest evidence path, +and a reason. Only a fully validated manifest grants type exemptions. Invalid +manifests produce CONFIG-001 and cannot bypass model requirements by claiming +algorithm kind. Repositories without manifests retain legacy model checks. diff --git a/standards/v1/README.md b/standards/v1/README.md index c750c1a..89e5966 100644 --- a/standards/v1/README.md +++ b/standards/v1/README.md @@ -1,8 +1,8 @@ # Web Model SDK Standard v1 中文是默认入口;[English](README.en.md) 提供等价英文说明。本文是所有 -SDK、Demo、门户和 Workflow 任务的阅读入口,规范版本为 `1.1.0`。`1.1.0` -仍兼容 `1.0.0` SDK manifest;模型清单可以通过可选的 `variants` 和 `sources` +SDK、Demo、门户和 Workflow 任务的阅读入口,规范版本为 `1.2.0`, +仍兼容 `1.0.0` / `1.1.0` 模型 SDK manifest;模型清单可以通过可选的 `variants` 和 `sources` 声明同一模型的精度、量化、后端和分发来源。 ## 阅读顺序 @@ -19,7 +19,7 @@ SDK、Demo、门户和 Workflow 任务的阅读入口,规范版本为 `1.1.0` ## 分层原则 -- 单 SDK 仓库负责当前模型的 runtime、npm 包、独立 Demo、文档、示例、 +- 单 SDK 仓库负责当前模型或算法的 runtime、npm 包、独立 Demo、文档、示例、 基准和 GitHub Release。 - `chenmohan123.github.io` 负责目录、分类、比较、SDK 介绍页以及组合技和 Workflow 的入口;单 SDK 介绍页跳转到对应仓库、npm 和 Demo。 @@ -27,7 +27,7 @@ SDK、Demo、门户和 Workflow 任务的阅读入口,规范版本为 `1.1.0` 才实现;必要时可以拆成独立 Workflow 网站。 统一的是语义、状态、数据字段和视觉令牌,不是每个页面的像素级复制。 -单 SDK Demo 是当前模型的工作台,门户是目录/检视器。 +单 SDK Demo 是当前模型或算法的工作台,门户是目录/检视器。 ## 合规等级 @@ -94,3 +94,25 @@ CDN/H5/web-view 的兼容基线;SDK runtime 不得依赖 UI 框架。Vue、CDN 40 至 64 位十六进制不可变 `revision`、仓库和路径、含主机的 HTTP(S) `downloadUrl`、正整数 `bytes` 及 64 位十六进制 `sha256`。显式选择的来源失败时不得静默换源;只有 `auto` 策略可以按清单尝试。Git LFS pointer 文件不是浏览器可用的模型本体。 + +### 纯算法 SDK + +仅 `1.2.0` 可声明 `kind: algorithm`。缺省 kind 或 `kind: model` +沿用模型规则,旧版不得通过算法 kind 绕过模型要求。算法与模型分支互斥: +算法必须声明 `algorithm`,禁止 `model` 和 `cache`;模型禁止 `algorithm`。 +`algorithm` 必填 id、version、family、source、license、input、output(非空字符串) +及 stateful(布尔值)。input/output 描述输入输出契约或指向完整 API 文档; +source 记录论文、代码来源和实现差异,许可义务须独立核对。 + +算法使用 [算法清单模板](templates/sdk-manifest.algorithm.yaml) 和公共 checklist。 +runtime 继续报告实际后端和执行模式,只声明已经实现的模式。 +算法耗时为 validationMs、predictionMs、associationMs、updateMs、totalMs; +cold 指新实例,warm 指复用实例状态,不伪造模型下载和缓存耗时。 +Demo 使用 data-sdk-algorithm-info、data-sdk-runtime-info、data-sdk-timing +和 data-sdk-state-reset。状态生命周期、复位、来源、许可、输入输出必须有文档。 + +规则的 appliesTo 声明模型或算法适用性;未声明表示两者适用。 +MODEL-001、CACHE-001、DEMO-004 仅适用模型;ALGORITHM-001、DEMO-006 +仅适用算法。不适用项保留 skip、清单证据路径及理由。 +只有完整校验通过的清单可决定类型豁免;无效清单仍产生 CONFIG-001, +不能靠 algorithm 声明跳过模型必需规则。无清单的旧仓库沿用模型检查。 diff --git a/standards/v1/demo-contract.md b/standards/v1/demo-contract.md index 9e7cba0..cb97476 100644 --- a/standards/v1/demo-contract.md +++ b/standards/v1/demo-contract.md @@ -1,6 +1,6 @@ # Single SDK Demo Contract -An SDK Demo focuses on one SDK/model. It is not a catalog and does not embed +An SDK Demo focuses on one SDK/model or algorithm. It is not a catalog and does not embed another SDK's inference implementation. ## Semantic regions @@ -17,7 +17,7 @@ another SDK's inference implementation. Chinese is the initial language. Switching to English changes UI copy only; refreshing may restore Chinese unless the Demo documents persistence. -The Demo exposes current-model cache usage, a current-model cleanup action, a +A model Demo exposes current-model cache usage, a current-model cleanup action, a global cleanup action, and a privacy statement. It must not render broken image previews for empty state. DOM markers such as `data-sdk-cache-clear`, `data-sdk-model-info`, `data-sdk-runtime-info`, and `data-sdk-timing` make the @@ -25,3 +25,18 @@ contract testable without prescribing a framework. Use [ui-tokens.json](ui-tokens.json) for colors, spacing, radius, focus, and status styles. Responsive layouts must not overflow on a 390px viewport. + +## 纯算法 Demo(1.2.0) + +上述模型名称、模型参数和缓存控件仅适用模型 SDK。纯算法 Demo 聚焦当前算法, +展示算法版本、家族、来源、许可、输入输出及状态限制;使用 +`data-sdk-algorithm-info` 替代模型信息标记,并保留 +`data-sdk-runtime-info`、`data-sdk-timing`。 + +提供 `data-sdk-state-reset` 控件并连接状态复位;重新开始、切换输入序列或 +seek 时按算法契约复位。复位同时清理当前结果,不冒充缓存清理。 +切换语言不得清空算法状态。无状态算法也提供清空结果的复位入口。 +不展示模型下载、缓存按钮或虚构 loading/downloading 阶段;算法计算状态仍 +使用适用的 idle、ready、running、success、error、unsupported 等语义。 +五个算法耗时字段见性能契约。静态标记检查只证明声明存在,交互必须通过 +浏览器验证。中文默认、隐私说明、可访问性和 390px 布局要求仍然适用。 diff --git a/standards/v1/docs-release-contract.md b/standards/v1/docs-release-contract.md index 6cef843..523ffa1 100644 --- a/standards/v1/docs-release-contract.md +++ b/standards/v1/docs-release-contract.md @@ -8,7 +8,7 @@ troubleshooting, privacy/deployment, and performance guidance. README and npm metadata expose install instructions, package name/version, GitHub repository, and live Demo. A public repository has CI, a changelog, at least one GitHub Release, and GitHub About description, Homepage/Demo URL, and -topics. Release notes identify model source, license, default assets, runtime +topics. Model release notes identify model source, license, default assets, runtime backends, and known limitations. Repository protection and live Demo delivery follow the @@ -19,3 +19,14 @@ files alone do not prove that those settings are active. Do not claim universal browser or device support from feature detection alone. Every compatibility entry records browser, OS, device, runtime/driver where relevant, and test date. + +## 纯算法文档与发布(1.2.0) + +算法 SDK 保留上述双语文档、示例、发布和带日期验证要求。以算法来源、许可、 +输入输出契约、状态生命周期和复位语义替代模型资产说明;同时记录算法版本、 +家族、参数约束、后端、性能及已知限制。说明独立实现与参考来源的关系, +不得在缺少证据时声称官方移植、逐值兼容或真实数据精度。 + +算法发布说明列出来源/论文、代码许可、实现差异、状态和 API 变化,不编造 +模型资产、模型校验和或缓存说明。尚未发布 npm/在线 Demo 时如实标明本地 +状态;发布及治理规则仍须按适用范围留存远程证据。 diff --git a/standards/v1/performance-contract.md b/standards/v1/performance-contract.md index 898609e..b8b46d0 100644 --- a/standards/v1/performance-contract.md +++ b/standards/v1/performance-contract.md @@ -1,6 +1,6 @@ # Performance and Timing Contract -Expose stable timing names with documented cold/warm semantics: +Model SDKs expose stable timing names with documented cold/warm semantics: ```text modelDownloadMs @@ -24,3 +24,15 @@ the two without labeling them. Recommended evidence includes peak memory, cache bytes, model precision, batch size, browser/device, and test date. These measurements are observations for the stated matrix, not universal benchmarks. + +## 纯算法耗时(1.2.0) + +算法结果和 Demo 必须提供 validationMs(输入校验)、predictionMs(状态预测)、 +associationMs(观测关联)、updateMs(状态更新)、totalMs(本次计算总耗时), +单位为毫秒。不执行的阶段以 0 表示并说明原因,不虚构模型下载或缓存耗时。 +各阶段边界和 totalMs 的计时范围必须文档化,不能以阶段相加冒充实测总耗时。 + +cold 指新算法实例首次处理;warm 指复用同一实例的已有状态。 +记录初始化/复位边界、输入规模、状态规模、设备、浏览器、runtime 和测试日期, +不能把有状态计算不同输入规模的耗时直接比较为加速比。 +仍报告实际后端及 main/worker 模式,不以特性检测推断兼容性或精度。 diff --git a/standards/v1/rules.yaml b/standards/v1/rules.yaml index 0df3c05..0eac3bd 100644 --- a/standards/v1/rules.yaml +++ b/standards/v1/rules.yaml @@ -1,4 +1,4 @@ -standardVersion: "1.1.0" +standardVersion: "1.2.0" rules: - id: META-001 level: required @@ -41,6 +41,7 @@ rules: message: Demo provides an in-page Chinese/English toggle remediation: Add a language toggle that does not alter inference state. - id: DEMO-004 + appliesTo: [model] level: required detector: demoCacheClear message: Demo exposes current-model and global cache cleanup controls @@ -48,9 +49,22 @@ rules: - id: DEMO-005 level: required detector: demoTimingMarkers - message: Demo exposes model/runtime/timing information - remediation: Render data-sdk-model-info, data-sdk-runtime-info, and data-sdk-timing regions. + message: Demo 展示当前模型或算法的信息、运行信息和耗时 + remediation: 模型使用 data-sdk-model-info,算法使用 data-sdk-algorithm-info;两者保留 data-sdk-runtime-info 和 data-sdk-timing。 + - id: DEMO-006 + appliesTo: [algorithm] + level: required + detector: demoStateReset + message: 算法 Demo 提供状态复位操作 + remediation: 使用 data-sdk-state-reset 标记复位控件,并按状态生命周期契约连接 reset。 + - id: ALGORITHM-001 + appliesTo: [algorithm] + level: required + detector: algorithmInformation + message: 算法清单声明身份、家族、来源、许可、输入输出与有状态语义 + remediation: 按 sdk-manifest.algorithm.yaml 填写 algorithm 字段并记录状态生命周期。 - id: MODEL-001 + appliesTo: [model] level: required detector: modelInformation message: Manifest declares model identity, precision, size, source, license, and checksum @@ -63,9 +77,10 @@ rules: - id: PERF-001 level: required detector: performanceTimings - message: Standard load and inference timing fields are declared - remediation: Document download, cache, integrity, session, inference, and total timing. + message: 声明当前 SDK 类型所需的标准耗时字段 + remediation: 模型保留下载、缓存、校验、会话、推理和总耗时;算法声明 validationMs、predictionMs、associationMs、updateMs、totalMs。 - id: CACHE-001 + appliesTo: [model] level: required detector: cacheContract message: Cache storage, versioning, estimate, and cleanup are declared diff --git a/standards/v1/sdk-contract.md b/standards/v1/sdk-contract.md index 6362f84..2db6f72 100644 --- a/standards/v1/sdk-contract.md +++ b/standards/v1/sdk-contract.md @@ -26,9 +26,28 @@ an explicit option and the result reports both requested and actual backend. The manifest is the source of truth for model identity, version, assets, precision, format, checksum, input/output contract, and verification matrix. +上述加载、模型下载和推理要求适用于模型 SDK;纯算法接口见下文。 + 模型清单兼容旧版必填的 `model.assets`,并可用 `model.variants[]` 描述同一 模型的 FP32、FP16、INT8 等精度或量化变体。变体必须声明 `id`、`precision`、 `quantization`、`opset`、文件大小、参数量、`wasm`/`webgpu` 后端及一个或多个 来源。来源的 `kind` 只能是 `git-lfs`、`huggingface`、`modelscope`、`custom`, 且必须使用 40 至 64 位十六进制不可变 revision、含主机的 HTTP(S) 下载地址、文件大小和 SHA-256;显式来源失败时 不得自动换源,只有 `auto` 策略可以按清单尝试。Git LFS pointer 不是模型本体。 + +## 纯算法 runtime(1.2.0) + +算法 SDK 不加载模型,不实现虚构的 load、下载或缓存接口。工厂名称和同步/ +异步计算接口可按领域定义,但必须公开输入、输出、参数约束、稳定错误码及 +资源释放契约。algorithm 的 id、version、family、source、license、input、 +output 为非空字符串,stateful 为布尔值;模型 SDK 不得声明此分支。 + +有状态算法必须文档化状态初始化、推进、实例隔离、reset 和 dispose 行为, +包括复位后 ID/代次含义、无效输入是否改变状态,以及释放后可调用的接口。 +复位必须清除算法实例状态;无状态算法也须说明复位如何清空 Demo 结果。 +文档必须说明取消的实际粒度,同步主线程算法只能在计算开始前检查取消, +不得声称中途抢占。来源与许可须区分算法思想、参考代码和本项目独立实现。 + +结果仍报告 requestedBackend、actualBackend、executionMode 和 runtimeVersion, +CPU 主线程实现声明 cpu/main。不得因没有模型而放宽实际后端报告、实例隔离、 +框架无关或带日期兼容性证据的要求。算法性能字段见性能契约。 diff --git a/standards/v1/sdk-manifest.schema.json b/standards/v1/sdk-manifest.schema.json index 130ff38..3dcee96 100644 --- a/standards/v1/sdk-manifest.schema.json +++ b/standards/v1/sdk-manifest.schema.json @@ -3,9 +3,10 @@ "$id": "https://chenmohan123.github.io/web-model-sdk-standard/v1/sdk-manifest.schema.json", "title": "Web Model SDK Standard v1 Manifest", "type": "object", - "required": ["schemaVersion", "id", "name", "summary", "package", "repository", "demo", "docs", "runtime", "model", "performance", "cache", "examples", "verification"], + "required": ["schemaVersion", "id", "name", "summary", "package", "repository", "demo", "docs", "runtime", "performance", "examples", "verification"], "properties": { - "schemaVersion": { "type": "string", "enum": ["1.0.0", "1.1.0"] }, + "schemaVersion": { "type": "string", "enum": ["1.0.0", "1.1.0", "1.2.0"] }, + "kind": { "type": "string", "enum": ["model", "algorithm"] }, "id": { "type": "string", "pattern": "^[a-z0-9][a-z0-9-]*$" }, "name": { "type": "string", "minLength": 1 }, "summary": { "type": "string", "minLength": 20 }, @@ -50,6 +51,21 @@ }, "additionalProperties": false }, + "algorithm": { + "type": "object", + "required": ["id", "version", "family", "source", "license", "input", "output", "stateful"], + "properties": { + "id": { "type": "string", "minLength": 1 }, + "version": { "type": "string", "minLength": 1 }, + "family": { "type": "string", "minLength": 1 }, + "source": { "type": "string", "minLength": 1 }, + "license": { "type": "string", "minLength": 1 }, + "input": { "type": "string", "minLength": 1 }, + "output": { "type": "string", "minLength": 1 }, + "stateful": { "type": "boolean" } + }, + "additionalProperties": false + }, "model": { "type": "object", "required": ["id", "version", "format", "parameterCount", "precision", "source", "license", "assets"], @@ -91,7 +107,7 @@ "type": "object", "required": ["timings", "coldStartDefined", "warmRunDefined"], "properties": { - "timings": { "type": "array", "items": { "type": "string", "enum": ["modelDownloadMs", "modelCacheReadMs", "integrityMs", "sessionMs", "decodeMs", "preprocessMs", "inferenceMs", "postprocessMs", "totalMs"] }, "minItems": 5 }, + "timings": { "type": "array", "items": { "type": "string", "enum": ["modelDownloadMs", "modelCacheReadMs", "integrityMs", "sessionMs", "decodeMs", "preprocessMs", "inferenceMs", "postprocessMs", "validationMs", "predictionMs", "associationMs", "updateMs", "totalMs"] }, "minItems": 5 }, "coldStartDefined": { "type": "boolean" }, "warmRunDefined": { "type": "boolean" } }, @@ -146,6 +162,15 @@ "additionalProperties": false } }, + "if": { "properties": { "kind": { "const": "algorithm" } }, "required": ["kind"] }, + "then": { + "properties": { "schemaVersion": { "const": "1.2.0" }, "algorithm": true, "model": false, "cache": false }, + "required": ["algorithm"] + }, + "else": { + "properties": { "model": true, "cache": true, "algorithm": false }, + "required": ["model", "cache"] + }, "$defs": { "example": { "type": "object", diff --git a/standards/v1/templates/demo-checklist.md b/standards/v1/templates/demo-checklist.md index baf8dba..e145daa 100644 --- a/standards/v1/templates/demo-checklist.md +++ b/standards/v1/templates/demo-checklist.md @@ -1,15 +1,19 @@ # Demo Checklist -- [ ] Demo focuses on this SDK/model only. +- [ ] Demo focuses on this SDK/model or algorithm only. - [ ] Initial document language is Chinese (`zh-CN`). - [ ] In-page Chinese/English toggle works without reloading model state. - [ ] Brand bar shows SDK name, package version, GitHub, and Demo link. - [ ] Input, backend/precision selection, run/reset, and disabled/loading states exist. - [ ] Empty preview does not render a broken image. - [ ] Status uses the standard state names and accessible text/icons. -- [ ] Model name, version, bytes, parameter count, precision, format, source, license, and SHA-256 are visible. +- [ ] (仅模型)Model name, version, bytes, parameter count, precision, format, source, license, and SHA-256 are visible. - [ ] Requested backend, actual backend, execution mode, runtime version, and verification matrix are visible. -- [ ] Download, cache read, integrity, session, preprocess, inference, postprocess, and total timings are visible. -- [ ] Current-model cache cleanup and global cache cleanup are user initiated and report results. +- [ ] (仅模型)Download, cache read, integrity, session, preprocess, inference, postprocess, and total timings are visible. +- [ ] (仅模型)Current-model cache cleanup and global cache cleanup are user initiated and report results. - [ ] Local-processing/privacy statement is visible. - [ ] 390px viewport has no horizontal overflow. +- [ ] (仅算法)data-sdk-algorithm-info 展示算法身份、来源、许可、输入输出与状态限制。 +- [ ] (仅算法)保留 data-sdk-runtime-info 和 data-sdk-timing,展示五个算法耗时字段及新实例/复用状态语义。 +- [ ] (仅算法)data-sdk-state-reset 实际触发复位;重播/切换序列/seek 遵循状态契约,切语言不复位。 +- [ ] (仅算法)不展示虚构模型下载或缓存操作;浏览器实测复位及错误状态。 diff --git a/standards/v1/templates/release-checklist.md b/standards/v1/templates/release-checklist.md index 7f0637f..c50533e 100644 --- a/standards/v1/templates/release-checklist.md +++ b/standards/v1/templates/release-checklist.md @@ -3,16 +3,19 @@ - [ ] Chinese README is the default and links to equivalent English README/docs. - [ ] npm package name/version, GitHub repository, and live Demo links work. - [ ] `pnpm verify` (or documented equivalent) passes. -- [ ] CI workflow runs tests, typecheck, lint, build, and model/checksum validation. +- [ ] CI workflow runs tests, typecheck, lint, build, and applicable manifest validation. - [ ] The active default-branch Ruleset requires pull requests, current CI checks, resolved conversations, and blocks deletion and force pushes. - [ ] The active release-tag Ruleset prevents matching published tags from being updated or deleted. - [ ] Ruleset bypass actors are absent or have a documented least-privilege reason. - [ ] GitHub About description, Homepage/Demo URL, and topics are configured. - [ ] Changelog contains the release entry. -- [ ] GitHub Release uses an existing immutable tag and states model source, license, assets, backends, and limitations. -- [ ] Model assets use versioned immutable URLs and SHA-256 checksums. +- [ ] GitHub Release uses an existing immutable tag and states source, license, backends, limitations, and model assets when applicable. +- [ ] (仅模型)Model assets use versioned immutable URLs and SHA-256 checksums. - [ ] Compatibility entries include browser, OS, device, backend, runtime, and test date. - [ ] The live Demo is deployed over HTTPS from protected source by a reproducible workflow with a commit-linked deployment record. - [ ] If GitHub Pages is used, Source is GitHub Actions and the deployment uses the `github-pages` environment, scoped Pages permissions, HTTPS, and concurrency control. - [ ] GitHub API governance evidence records the repository, Ruleset/environment identifiers, observed values, and verification time without credentials. - [ ] `pnpm sdk:check -- --repo . --format table` has no required failures. +- [ ] (仅算法)使用 1.2.0 algorithm 清单,来源与许可已核对,不含 model/cache。 +- [ ] (仅算法)双语文档与发布说明记录输入输出、状态生命周期、复位、实现差异及 cold/warm 性能。 +- [ ] 不适用规则以 skip 保留证据和理由,不能把本地通过称为远程发布成功。 diff --git a/standards/v1/templates/sdk-manifest.algorithm.yaml b/standards/v1/templates/sdk-manifest.algorithm.yaml new file mode 100644 index 0000000..048893f --- /dev/null +++ b/standards/v1/templates/sdk-manifest.algorithm.yaml @@ -0,0 +1,40 @@ +schemaVersion: "1.2.0" +kind: algorithm +id: example-algorithm +name: 浏览器算法示例 +summary: 框架无关的浏览器纯算法,公开输入输出、状态生命周期和独立演示页面。 +package: + name: web-sdk-example-algorithm + version: 0.1.0 +repository: https://github.com/owner/web-sdk-example-algorithm +demo: + url: https://owner.github.io/web-sdk-example-algorithm/ + defaultLanguage: zh-CN + framework: react + localProcessing: true +docs: + zhCN: docs/zh-CN/quick-start.md + en: docs/en/quick-start.md +runtime: + backends: [cpu] + executionModes: [main] + actualBackendReported: true + runtimeVersion: example-algorithm@0.1.0 +algorithm: + id: example-algorithm + version: 0.1.0 + family: 状态估计算法 + source: 独立实现;请替换为论文、代码来源和实现差异的说明或文档路径 + license: Apache-2.0 + input: docs/zh-CN/api.md#输入 + output: docs/zh-CN/api.md#输出 + stateful: true +performance: + timings: [validationMs, predictionMs, associationMs, updateMs, totalMs] + coldStartDefined: true + warmRunDefined: true +examples: + vanilla: { status: available, path: examples/vanilla } + react: { status: available, path: examples/react } +verification: + environments: [] diff --git a/tools/sdk-standard-check/algorithm.test.ts b/tools/sdk-standard-check/algorithm.test.ts new file mode 100644 index 0000000..5311978 --- /dev/null +++ b/tools/sdk-standard-check/algorithm.test.ts @@ -0,0 +1,98 @@ +import { afterEach, describe, expect, it } from "vitest"; +import { cpSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; +import path from "node:path"; +import YAML from "yaml"; +import { validateManifest } from "./src/manifest.mjs"; +import { scanRepository } from "./src/check.mjs"; + +const complete = "tools/sdk-standard-check/fixtures/complete-sdk"; +const model = () => YAML.parse(readFileSync(`${complete}/sdk-manifest.yaml`, "utf8")); +const algorithm = () => { + const value = model(); + value.schemaVersion = "1.2.0"; + value.kind = "algorithm"; + delete value.model; + delete value.cache; + value.algorithm = { + id: "tracking", version: "0.1.0", family: "multi-object-tracking", + source: "独立实现;论文与差异见 NOTICE", license: "Apache-2.0", + input: "docs/zh-CN/api.md#tracking-frame", output: "docs/zh-CN/api.md#tracking-result", stateful: true, + }; + value.runtime = { backends: ["cpu"], executionModes: ["main"], actualBackendReported: true, runtimeVersion: "tracking@0.1.0" }; + value.performance.timings = ["validationMs", "predictionMs", "associationMs", "updateMs", "totalMs"]; + return value; +}; +const demo = '
'; +const roots: string[] = []; +function repository(value: ReturnType, html = demo) { + const root = mkdtempSync(path.join(tmpdir(), "sdk-algorithm-")); + roots.push(root); + cpSync(complete, root, { recursive: true }); + writeFileSync(path.join(root, "sdk-manifest.yaml"), YAML.stringify(value)); + writeFileSync(path.join(root, "apps/demo/index.html"), html); + return root; +} +afterEach(() => { + for (const root of roots.splice(0)) { + expect(path.dirname(path.resolve(root))).toBe(path.resolve(tmpdir())); + expect(path.basename(root)).toMatch(/^sdk-algorithm-/); + rmSync(root, { recursive: true, force: true }); + } +}); + +describe("纯算法 SDK 契约", () => { + it("合法算法无模型与缓存也通过,模型专属规则有跳过理由和证据", async () => { + const report = await scanRepository(repository(algorithm())); + expect(report.findings.filter((x: { level: string; status: string }) => x.level === "required" && x.status === "fail")).toEqual([]); + for (const id of ["DEMO-004", "MODEL-001", "CACHE-001"]) { + expect(report.findings).toContainEqual(expect.objectContaining({ id, status: "skip", path: "sdk-manifest.yaml", evidence: expect.stringContaining("algorithm") })); + } + for (const id of ["ALGORITHM-001", "DEMO-005", "DEMO-006", "PERF-001"]) { + expect(report.findings).toContainEqual(expect.objectContaining({ id, status: "pass" })); + } + }); + + it.each(["id", "version", "family", "source", "license", "input", "output", "stateful"])("拒绝缺少算法字段 %s", (field) => { + const value = algorithm(); + delete value.algorithm[field]; + expect(validateManifest(value)).toContainEqual(expect.stringContaining(`/algorithm/${field}`)); + }); + + it.each(["缺少 algorithm", "携带 model", "携带 cache", "旧版 1.0.0", "旧版 1.1.0", "伪造 kind", "无效 stateful", "缺少算法 timing"])("%s 不得获得模型豁免", async (scenario) => { + const value = algorithm(); + if (scenario === "缺少 algorithm") delete value.algorithm; + if (scenario === "携带 model") value.model = model().model; + if (scenario === "携带 cache") value.cache = model().cache; + if (scenario.startsWith("旧版")) value.schemaVersion = scenario.slice(3); + if (scenario === "伪造 kind") value.kind = "Algorithm"; + if (scenario === "无效 stateful") value.algorithm.stateful = "true"; + if (scenario === "缺少算法 timing") value.performance.timings = ["validationMs", "predictionMs", "associationMs", "totalMs", "totalMs"]; + const report = await scanRepository(repository(value)); + expect(report.findings).toContainEqual(expect.objectContaining({ id: "CONFIG-001", status: "fail" })); + for (const id of ["MODEL-001", "CACHE-001", "ALGORITHM-001"]) { + expect(report.findings).toContainEqual(expect.objectContaining({ id, status: "fail" })); + } + }); + + it.each(["data-sdk-state-reset", "data-sdk-algorithm-info", "data-sdk-runtime-info", "data-sdk-timing"])("缺少 %s 标记会失败,说明文字不能代替标记", async (marker) => { + const report = await scanRepository(repository(algorithm(), demo.replace(marker, "data-unrelated") + "validationMs totalMs 耗时")); + expect(report.findings).toContainEqual(expect.objectContaining({ id: marker === "data-sdk-state-reset" ? "DEMO-006" : "DEMO-005", status: "fail" })); + }); + + it.each(["1.0.0", "1.1.0", "1.2.0"])("%s 模型保留 assets 必填及算法分支互斥", (schemaVersion) => { + const value = { ...model(), schemaVersion, kind: "model" }; + expect(validateManifest(value)).toEqual([]); + value.algorithm = algorithm().algorithm; + expect(validateManifest(value).length).toBeGreaterThan(0); + delete value.algorithm; + delete value.model.assets; + expect(validateManifest(value)).toContainEqual(expect.stringContaining("/model/assets")); + }); + + it("算法脚手架模板通过完整清单校验", () => { + const value = YAML.parse(readFileSync("standards/v1/templates/sdk-manifest.algorithm.yaml", "utf8")); + expect(value.kind).toBe("algorithm"); + expect(validateManifest(value)).toEqual([]); + }); +}); diff --git a/tools/sdk-standard-check/checker.test.ts b/tools/sdk-standard-check/checker.test.ts index 377b1c6..b4c689a 100644 --- a/tools/sdk-standard-check/checker.test.ts +++ b/tools/sdk-standard-check/checker.test.ts @@ -124,7 +124,7 @@ describe("SDK repository discovery", () => { const summary = summarize(report); const remoteFindings = report.findings.filter((finding: { id: string }) => /^(GOV|DEPLOY|PAGES)-/.test(finding.id)); - expect(report.standardVersion).toBe("1.1.0"); + expect(report.standardVersion).toBe("1.2.0"); expect(remoteFindings.map((finding: { id: string }) => finding.id)).toEqual([ "GOV-001", "GOV-002", @@ -132,7 +132,7 @@ describe("SDK repository discovery", () => { "PAGES-001", ]); expect(remoteFindings.every((finding: { status: string }) => finding.status === "skip")).toBe(true); - expect(summary.requiredSkipped).toBe(4); + expect(summary.requiredSkipped).toBe(6); expect(summary.status).toBe("locally-compliant"); }); diff --git a/tools/sdk-standard-check/src/discover.mjs b/tools/sdk-standard-check/src/discover.mjs index 41098c4..7299c95 100644 --- a/tools/sdk-standard-check/src/discover.mjs +++ b/tools/sdk-standard-check/src/discover.mjs @@ -29,6 +29,9 @@ export async function discoverRepository(root, manifest) { demoLanguageToggle: false, cacheClear: false, timingMarkers: false, + algorithmTimingMarkers: false, + demoStateReset: false, + algorithmInformation: false, modelInformation: false, runtimeInformation: false, performanceTimings: false, @@ -48,6 +51,7 @@ export async function discoverRepository(root, manifest) { if (files.some((file) => file.startsWith("docs/zh-CN/"))) { evidence.locales.push("zh-CN"); addEvidence(evidence, "docs.zhCN", files.find((file) => file.startsWith("docs/zh-CN/"))); } if (files.some((file) => file.startsWith("docs/en/"))) { evidence.locales.push("en"); addEvidence(evidence, "docs.en", files.find((file) => file.startsWith("docs/en/"))); } + if (manifest.path) addEvidence(evidence, "manifestPresent", manifest.path); const demoFile = files.find((file) => /^apps\/demo\/(?:index\.html|src\/main\.[jt]sx?|package\.json)$/i.test(file)) ?? files.find((file) => /^demo\/index\.html$/i.test(file)); evidence.demoEntry = Boolean(demoFile); if (demoFile) addEvidence(evidence, "demoEntry", demoFile); @@ -56,6 +60,21 @@ export async function discoverRepository(root, manifest) { if (/English|中文|language|语言|i18n/i.test(text)) { evidence.demoLanguageToggle = true; addEvidence(evidence, "demoLanguageToggle", file); } if (/data-sdk-cache-clear|clear(?:Model)?Cache|清理缓存/i.test(text)) { evidence.cacheClear = true; addEvidence(evidence, "cacheClear", file); } if (/data-sdk-(?:timing|model-info|runtime-info)|modelDownloadMs|inferenceMs|耗时/i.test(text)) { evidence.timingMarkers = true; addEvidence(evidence, "timingMarkers", file); } + if (/data-sdk-state-reset\b/.test(text)) { evidence.demoStateReset = true; addEvidence(evidence, "demoStateReset", file); } + } + + // 算法必须具备三个独立区域;允许不同组件文件分别提供标记。 + const algorithmMarkers = ["data-sdk-algorithm-info", "data-sdk-runtime-info", "data-sdk-timing"]; + const demoTexts = allText.filter(({ file }) => isDemoFile(file)); + evidence.algorithmTimingMarkers = algorithmMarkers.every((marker) => demoTexts.some(({ text }) => new RegExp(`${marker}\\b`).test(text))); + if (evidence.algorithmTimingMarkers) { + for (const { file, text } of demoTexts) { + if (algorithmMarkers.some((marker) => new RegExp(`${marker}\\b`).test(text))) addEvidence(evidence, "demoTimingMarkers", file); + } + } + if (!manifest.errors.length && manifest.value?.kind === "algorithm") { + evidence.algorithmInformation = true; + addEvidence(evidence, "algorithmInformation", manifest.path); } if (manifest.value?.model?.assets?.length || /model(?:\s|_|-)info|precision|sha256|参数量|精度/i.test(joined)) { evidence.modelInformation = true; addEvidence(evidence, "modelInformation", manifest.path ?? markdown[0] ?? "README.md"); } diff --git a/tools/sdk-standard-check/src/manifest.mjs b/tools/sdk-standard-check/src/manifest.mjs index 3d8b6da..7b53dec 100644 --- a/tools/sdk-standard-check/src/manifest.mjs +++ b/tools/sdk-standard-check/src/manifest.mjs @@ -82,19 +82,22 @@ export function validateManifest(value, options = {}) { // 完整 schema 确认类型后,保留既有契约的 HTTP、必需耗时和缓存能力检查。 const errors = []; if (!isHttpUrlWithHost(value.repository)) errors.push("/repository:必须是含主机的 HTTP(S) 地址"); - for (const [index, asset] of value.model.assets.entries()) { + for (const [index, asset] of (value.model?.assets ?? []).entries()) { if (!isHttpUrlWithHost(asset.url)) errors.push(`/model/assets/${index}/url:必须是含主机的 HTTP(S) 地址`); } - for (const [variantIndex, variant] of (value.model.variants ?? []).entries()) { + for (const [variantIndex, variant] of (value.model?.variants ?? []).entries()) { for (const [sourceIndex, source] of variant.sources.entries()) { if (!isHttpUrlWithHost(source.downloadUrl)) errors.push(`/model/variants/${variantIndex}/sources/${sourceIndex}/downloadUrl:必须是含主机的 HTTP(S) 地址`); } } const timingSet = new Set(value.performance.timings); - for (const field of ["modelDownloadMs", "modelCacheReadMs", "integrityMs", "sessionMs", "inferenceMs", "totalMs"]) { + const requiredTimings = value.kind === "algorithm" + ? ["validationMs", "predictionMs", "associationMs", "updateMs", "totalMs"] + : ["modelDownloadMs", "modelCacheReadMs", "integrityMs", "sessionMs", "inferenceMs", "totalMs"]; + for (const field of requiredTimings) { if (!timingSet.has(field)) errors.push(`/performance/timings:缺少 ${field}`); } - for (const field of ["versionedKeys", "clearCurrent", "clearAll", "estimate"]) { + for (const field of value.kind === "algorithm" ? [] : ["versionedKeys", "clearCurrent", "clearAll", "estimate"]) { if (value.cache[field] !== true) errors.push(`/cache/${field}:必须声明为 true`); } return errors; diff --git a/tools/sdk-standard-check/src/rules.mjs b/tools/sdk-standard-check/src/rules.mjs index 05a08e7..dac5bbc 100644 --- a/tools/sdk-standard-check/src/rules.mjs +++ b/tools/sdk-standard-check/src/rules.mjs @@ -29,7 +29,9 @@ const detectors = { demoChineseDefault: (evidence) => evidence.demoChineseDefault, demoLanguageToggle: (evidence) => evidence.demoLanguageToggle, demoCacheClear: (evidence) => evidence.cacheClear, - demoTimingMarkers: (evidence) => evidence.timingMarkers, + demoTimingMarkers: (evidence, manifest) => manifest?.kind === "algorithm" ? evidence.algorithmTimingMarkers : evidence.timingMarkers, + demoStateReset: (evidence) => evidence.demoStateReset, + algorithmInformation: (evidence, manifest) => Boolean(manifest?.algorithm) && evidence.algorithmInformation, modelInformation: (evidence, manifest) => Boolean(manifest?.model?.assets?.length) && evidence.modelInformation, runtimeInformation: (evidence, manifest) => Boolean(manifest?.runtime?.actualBackendReported) && evidence.runtimeInformation, performanceTimings: (evidence, manifest) => Boolean(manifest?.performance?.timings?.length) && evidence.performanceTimings, @@ -53,6 +55,13 @@ const detectors = { export async function evaluateRules(evidence, manifest, standardRoot = defaultStandardRoot, loadedRuleSet) { const rules = (loadedRuleSet ?? await loadRuleSet(standardRoot)).rules; return rules.map((rule) => { + // 仅经完整校验的清单可决定类型豁免;无清单的旧仓库沿用模型检查。 + const kind = manifest ? (manifest.kind ?? "model") : evidence.manifestDeclared ? null : "model"; + if (kind && rule.appliesTo && !rule.appliesTo.includes(kind)) { + return result(rule.id, rule.level, "skip", rule.message, rule.remediation, + manifest ? evidence.evidenceByKey["manifestPresent"]?.[0] : undefined, + `当前类型为 ${kind},本规则仅适用于 ${rule.appliesTo.join("、")};${manifest ? "清单已通过完整校验" : "未声明清单,沿用旧版模型检查,不豁免模型规则"}。`); + } if (["github-api", "remote-api"].includes(rule.verification)) { return result( rule.id, diff --git a/tools/sdk-standard-check/src/types.mjs b/tools/sdk-standard-check/src/types.mjs index 8d72937..721cc7b 100644 --- a/tools/sdk-standard-check/src/types.mjs +++ b/tools/sdk-standard-check/src/types.mjs @@ -24,6 +24,9 @@ * @property {boolean} demoLanguageToggle * @property {boolean} cacheClear * @property {boolean} timingMarkers + * @property {boolean} algorithmTimingMarkers + * @property {boolean} demoStateReset + * @property {boolean} algorithmInformation * @property {boolean} modelInformation * @property {boolean} runtimeInformation * @property {boolean} performanceTimings diff --git a/tools/sdk-standard-check/standard-files.test.ts b/tools/sdk-standard-check/standard-files.test.ts index 8565acd..00ec69c 100644 --- a/tools/sdk-standard-check/standard-files.test.ts +++ b/tools/sdk-standard-check/standard-files.test.ts @@ -34,12 +34,12 @@ describe("v1 standard source", () => { for (const file of requiredFiles) expect(existsSync(file), file).toBe(true); }); - it("declares v1.1 rules while accepting v1.0 manifests", () => { + it("声明 v1.2 规则并兼容旧模型清单", () => { const rules = YAML.parse(readFileSync("standards/v1/rules.yaml", "utf8")); const schema = JSON.parse(readFileSync("standards/v1/sdk-manifest.schema.json", "utf8")); - expect(rules.standardVersion).toBe("1.1.0"); + expect(rules.standardVersion).toBe("1.2.0"); expect(schema.$id).toContain("web-model-sdk-standard/v1"); - expect(schema.properties.schemaVersion.enum).toEqual(["1.0.0", "1.1.0"]); + expect(schema.properties.schemaVersion.enum).toEqual(["1.0.0", "1.1.0", "1.2.0"]); }); it("defines GitHub Rulesets and Pages as remotely verified governance rules", () => { From 2db5abc15a0afcfa49f29181359bd4fd2b8eed47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E9=BB=98=E6=B6=B5?= <21739308@qq.com> Date: Sat, 19 Sep 2026 08:52:59 +0800 Subject: [PATCH 03/11] =?UTF-8?q?=E4=BF=9D=E5=AD=98=E8=B7=9F=E8=B8=AASDK?= =?UTF-8?q?=E5=AE=9E=E6=96=BD=E5=89=8D=E6=A3=80=E6=9F=A5=E4=B8=8E=E9=AA=8C?= =?UTF-8?q?=E6=94=B6=E8=8C=83=E5=9B=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2026-09-19-pp-tracking-foundation.md | 3 +- reports/sdk-standard/pp-tracking-before.json | 225 ++++++++++++++++++ 2 files changed, 227 insertions(+), 1 deletion(-) create mode 100644 reports/sdk-standard/pp-tracking-before.json diff --git a/docs/superpowers/plans/2026-09-19-pp-tracking-foundation.md b/docs/superpowers/plans/2026-09-19-pp-tracking-foundation.md index cb78ed5..856eecd 100644 --- a/docs/superpowers/plans/2026-09-19-pp-tracking-foundation.md +++ b/docs/superpowers/plans/2026-09-19-pp-tracking-foundation.md @@ -68,11 +68,12 @@ a.reset(); expect(a.update(frame).generation).toBe(1); ### Task 4: 桌面验收证据与交付复核 -**Files:** SDK scripts/evaluation/*、reports/2026-09-19-desktop/*、docs兼容/性能指南、manifest.verification;门户reports/sdk-standard/pp-tracking-after.json与计划验收记录。 +**Files:** SDK scripts/evaluation/*、reports/2026-09-19-desktop/*、docs兼容/性能指南、manifest.verification;门户reports/sdk-standard/pp-tracking-after.json与计划验收记录,以及docs/superpowers/plans/2026-09-13-pp-detection-multi-model-roadmap.md、2026-08-17-web-model-sdk-portal-roadmap.md中的当前跟踪进度。 **Interfaces:** 消费已完成SDK,不能用静态声明替代运行;证据区分机制正确性和浏览器性能,无移动端/MOT精度承诺。 - [ ] 可复现合成序列性能测量至少10、50、100框,注明浏览器/OS/CPU/日期及样本数、p50/p95;包含cold创建与warm更新,固定输入可重跑,无绝对性能断言。 - [ ] 记录真实浏览器主线程运行、两种语言、390px、Vanilla及导入导出,保留机器可读摘要和命令。截图放忽略目录,报告只列路径。 - [ ] 校验npm pack、无runtime依赖和源码来源;运行全部SDK tests/typechecks/builds/browser与门户sdk:check/tests/build。 - [ ] 更新verification只填实际测试的浏览器版本和设备,sdk:check远程skip如实保留;记录发布尚未执行及真实视频质量未测。 +- [ ] 同步两份总路线的当前状态为实际完成的本地SDK/桌面证据,保留旧阶段历史说明,不把尚未发布的Tracking登记为可用门户条目;归档主代理的7个现有SDK新旧标准检查差异报告。 - [ ] 提交证据和兼容说明,独立整分支审查后交付本地Demo及可审查代码。 diff --git a/reports/sdk-standard/pp-tracking-before.json b/reports/sdk-standard/pp-tracking-before.json new file mode 100644 index 0000000..0247385 --- /dev/null +++ b/reports/sdk-standard/pp-tracking-before.json @@ -0,0 +1,225 @@ +{ + "standardVersion": "1.2.0", + "repositories": [ + { + "standardVersion": "1.2.0", + "repository": "web-sdk-PP-Tracking", + "summary": { + "requiredPassed": 0, + "requiredFailed": 18, + "requiredSkipped": 6, + "requiredUnknown": 0, + "requiredNotApplicable": 0, + "recommendedPassed": 0, + "recommendedFailed": 3, + "labs": 1, + "status": "partial" + }, + "findings": [ + { + "id": "ALGORITHM-001", + "level": "required", + "status": "skip", + "message": "算法清单声明身份、家族、来源、许可、输入输出与有状态语义", + "remediation": "按 sdk-manifest.algorithm.yaml 填写 algorithm 字段并记录状态生命周期。", + "evidence": "当前类型为 model,本规则仅适用于 algorithm;未声明清单,沿用旧版模型检查,不豁免模型规则。" + }, + { + "id": "CACHE-001", + "level": "required", + "status": "fail", + "message": "Cache storage, versioning, estimate, and cleanup are declared", + "remediation": "Add cache contract fields and expose cleanup controls in the Demo." + }, + { + "id": "DEMO-001", + "level": "required", + "status": "fail", + "message": "A runnable standalone Demo entry exists", + "remediation": "Add a Demo app with a documented start/build command." + }, + { + "id": "DEMO-002", + "level": "required", + "status": "fail", + "message": "Demo declares Chinese as its initial language", + "remediation": "Set lang or default language state to zh-CN." + }, + { + "id": "DEMO-003", + "level": "required", + "status": "fail", + "message": "Demo provides an in-page Chinese/English toggle", + "remediation": "Add a language toggle that does not alter inference state." + }, + { + "id": "DEMO-004", + "level": "required", + "status": "fail", + "message": "Demo exposes current-model and global cache cleanup controls", + "remediation": "Wire clear-current and clear-all actions to the SDK cache API." + }, + { + "id": "DEMO-005", + "level": "required", + "status": "fail", + "message": "Demo 展示当前模型或算法的信息、运行信息和耗时", + "remediation": "模型使用 data-sdk-model-info,算法使用 data-sdk-algorithm-info;两者保留 data-sdk-runtime-info 和 data-sdk-timing。" + }, + { + "id": "DEMO-006", + "level": "required", + "status": "skip", + "message": "算法 Demo 提供状态复位操作", + "remediation": "使用 data-sdk-state-reset 标记复位控件,并按状态生命周期契约连接 reset。", + "evidence": "当前类型为 model,本规则仅适用于 algorithm;未声明清单,沿用旧版模型检查,不豁免模型规则。" + }, + { + "id": "DEPLOY-001", + "level": "required", + "status": "skip", + "message": "The live Demo has a reproducible HTTPS deployment from protected source with least-privilege credentials", + "remediation": "Configure and verify a deployment that meets standards/v1/repository-governance-contract.md, then record its commit and deployment evidence.", + "evidence": "Requires read-only remote-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "DOC-001", + "level": "required", + "status": "fail", + "message": "Chinese-default and English README files link to one another", + "remediation": "Add README.md and README.en.md with reciprocal language links." + }, + { + "id": "DOC-002", + "level": "required", + "status": "fail", + "message": "Chinese and English documentation directories are present", + "remediation": "Provide equivalent docs/zh-CN and docs/en quick-start/API guidance." + }, + { + "id": "DOC-003", + "level": "required", + "status": "fail", + "message": "README or package metadata exposes GitHub, npm, and live Demo links", + "remediation": "Add repository, package, and Demo URLs to README and manifest." + }, + { + "id": "EXAMPLE-001", + "level": "required", + "status": "fail", + "message": "Vanilla TypeScript/DOM portability example exists", + "remediation": "Add examples/vanilla with a runnable README." + }, + { + "id": "EXAMPLE-002", + "level": "required", + "status": "fail", + "message": "React reference example exists", + "remediation": "Add examples/react or explicitly record a migration exception." + }, + { + "id": "EXAMPLE-003", + "level": "recommended", + "status": "fail", + "message": "Declared target surfaces have runnable examples or explicit unsupported status", + "remediation": "Align examples with manifest target surfaces." + }, + { + "id": "GOV-001", + "level": "required", + "status": "skip", + "message": "The default branch has an active least-privilege Ruleset for pull requests, current CI checks, resolved conversations, deletion, and force-push protection", + "remediation": "Configure an active default-branch Ruleset using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "GOV-002", + "level": "required", + "status": "skip", + "message": "Release tags are immutable under an active tag Ruleset", + "remediation": "Protect the documented release tag pattern against updates and deletion, minimize bypass, and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "LABS-001", + "level": "labs", + "status": "skip", + "message": "Experimental backends and NPU claims have explicit evidence and limits", + "remediation": "Mark WebNN/NPU as labs unless a dated verification matrix exists.", + "evidence": "Labs rule is informational" + }, + { + "id": "META-001", + "level": "required", + "status": "fail", + "message": "SDK manifest is present and declares the v1 contract", + "remediation": "Copy standards/v1/templates/sdk-manifest.yaml and fill every required field." + }, + { + "id": "META-002", + "level": "required", + "status": "fail", + "message": "package.json declares a public package identity and version", + "remediation": "Add a non-private package with a semver version and repository metadata." + }, + { + "id": "MODEL-001", + "level": "required", + "status": "fail", + "message": "Manifest declares model identity, precision, size, source, license, and checksum", + "remediation": "Complete the model and asset fields in sdk-manifest.yaml." + }, + { + "id": "PAGES-001", + "level": "required", + "status": "skip", + "message": "A GitHub Pages Demo deploys through GitHub Actions with scoped permissions, environment binding, HTTPS, and concurrency control", + "remediation": "Configure Pages and its deployment workflow using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "PERF-001", + "level": "required", + "status": "fail", + "message": "声明当前 SDK 类型所需的标准耗时字段", + "remediation": "模型保留下载、缓存、校验、会话、推理和总耗时;算法声明 validationMs、predictionMs、associationMs、updateMs、totalMs。" + }, + { + "id": "RELEASE-001", + "level": "required", + "status": "fail", + "message": "CI workflow runs verification before release", + "remediation": "Add .github/workflows/ci.yml with tests, typecheck, and build." + }, + { + "id": "RELEASE-002", + "level": "required", + "status": "fail", + "message": "GitHub Release workflow exists", + "remediation": "Add a tag-triggered release workflow and publish notes." + }, + { + "id": "RELEASE-003", + "level": "recommended", + "status": "fail", + "message": "Changelog documents user-visible SDK and Demo changes", + "remediation": "Add CHANGELOG.md with the current release entry." + }, + { + "id": "RUNTIME-001", + "level": "required", + "status": "fail", + "message": "Requested/actual backend and execution mode are declared", + "remediation": "Add runtime backends, execution modes, and actual backend reporting." + }, + { + "id": "UI-001", + "level": "recommended", + "status": "fail", + "message": "Demo styling references the shared token vocabulary", + "remediation": "Import or copy standards/v1/ui-tokens.json values without introducing conflicting status colors." + } + ] + } + ] +} From 832625e9dc218d590a91fb5966c6e99d417bfe8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E9=BB=98=E6=B6=B5?= <21739308@qq.com> Date: Sat, 19 Sep 2026 09:55:12 +0800 Subject: [PATCH 04/11] =?UTF-8?q?=E5=BD=92=E6=A1=A3=E8=B7=9F=E8=B8=AA?= =?UTF-8?q?=E6=A0=87=E5=87=86=E5=9B=9E=E5=BD=92=E5=B9=B6=E5=90=8C=E6=AD=A5?= =?UTF-8?q?=E7=8B=AC=E7=AB=8BSDK=E6=9C=AC=E5=9C=B0=E9=AA=8C=E6=94=B6?= =?UTF-8?q?=E8=B7=AF=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitattributes | 3 + ...2026-08-17-web-model-sdk-portal-roadmap.md | 4 +- ...-09-13-pp-detection-multi-model-roadmap.md | 4 +- reports/sdk-standard/pp-tracking-after.json | 269 ++ .../tracking/2026-09-19-foundation/README.md | 42 + .../2026-09-19-foundation/portal-build.log | 82 + .../portal-task-4-build.log | 80 + .../2026-09-19-foundation/portal-test.log | 31 + .../portal-validation-summary.json | 18 + .../tracking-standard-regression.mjs | 25 + .../standard-regression.json | 3414 +++++++++++++++++ .../verify-standard-regression.mjs | 72 + 12 files changed, 4040 insertions(+), 4 deletions(-) create mode 100644 reports/sdk-standard/pp-tracking-after.json create mode 100644 reports/tracking/2026-09-19-foundation/README.md create mode 100644 reports/tracking/2026-09-19-foundation/portal-build.log create mode 100644 reports/tracking/2026-09-19-foundation/portal-task-4-build.log create mode 100644 reports/tracking/2026-09-19-foundation/portal-test.log create mode 100644 reports/tracking/2026-09-19-foundation/portal-validation-summary.json create mode 100644 reports/tracking/2026-09-19-foundation/provenance/tracking-standard-regression.mjs create mode 100644 reports/tracking/2026-09-19-foundation/standard-regression.json create mode 100644 reports/tracking/2026-09-19-foundation/verify-standard-regression.mjs diff --git a/.gitattributes b/.gitattributes index 6a613a7..1bad34c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -6,3 +6,6 @@ reports/sdk-standard/2026-09-15-detection-selection/*.log -text -whitespace src/data/pp-detection-comparison.json -text # 实验归档保留原始 CRLF 和文件尾空行,校验其他行尾空格。 reports/rotated-detection/2026-09-18-feasibility/** -text whitespace=blank-at-eol,space-before-tab,cr-at-eol +reports/tracking/2026-09-19-foundation/** -text whitespace=blank-at-eol,space-before-tab,cr-at-eol +# 原始构建/测试日志保留工具输出的行尾空格和尾部空行。 +reports/tracking/2026-09-19-foundation/*.log -text -whitespace diff --git a/docs/superpowers/plans/2026-08-17-web-model-sdk-portal-roadmap.md b/docs/superpowers/plans/2026-08-17-web-model-sdk-portal-roadmap.md index b3a73e8..79952f0 100644 --- a/docs/superpowers/plans/2026-08-17-web-model-sdk-portal-roadmap.md +++ b/docs/superpowers/plans/2026-08-17-web-model-sdk-portal-roadmap.md @@ -14,7 +14,7 @@ readiness, not by the number of models mentioned in the catalog. TinyPose 0.3.0、PP-Segmentation 0.1.0 与 PP-RotatedDetection 0.1.0 已正式发布。分割以 PP-YOLOE_seg_s 640 FP32 提供单帧图片、原图框与二值 ROI 掩码;旋转框以 PP-YOLOE-R-s 1024 FP32 提供 DOTA 15 类和原图四点框。两者均完成双源、npm、Release 和 HTTPS Demo 回读;旋转框固定发布证据见[独立 SDK 回执](https://github.com/chenmohan123/web-sdk-PP-RotatedDetection/tree/b54ae15ca124fd111cac6e683409fdbb88a14e13/reports/2026-09-18-release)。门户已登记第七个 SDK,并保留各自桌面验证边界。 -旋转框首版已按可行性结论发布,当前不扩展切片、媒体、手机或 NPU 承诺。多目标跟踪已完成[固定参考可行性评估](../../../reports/tracking/2026-09-18-feasibility/README.md):五配置60组合成实跑和多实例状态诊断,首发候选为 ByteTrack 算法机制。低分恢复有效,同时保留交叉掉头身份交换、跨类复用、全局 ID 和时间间隔边界;根 MIT/Apache 与基础来源 GPL 的追溯差异尚需处置。下一阶段先完成来源/许可、无权重算法标准扩展和独立 API/实现设计,再验证真实序列与桌面浏览器。本轮没有建立生产 SDK、发布 npm 或分发上游代码,不把合成结果当作 MOT 精度或浏览器性能。分割精度变体和媒体扩展留待后续,Workflow 继续暂缓。 +旋转框首版已按可行性结论发布,当前不扩展切片、媒体、手机或 NPU 承诺。多目标跟踪在[前期固定参考评估](../../../reports/tracking/2026-09-18-feasibility/README.md)后,已完成纯算法标准1.2.0和独立 `web-sdk-pp-tracking@0.1.0` 本地首版:ByteTrack高低分机制、数学定义独立实现、Apache-2.0来源说明、实例状态、CPU/main API、双语Demo及Vanilla/React示例。前期参考代码的许可疑点仍在历史报告中;本产品不复制或分发这些代码。29单测、9组Chromium153交互与实际包通过;Chromium151的10/50/100框各600次warm样本和129次独立API更新已归档,[7SDK新旧标准回归](../../../reports/tracking/2026-09-19-foundation/README.md)既有规则状态无变化。当前仅本地交付、待整分支独立审查,未发布npm/Release/在线Demo,不登记第8个在线条目;真实视频MOT精度、手机和远程治理尚未验证。分割精度变体和媒体扩展留待后续,Workflow继续暂缓。 ## Current Baseline @@ -155,4 +155,4 @@ APIs remain separate Labs work and are not prerequisites for the MVP. ## Next Concrete Task -PP-Segmentation 与 PP-RotatedDetection 门户登记均已上线。跟踪固定参考评估已完成,下一阶段以 ByteTrack 算法机制为候选,处理来源/许可与无权重标准,设计独立 API、实例状态和时间策略后再进入 TS/真实序列/桌面浏览器验证;当前无跟踪生产 SDK。上方各 Phase 为历史总体路线;Workflow 实施仍暂缓,不因新增目录项或跟踪评估而启动。 +PP-Segmentation 与 PP-RotatedDetection 门户登记均已上线。跟踪独立本地SDK、算法标准和桌面合成验收已完成;下一步独立整分支审查、本地Demo交付,再按明确范围安排发布和真实授权视频质量验证。当前没有已发布的跟踪npm/在线Demo,不进入可用门户目录;390px仅为桌面布局证据。上方各Phase保留为历史总体路线;Workflow实施仍暂缓。 diff --git a/docs/superpowers/plans/2026-09-13-pp-detection-multi-model-roadmap.md b/docs/superpowers/plans/2026-09-13-pp-detection-multi-model-roadmap.md index d5dbe29..87c87c2 100644 --- a/docs/superpowers/plans/2026-09-13-pp-detection-multi-model-roadmap.md +++ b/docs/superpowers/plans/2026-09-13-pp-detection-multi-model-roadmap.md @@ -18,7 +18,7 @@ - **TinyPose:0.3.0 已发布。** 三个稳定变体,单帧/人体框 API,独立 Demo 支持图片、本地视频和摄像头。物理摄像头、手机及 NPU 的验证边界保持不变。 - **PP-Segmentation:0.1.0 已发布。** PP-YOLOE_seg_s 640 FP32,单帧 Blob/RGBA、原图框和独立二值 ROI 掩码。64图四模式严格质量验收通过,最小掩码IoU为0.9987084870848708;双源八组合、npm、GitHub Release和HTTPS Demo均已核验。原始官方裁边口径失败记录保留,最终采用原图整数尺寸独立参考。[固定发布记录](https://github.com/chenmohan123/web-sdk-PP-Segmentation/tree/89b350d30305ecbc275780e455d1c115a250570f/reports/2026-09-18-release-readiness)包含许可、双源、质量和交付回执。Trusted Publishing已配置,实际OIDC发布待下一次新版本验证。门户登记为第六个SDK。 - **PP-RotatedDetection:0.1.0 已发布。** PP-YOLOE-R-s 1024 FP32,单帧 Blob/RGBA、DOTA 15 类和原图四点框。双源 × WASM/WebGPU × 主线程/Worker、npm、GitHub Release 与 HTTPS Demo 均已核验;[固定发布记录](https://github.com/chenmohan123/web-sdk-PP-RotatedDetection/tree/b54ae15ca124fd111cac6e683409fdbb88a14e13/reports/2026-09-18-release)保留模型身份、数值和交付回执。门户登记为第七个 SDK;大图切片、媒体、手机与 NPU 不在当前验证范围。 -- **多目标跟踪:固定参考可行性评估完成,生产前置待办。** [60组合成实跑证据](../../../reports/tracking/2026-09-18-feasibility/README.md)覆盖官方 ByteTrack、OC-SORT 及 Paddle 两类跟踪实现;首发候选为 ByteTrack 算法机制。低分关联、短遮挡恢复已复现,同时保留掉头交叉身份交换、跨类 ID 复用、全局计数碰撞/去重及时间间隔边界。根 MIT/Apache 与基础来源 GPL 的追溯差异尚需处置;先完成来源/许可、无权重算法标准扩展与独立 API 设计,再进入 TS 和真实序列/桌面浏览器验证。本轮未建立 SDK、发布 npm 或上游代码,合成结果不是 MOT 基准,Workflow 继续暂缓。 +- **多目标跟踪:独立SDK本地首版与桌面证据已完成,尚未发布。** [前期60组合成评估](../../../reports/tracking/2026-09-18-feasibility/README.md)保留参考实现的许可追溯疑点和身份交换等限制。现采用ByteTrack高低分关联机制,依据数学定义独立实现Kalman、全局分配、实例状态与时间策略,以Apache-2.0提供本地 `web-sdk-pp-tracking@0.1.0`,不复制或分发旧参考代码。纯算法标准1.2.0、框架无关CPU/main API、双语Demo及Vanilla/React示例已完成;29单测、9组Chromium153产品交互和实际包通过,Chromium151的10/50/100框各600次warm性能样本已归档。[本地验收与7SDK标准回归](../../../reports/tracking/2026-09-19-foundation/README.md)保留提交、哈希和边界。尚未发布npm/Release/在线Demo,不登记第8个在线SDK;真实授权视频质量、移动端和远程治理另行验证,Workflow继续暂缓。 ### 已完成:旋转框独立 SDK 可行性 @@ -32,7 +32,7 @@ PP-YOLOE-R-s 单尺度 FP32 已建立独立 SDK,完成四点坐标、原图还原、Blob/RGBA、浏览器预处理、显式 CPU/GPU、Worker、取消/释放、缓存、完整性校验、双源分发与正式发布。极端长宽比、平分排序与大图切片边界仍需另行验证;未经验证的能力不进入当前承诺。 -旋转框任务保持独立,不加入现有轴对齐 Detection API。分割 FP16/量化和媒体能力、HRNet 等姿态扩展保留后续;跟踪依据上述证据进入来源/许可、无权重标准与独立实现设计阶段,真实视频和浏览器尚未验证。继续桌面优先、模型 SDK 的 ModelScope 默认并保留 Hugging Face,以及独立 Demo 统一风格的要求;无权重算法不得伪造模型资产/缓存或 GPU 支持。手机复核按后续发布范围安排,Workflow 仍暂缓。 +旋转框任务保持独立,不加入现有轴对齐 Detection API。分割 FP16/量化和媒体能力、HRNet 等姿态扩展保留后续;跟踪已完成独立本地实现和桌面合成验证,当前等待整分支独立审查及后续发布安排,真实授权视频MOT质量尚未验证。继续桌面优先、模型 SDK 的 ModelScope 默认并保留 Hugging Face,以及独立 Demo 统一风格的要求;无权重算法不得伪造模型资产/缓存或 GPU 支持。390px桌面视口不等同手机验证,Workflow仍暂缓。 后文日期更早的“下一阶段”保留为历史实施记录,当前优先级以本节为准。 diff --git a/reports/sdk-standard/pp-tracking-after.json b/reports/sdk-standard/pp-tracking-after.json new file mode 100644 index 0000000..3e63971 --- /dev/null +++ b/reports/sdk-standard/pp-tracking-after.json @@ -0,0 +1,269 @@ +{ + "standardVersion": "1.2.0", + "repositories": [ + { + "standardVersion": "1.2.0", + "repository": "web-sdk-PP-Tracking", + "summary": { + "requiredPassed": 17, + "requiredFailed": 0, + "requiredSkipped": 7, + "requiredUnknown": 0, + "requiredNotApplicable": 0, + "recommendedPassed": 3, + "recommendedFailed": 0, + "labs": 1, + "status": "locally-compliant" + }, + "findings": [ + { + "id": "ALGORITHM-001", + "level": "required", + "status": "pass", + "message": "算法清单声明身份、家族、来源、许可、输入输出与有状态语义", + "remediation": "按 sdk-manifest.algorithm.yaml 填写 algorithm 字段并记录状态生命周期。", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "CACHE-001", + "level": "required", + "status": "skip", + "message": "Cache storage, versioning, estimate, and cleanup are declared", + "remediation": "Add cache contract fields and expose cleanup controls in the Demo.", + "path": "sdk-manifest.yaml", + "evidence": "当前类型为 algorithm,本规则仅适用于 model;清单已通过完整校验。" + }, + { + "id": "DEMO-001", + "level": "required", + "status": "pass", + "message": "A runnable standalone Demo entry exists", + "remediation": "Add a Demo app with a documented start/build command.", + "path": "demo/index.html", + "evidence": "demo/index.html" + }, + { + "id": "DEMO-002", + "level": "required", + "status": "pass", + "message": "Demo declares Chinese as its initial language", + "remediation": "Set lang or default language state to zh-CN.", + "path": "demo/index.html", + "evidence": "demo/index.html, demo/src/App.tsx, examples/vanilla/index.html" + }, + { + "id": "DEMO-003", + "level": "required", + "status": "pass", + "message": "Demo provides an in-page Chinese/English toggle", + "remediation": "Add a language toggle that does not alter inference state.", + "path": "demo/src/App.tsx", + "evidence": "demo/src/App.tsx" + }, + { + "id": "DEMO-004", + "level": "required", + "status": "skip", + "message": "Demo exposes current-model and global cache cleanup controls", + "remediation": "Wire clear-current and clear-all actions to the SDK cache API.", + "path": "sdk-manifest.yaml", + "evidence": "当前类型为 algorithm,本规则仅适用于 model;清单已通过完整校验。" + }, + { + "id": "DEMO-005", + "level": "required", + "status": "pass", + "message": "Demo 展示当前模型或算法的信息、运行信息和耗时", + "remediation": "模型使用 data-sdk-model-info,算法使用 data-sdk-algorithm-info;两者保留 data-sdk-runtime-info 和 data-sdk-timing。", + "path": "demo/src/App.tsx", + "evidence": "demo/src/App.tsx" + }, + { + "id": "DEMO-006", + "level": "required", + "status": "pass", + "message": "算法 Demo 提供状态复位操作", + "remediation": "使用 data-sdk-state-reset 标记复位控件,并按状态生命周期契约连接 reset。", + "path": "demo/src/App.tsx", + "evidence": "demo/src/App.tsx" + }, + { + "id": "DEPLOY-001", + "level": "required", + "status": "skip", + "message": "The live Demo has a reproducible HTTPS deployment from protected source with least-privilege credentials", + "remediation": "Configure and verify a deployment that meets standards/v1/repository-governance-contract.md, then record its commit and deployment evidence.", + "evidence": "Requires read-only remote-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "DOC-001", + "level": "required", + "status": "pass", + "message": "Chinese-default and English README files link to one another", + "remediation": "Add README.md and README.en.md with reciprocal language links.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-002", + "level": "required", + "status": "pass", + "message": "Chinese and English documentation directories are present", + "remediation": "Provide equivalent docs/zh-CN and docs/en quick-start/API guidance.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-003", + "level": "required", + "status": "pass", + "message": "README or package metadata exposes GitHub, npm, and live Demo links", + "remediation": "Add repository, package, and Demo URLs to README and manifest.", + "path": "README.md", + "evidence": "README.md" + }, + { + "id": "EXAMPLE-001", + "level": "required", + "status": "pass", + "message": "Vanilla TypeScript/DOM portability example exists", + "remediation": "Add examples/vanilla with a runnable README.", + "path": "examples/vanilla/README.en.md", + "evidence": "examples/vanilla/README.en.md" + }, + { + "id": "EXAMPLE-002", + "level": "required", + "status": "pass", + "message": "React reference example exists", + "remediation": "Add examples/react or explicitly record a migration exception.", + "path": "examples/react/README.en.md", + "evidence": "examples/react/README.en.md" + }, + { + "id": "EXAMPLE-003", + "level": "recommended", + "status": "pass", + "message": "Declared target surfaces have runnable examples or explicit unsupported status", + "remediation": "Align examples with manifest target surfaces.", + "path": "examples/vanilla/README.en.md", + "evidence": "examples/vanilla/README.en.md, examples/react/README.en.md" + }, + { + "id": "GOV-001", + "level": "required", + "status": "skip", + "message": "The default branch has an active least-privilege Ruleset for pull requests, current CI checks, resolved conversations, deletion, and force-push protection", + "remediation": "Configure an active default-branch Ruleset using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "GOV-002", + "level": "required", + "status": "skip", + "message": "Release tags are immutable under an active tag Ruleset", + "remediation": "Protect the documented release tag pattern against updates and deletion, minimize bypass, and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "LABS-001", + "level": "labs", + "status": "skip", + "message": "Experimental backends and NPU claims have explicit evidence and limits", + "remediation": "Mark WebNN/NPU as labs unless a dated verification matrix exists.", + "evidence": "Labs rule is informational" + }, + { + "id": "META-001", + "level": "required", + "status": "pass", + "message": "SDK manifest is present and declares the v1 contract", + "remediation": "Copy standards/v1/templates/sdk-manifest.yaml and fill every required field.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "META-002", + "level": "required", + "status": "pass", + "message": "package.json declares a public package identity and version", + "remediation": "Add a non-private package with a semver version and repository metadata.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "MODEL-001", + "level": "required", + "status": "skip", + "message": "Manifest declares model identity, precision, size, source, license, and checksum", + "remediation": "Complete the model and asset fields in sdk-manifest.yaml.", + "path": "sdk-manifest.yaml", + "evidence": "当前类型为 algorithm,本规则仅适用于 model;清单已通过完整校验。" + }, + { + "id": "PAGES-001", + "level": "required", + "status": "skip", + "message": "A GitHub Pages Demo deploys through GitHub Actions with scoped permissions, environment binding, HTTPS, and concurrency control", + "remediation": "Configure Pages and its deployment workflow using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "PERF-001", + "level": "required", + "status": "pass", + "message": "声明当前 SDK 类型所需的标准耗时字段", + "remediation": "模型保留下载、缓存、校验、会话、推理和总耗时;算法声明 validationMs、predictionMs、associationMs、updateMs、totalMs。", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "RELEASE-001", + "level": "required", + "status": "pass", + "message": "CI workflow runs verification before release", + "remediation": "Add .github/workflows/ci.yml with tests, typecheck, and build.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "RELEASE-002", + "level": "required", + "status": "pass", + "message": "GitHub Release workflow exists", + "remediation": "Add a tag-triggered release workflow and publish notes.", + "path": ".github/workflows/release.yml", + "evidence": ".github/workflows/release.yml" + }, + { + "id": "RELEASE-003", + "level": "recommended", + "status": "pass", + "message": "Changelog documents user-visible SDK and Demo changes", + "remediation": "Add CHANGELOG.md with the current release entry.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "RUNTIME-001", + "level": "required", + "status": "pass", + "message": "Requested/actual backend and execution mode are declared", + "remediation": "Add runtime backends, execution modes, and actual backend reporting.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "UI-001", + "level": "recommended", + "status": "pass", + "message": "Demo styling references the shared token vocabulary", + "remediation": "Import or copy standards/v1/ui-tokens.json values without introducing conflicting status colors.", + "path": "standards/v1/ui-tokens.json", + "evidence": "standards/v1/ui-tokens.json" + } + ] + } + ] +} diff --git a/reports/tracking/2026-09-19-foundation/README.md b/reports/tracking/2026-09-19-foundation/README.md new file mode 100644 index 0000000..82e8cd4 --- /dev/null +++ b/reports/tracking/2026-09-19-foundation/README.md @@ -0,0 +1,42 @@ +# 独立跟踪SDK本地验收与标准回归(2026-09-19) + +本轮完成纯算法标准1.2.0及独立 `web-sdk-PP-Tracking` 本地首版。SDK仍未发布npm、Release或在线Demo,因此门户在线目录继续保留7个已发布SDK,Workflow继续暂缓。本目录只归档标准兼容与门户回归证据,不包含跟踪运行时。 + +## 7个既有SDK的新旧标准差异 + +[原始报告](standard-regression.json)于2026-09-19T00:32:22.867Z生成,原字节保留每条规则的级别、状态、证据和修复建议。旧标准提交 `df2863ec22e73790f35fcf57c673a55afd4a1be0`,新标准/检查器提交 `063e8c215b1791d43d3ee704bcb83006ddc43611`。原JSON只记录baseline短SHA,新标准完整SHA在本说明补充,不改写旧测量字段。 + +| SDK | 固定提交 | 既有规则状态变化 | 旧/新required失败 | +| --- | --- | --- | --- | +| Detection | fede55916770922943178acf1de6684708573d04 | 0 | 0 / 0 | +| DocLayoutV3 | 3aedb7b35af49a1c207fd4f846211366a9d09973 | 0 | 0 / 0 | +| LCNet_x1_0_doc_ori | 0e404877890ab79442f3db43b44aaf726b81e131 | 0 | 0 / 0 | +| OCRv6 | 746a44d0dc59b2b21496dc6853cea56adeb1b17b | 0 | 0 / 0 | +| RotatedDetection | b54ae15ca124fd111cac6e683409fdbb88a14e13 | 0 | 0 / 0 | +| Segmentation | 89b350d30305ecbc275780e455d1c115a250570f | 0 | 0 / 0 | +| TinyPose | 70fe7e5a2377be64cd86efc8b306f03121b6f5c9 | 0 | 0 / 0 | + +新增算法专用规则对旧模型SDK为不适用skip,不偷换成pass;远程治理/托管required依然skip,不能由本地扫描宣称全面compliant。检查对象是提交的git archive快照,不包含用户未提交内容。原Detection工作目录有受限临时目录会导致EPERM,本轮未改权限;采用提交快照避免扫描非交付临时内容。 + +原脚本在[provenance/](provenance/)。正式复跑入口基于报告里的固定提交生成临时快照,使用新旧固定检查器,不自动改写历史报告。先安装门户锁定依赖,确保Git和Python3可用,再在门户根目录执行: + +```powershell +node reports/tracking/2026-09-19-foundation/verify-standard-regression.mjs verify +node reports/tracking/2026-09-19-foundation/verify-standard-regression.mjs rerun --repo-root F:/git/00_chenmohan/github --out .tmp/tracking-standard-regression-replay.json +pnpm --config.verify-deps-before-run=false --config.manage-package-manager-versions=false sdk:check -- --repo F:/git/00_chenmohan/github/web-sdk-PP-Tracking --format json --out reports/sdk-standard/pp-tracking-after.json +``` + +`verify`固定原JSON SHA256为 `3c20f94d6e7f099cfc4e14d1848c120ae259249bfd47237ee1394de86af712d4`,重新推导7个SDK的差异和失败列表。`rerun`还从Git提交重新导出并比较每条规则id/level/status,新报告保留新执行时间与新证据路径,不覆盖原报告。源码仓库位置由 `--repo-root` 指定,临时快照在系统临时目录;快照在任务结束后保留供检查。Windows系统tar无法正确解包TinyPose的既有中文截图名,入口改用Python标准库zipfile解包git archive ZIP;这不是原始报告扫描失败。 + +## 门户和SDK证据 + +- [portal-test.log](portal-test.log):Task1完整原始日志,11文件93测试通过。 +- [portal-build.log](portal-build.log):Task1完整原始构建日志,18页,0错误、0警告、7个既有hint。 +- [portal-task-4-build.log](portal-task-4-build.log):归档入口和路线更新后的实际构建,18页,56个诊断文件,0错误、0警告、仍为7个既有hint。 +- [portal-validation-summary.json](portal-validation-summary.json):既有执行结构化摘要;14 e2e基于生产preview通过,原日志在主代理会话93634a,未伪造本地日志。默认dev启动超时仍是已知环境现象。 +- [Tracking after标准报告](../../sdk-standard/pp-tracking-after.json):归档/文档修改后的实际本地扫描,保留远程skip。 +- SDK验收:独立仓库 `F:/git/00_chenmohan/github/web-sdk-PP-Tracking/reports/2026-09-19-desktop/README.md`,原始129次公开API更新、10/50/100框各600个warm样本、29单测/9浏览器组/实际包/全部构建日志及源码来源说明。 + +性能核心提交为518f94a,ESM SHA256为 `fdd721bf78b7dc2ea3c554619418c914041971e37a2274ef3851e251997d4935`。Chromium151.0.7922.34对应性能,Chromium153.0.8010.12对应完整Demo交互,两者没有混为一次验证。390px是桌面视口;不存在真实手机或真实视频MOT精度结论。独立Apache-2.0实现避免分发前期存在来源义务疑点的参考代码,但不宣称上游逐值等价。 + +本轮只更新本地证据、兼容说明与两份总路线。待独立整分支审查后交付本地Demo;正式发布、远程治理核验和真实授权序列质量需另行安排。 diff --git a/reports/tracking/2026-09-19-foundation/portal-build.log b/reports/tracking/2026-09-19-foundation/portal-build.log new file mode 100644 index 0000000..2500a0f --- /dev/null +++ b/reports/tracking/2026-09-19-foundation/portal-build.log @@ -0,0 +1,82 @@ +$ astro check && astro build +08:29:11 [vite] Re-optimizing dependencies because vite config has changed +08:29:12 [vite] [optimizer] bundling dependencies... +08:29:14 [content] Syncing content +08:29:14 [content] Synced content +08:29:14 [types] Generated 3.07s +08:29:14 [check] Getting diagnostics for Astro files in C:\Users\chenm\.codex\worktrees\segmentation-portal\chenmohan123.github.io... +reports/sdk-standard/2026-09-12-detection-download/verify-example.mjs:76:10 - warning ts(2568): Property 'error' may not exist on type '{ date: string; browser: string; sdk: string; status: string; runs: never[]; errors: never[]; }'. Did you mean 'errors'? + +76 report.error = String(error); +   ~~~~~ +reports/sdk-standard/2026-09-12-detection-download/verify-example.mjs:36:12 - warning ts(2568): Property 'click' may not exist on type 'SVGElement | HTMLElement'. Did you mean 'onclick'? + Property 'click' does not exist on type 'SVGElement'. + +36 button.click(); +   ~~~~~ +reports/sdk-standard/2026-09-12-detection-download/verify-example.mjs:35:12 - warning ts(2568): Property 'click' may not exist on type 'SVGElement | HTMLElement'. Did you mean 'onclick'? + Property 'click' does not exist on type 'SVGElement'. + +35 button.click(); +   ~~~~~ + +reports/sdk-standard/2026-09-15-tiny-release/demo-smoke.mjs:161:10 - warning ts(2568): Property 'error' may not exist on type '{ date: string; base: string; deployment: any; os: string; cpu: string; manifestSha256: string; rows: never[]; errors: never[]; status: string; }'. Did you mean 'errors'? + +161 result.error = String(e); +   ~~~~~ + +src/lib/registry/schema.ts:22:121 - warning ts(6385): '(params?: string | { normalize?: boolean | undefined; pattern?: RegExp | undefined; abort?: boolean | undefined; hostname?: RegExp | undefined; protocol?: RegExp | undefined; error?: string | ... 1 more ... | undefined; message?: string | undefined; } | undefined): ZodString' is deprecated. + +22 assets: z.array(z.object({ id: z.string(), precision: z.string(), bytes: z.number().int().positive(), url: z.string().url(), sha256: z.string().regex(/^[a-f0-9]{64}$/) })).min(1), +   ~~~ +src/lib/registry/schema.ts:15:36 - warning ts(6385): '(params?: string | { normalize?: boolean | undefined; pattern?: RegExp | undefined; abort?: boolean | undefined; hostname?: RegExp | undefined; protocol?: RegExp | undefined; error?: string | ... 1 more ... | undefined; message?: string | undefined; } | undefined): ZodString' is deprecated. + +15 demo: z.object({ url: z.string().url(), localProcessing: z.boolean() }), +   ~~~ +src/lib/registry/schema.ts:12:26 - warning ts(6385): '(params?: string | { normalize?: boolean | undefined; pattern?: RegExp | undefined; abort?: boolean | undefined; hostname?: RegExp | undefined; protocol?: RegExp | undefined; error?: string | ... 1 more ... | undefined; message?: string | undefined; } | undefined): ZodString' is deprecated. + +12 repository: z.string().url(), +   ~~~ + +Result (54 files): +- 0 errors +- 0 warnings +- 7 hints + +08:29:34 [content] Syncing content +08:29:34 [content] Synced content +08:29:34 [types] Generated 697ms +08:29:34 [build] output: "static" +08:29:34 [build] mode: "static" +08:29:34 [build] directory: C:\Users\chenm\.codex\worktrees\segmentation-portal\chenmohan123.github.io\dist\ +08:29:34 [build] Collecting build info... +08:29:34 [build] ✓ Completed in 772ms. +08:29:34 [build] Building static entrypoints... +08:29:36 [vite] ✓ built in 1.16s +08:29:42 [vite] ✓ built in 5.96s +08:29:42 [build] Rearranging server assets... + + generating static routes +08:29:42 ├─ /brands/baidu/index.html (+17ms) +08:29:42 ├─ /docs/index.html (+9ms) +08:29:42 ├─ /models/pp-detection/compare/index.html (+60ms) +08:29:42 ├─ /models/pp-detection/index.html (+3ms) +08:29:42 ├─ /models/pp-doclayoutv3/index.html (+3ms) +08:29:42 ├─ /models/pp-lcnet-x1-0-doc-ori/index.html (+3ms) +08:29:42 ├─ /models/pp-ocrv6/index.html (+3ms) +08:29:42 ├─ /models/pp-rotated-detection/index.html (+3ms) +08:29:42 ├─ /models/pp-segmentation/index.html (+3ms) +08:29:42 ├─ /models/pp-tinypose/index.html (+3ms) +08:29:42 ├─ /tasks/detection/index.html (+4ms) +08:29:42 ├─ /tasks/document-layout/index.html (+4ms) +08:29:42 ├─ /tasks/image-correction/index.html (+3ms) +08:29:42 ├─ /tasks/ocr/index.html (+3ms) +08:29:42 ├─ /tasks/rotated-detection/index.html (+3ms) +08:29:42 ├─ /tasks/instance-segmentation/index.html (+3ms) +08:29:42 ├─ /tasks/pose-estimation/index.html (+3ms) +08:29:42 ├─ /index.html (+89ms) +08:29:42 ✓ Completed in 479ms. + +08:29:42 [build] ✓ Completed in 7.70s. +08:29:42 [build] 18 page(s) built in 8.48s +08:29:42 [build] Complete! diff --git a/reports/tracking/2026-09-19-foundation/portal-task-4-build.log b/reports/tracking/2026-09-19-foundation/portal-task-4-build.log new file mode 100644 index 0000000..98e405f --- /dev/null +++ b/reports/tracking/2026-09-19-foundation/portal-task-4-build.log @@ -0,0 +1,80 @@ +$ astro check && astro build +09:53:13 [content] Syncing content +09:53:13 [content] Synced content +09:53:13 [types] Generated 631ms +09:53:13 [check] Getting diagnostics for Astro files in C:\Users\chenm\.codex\worktrees\segmentation-portal\chenmohan123.github.io... +reports/sdk-standard/2026-09-12-detection-download/verify-example.mjs:76:10 - warning ts(2568): Property 'error' may not exist on type '{ date: string; browser: string; sdk: string; status: string; runs: never[]; errors: never[]; }'. Did you mean 'errors'? + +76 report.error = String(error); +   ~~~~~ +reports/sdk-standard/2026-09-12-detection-download/verify-example.mjs:36:12 - warning ts(2568): Property 'click' may not exist on type 'SVGElement | HTMLElement'. Did you mean 'onclick'? + Property 'click' does not exist on type 'SVGElement'. + +36 button.click(); +   ~~~~~ +reports/sdk-standard/2026-09-12-detection-download/verify-example.mjs:35:12 - warning ts(2568): Property 'click' may not exist on type 'SVGElement | HTMLElement'. Did you mean 'onclick'? + Property 'click' does not exist on type 'SVGElement'. + +35 button.click(); +   ~~~~~ + +reports/sdk-standard/2026-09-15-tiny-release/demo-smoke.mjs:161:10 - warning ts(2568): Property 'error' may not exist on type '{ date: string; base: string; deployment: any; os: string; cpu: string; manifestSha256: string; rows: never[]; errors: never[]; status: string; }'. Did you mean 'errors'? + +161 result.error = String(e); +   ~~~~~ + +src/lib/registry/schema.ts:22:121 - warning ts(6385): '(params?: string | { normalize?: boolean | undefined; pattern?: RegExp | undefined; abort?: boolean | undefined; hostname?: RegExp | undefined; protocol?: RegExp | undefined; error?: string | ... 1 more ... | undefined; message?: string | undefined; } | undefined): ZodString' is deprecated. + +22 assets: z.array(z.object({ id: z.string(), precision: z.string(), bytes: z.number().int().positive(), url: z.string().url(), sha256: z.string().regex(/^[a-f0-9]{64}$/) })).min(1), +   ~~~ +src/lib/registry/schema.ts:15:36 - warning ts(6385): '(params?: string | { normalize?: boolean | undefined; pattern?: RegExp | undefined; abort?: boolean | undefined; hostname?: RegExp | undefined; protocol?: RegExp | undefined; error?: string | ... 1 more ... | undefined; message?: string | undefined; } | undefined): ZodString' is deprecated. + +15 demo: z.object({ url: z.string().url(), localProcessing: z.boolean() }), +   ~~~ +src/lib/registry/schema.ts:12:26 - warning ts(6385): '(params?: string | { normalize?: boolean | undefined; pattern?: RegExp | undefined; abort?: boolean | undefined; hostname?: RegExp | undefined; protocol?: RegExp | undefined; error?: string | ... 1 more ... | undefined; message?: string | undefined; } | undefined): ZodString' is deprecated. + +12 repository: z.string().url(), +   ~~~ + +Result (56 files): +- 0 errors +- 0 warnings +- 7 hints + +09:53:23 [content] Syncing content +09:53:23 [content] Synced content +09:53:23 [types] Generated 643ms +09:53:23 [build] output: "static" +09:53:23 [build] mode: "static" +09:53:23 [build] directory: C:\Users\chenm\.codex\worktrees\segmentation-portal\chenmohan123.github.io\dist\ +09:53:23 [build] Collecting build info... +09:53:23 [build] ✓ Completed in 713ms. +09:53:23 [build] Building static entrypoints... +09:53:23 [vite] ✓ built in 304ms +09:53:23 [vite] ✓ built in 487ms +09:53:23 [build] Rearranging server assets... + + generating static routes +09:53:24 ├─ /brands/baidu/index.html (+14ms) +09:53:24 ├─ /docs/index.html (+5ms) +09:53:24 ├─ /models/pp-detection/compare/index.html (+36ms) +09:53:24 ├─ /models/pp-detection/index.html (+5ms) +09:53:24 ├─ /models/pp-doclayoutv3/index.html (+3ms) +09:53:24 ├─ /models/pp-lcnet-x1-0-doc-ori/index.html (+3ms) +09:53:24 ├─ /models/pp-ocrv6/index.html (+3ms) +09:53:24 ├─ /models/pp-rotated-detection/index.html (+3ms) +09:53:24 ├─ /models/pp-segmentation/index.html (+3ms) +09:53:24 ├─ /models/pp-tinypose/index.html (+3ms) +09:53:24 ├─ /tasks/detection/index.html (+3ms) +09:53:24 ├─ /tasks/document-layout/index.html (+2ms) +09:53:24 ├─ /tasks/image-correction/index.html (+2ms) +09:53:24 ├─ /tasks/ocr/index.html (+3ms) +09:53:24 ├─ /tasks/rotated-detection/index.html (+2ms) +09:53:24 ├─ /tasks/instance-segmentation/index.html (+3ms) +09:53:24 ├─ /tasks/pose-estimation/index.html (+2ms) +09:53:24 ├─ /index.html (+72ms) +09:53:24 ✓ Completed in 250ms. + +09:53:24 [build] ✓ Completed in 1.12s. +09:53:24 [build] 18 page(s) built in 1.84s +09:53:24 [build] Complete! diff --git a/reports/tracking/2026-09-19-foundation/portal-test.log b/reports/tracking/2026-09-19-foundation/portal-test.log new file mode 100644 index 0000000..fdac407 --- /dev/null +++ b/reports/tracking/2026-09-19-foundation/portal-test.log @@ -0,0 +1,31 @@ +$ vitest run + + RUN v3.2.7 C:/Users/chenm/.codex/worktrees/segmentation-portal/chenmohan123.github.io + + ✓ src/lib/registry/query.test.ts (3 tests) 13ms + ✓ tools/detection-comparison/tiny-precision.test.ts (3 tests) 54ms + ✓ tools/detection-comparison/build.test.ts (5 tests) 52ms + ✓ tools/detection-comparison/catalog.test.ts (2 tests) 98ms + ✓ src/content/models/registry.test.ts (6 tests) 302ms + ✓ tools/sdk-standard-check/standard-files.test.ts (7 tests) 596ms + ✓ v1 standard source > 接受包含变体、精度和多来源的模型清单 471ms + ✓ tools/sdk-standard-check/checker.test.ts (13 tests) 1044ms + ✓ SDK repository discovery > accepts both v1.0 manifests and the v1.1 manifest template 534ms + ✓ src/components/registry/ModelDirectory.test.tsx (5 tests) 1482ms + ✓ ModelDirectory > 旋转框检测分类只展示独立 SDK 并提供详情入口 929ms + ✓ src/components/model/DetectionComparison.test.tsx (2 tests) 2562ms + ✓ Detection 模型选型 > 筛选稳定精度并保留批次,不把XS实验量化列入稳定表 1648ms + ✓ Detection 模型选型 > 切换后端同时更新AP与耗时,明确不同汇总规则 912ms + ✓ tools/sdk-standard-check/algorithm.test.ts (25 tests) 2292ms + ✓ 纯算法 SDK 契约 > 合法算法无模型与缓存也通过,模型专属规则有跳过理由和证据 730ms + ✓ 纯算法 SDK 契约 > 缺少 data-sdk-runtime-info 标记会失败,说明文字不能代替标记 320ms + ✓ tools/sdk-standard-check/manifest-validation.test.ts (22 tests) 2511ms + ✓ 清单完整 schema 校验 > 扫描使用所选标准的 schema,标准损坏时不归咎于仓库 592ms + ✓ 清单完整 schema 校验 > YAML 中错误的资产数组返回 CONFIG-001 和 CLI 退出码 1 702ms + ✓ 清单完整 schema 校验 > 多个仓库中的损坏清单不会阻止后续仓库扫描 852ms + + Test Files 11 passed (11) + Tests 93 passed (93) + Start at 08:28:55 + Duration 8.82s (transform 1.19s, setup 10.04s, collect 4.67s, tests 11.01s, environment 42.18s, prepare 3.64s) + diff --git a/reports/tracking/2026-09-19-foundation/portal-validation-summary.json b/reports/tracking/2026-09-19-foundation/portal-validation-summary.json new file mode 100644 index 0000000..97525ce --- /dev/null +++ b/reports/tracking/2026-09-19-foundation/portal-validation-summary.json @@ -0,0 +1,18 @@ +{ + "date": "2026-09-19", + "kind": "既有执行结果的结构化摘要,不是原始测试日志或重新执行", + "unit": { "passed": 93, "files": 11, "source": "portal-test.log" }, + "build": { "pages": 18, "errors": 0, "warnings": 0, "hints": 7, "source": "portal-build.log", "note": "Task 1的既有构建;Task 4归档后构建结果另见实施报告。" }, + "e2e": { + "passed": 14, + "durationSeconds": 6.3, + "server": "本轮生产构建的Astro preview,127.0.0.1:4321", + "viewports": [1280, 390], + "scope": ["目录", "详情", "选型"], + "source": ".superpowers/sdd/2026-09-19-pp-tracking-foundation/progress.md;主代理命令会话93634a", + "rawLogFile": null, + "note": "原始输出保留在会话中,未另存日志;本文件仅据主代理执行记录整理。默认astro dev在60000ms未监听,生产preview测试通过不表示dev问题已修复。" + }, + "remoteChanges": false, + "trackingRegisteredInOnlineCatalog": false +} diff --git a/reports/tracking/2026-09-19-foundation/provenance/tracking-standard-regression.mjs b/reports/tracking/2026-09-19-foundation/provenance/tracking-standard-regression.mjs new file mode 100644 index 0000000..5374ca9 --- /dev/null +++ b/reports/tracking/2026-09-19-foundation/provenance/tracking-standard-regression.mjs @@ -0,0 +1,25 @@ +import fs from 'node:fs/promises'; +import path from 'node:path'; +import { execFileSync } from 'node:child_process'; +import { scanRepository as current } from '../tools/sdk-standard-check/src/check.mjs'; +import { scanRepository as baseline } from './tracking-baseline-reference/tools/sdk-standard-check/src/check.mjs'; + +const roots = ['Detection', 'DocLayoutV3', 'LCNet_x1_0_doc_ori', 'OCRv6', 'RotatedDetection', 'Segmentation', 'TinyPose'].map(x=>'web-sdk-PP-'+x); +const baseStandard = path.resolve('.tmp/tracking-baseline-reference/standards/v1'); +const nextStandard = path.resolve('standards/v1'); +const records = []; +for (const name of roots) { + const snapshot = path.resolve('C:/Users/chenm/.codex/tmp/tracking-compat-20260919', name); + const a = await baseline(snapshot, {standardRoot:baseStandard}); + const b = await current(snapshot, {standardRoot:nextStandard}); + const prior = new Map(a.findings.map(x=>[x.id,x])); + const changes = b.findings.filter(x=>prior.has(x.id) && x.status!==prior.get(x.id).status).map(x=>({id:x.id,before:prior.get(x.id).status,after:x.status})); + records.push({repository:name, commit:execFileSync('git',['-C',path.join('F:/git/00_chenmohan/github',name),'rev-parse','HEAD'],{encoding:'utf8'}).trim(), changes, + baselineFailures:a.findings.filter(x=>x.level==='required'&&x.status==='fail').map(x=>x.id), + currentFailures:b.findings.filter(x=>x.level==='required'&&x.status==='fail').map(x=>x.id), + baseline:a.findings,current:b.findings}); +} +const report={verifiedAt:new Date().toISOString(),baseline:'df2863e',scope:'仅各仓库HEAD的git archive快照;不包含用户未提交文件或本地临时目录',records}; +await fs.writeFile('.tmp/tracking-standard-regression.json',JSON.stringify(report,null,2)+'\n'); +console.log(JSON.stringify(records.map(({repository,changes,baselineFailures,currentFailures})=>({repository,changes,baselineFailures,currentFailures})),null,2)); +if(records.some(x=>x.changes.length))process.exitCode=1; diff --git a/reports/tracking/2026-09-19-foundation/standard-regression.json b/reports/tracking/2026-09-19-foundation/standard-regression.json new file mode 100644 index 0000000..5abf757 --- /dev/null +++ b/reports/tracking/2026-09-19-foundation/standard-regression.json @@ -0,0 +1,3414 @@ +{ + "verifiedAt": "2026-09-19T00:32:22.867Z", + "baseline": "df2863e", + "scope": "仅各仓库HEAD的git archive快照;不包含用户未提交文件或本地临时目录", + "records": [ + { + "repository": "web-sdk-PP-Detection", + "commit": "fede55916770922943178acf1de6684708573d04", + "changes": [], + "baselineFailures": [], + "currentFailures": [], + "baseline": [ + { + "id": "META-001", + "level": "required", + "status": "pass", + "message": "SDK manifest is present and declares the v1 contract", + "remediation": "Copy standards/v1/templates/sdk-manifest.yaml and fill every required field.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "META-002", + "level": "required", + "status": "pass", + "message": "package.json declares a public package identity and version", + "remediation": "Add a non-private package with a semver version and repository metadata.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-001", + "level": "required", + "status": "pass", + "message": "Chinese-default and English README files link to one another", + "remediation": "Add README.md and README.en.md with reciprocal language links.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-002", + "level": "required", + "status": "pass", + "message": "Chinese and English documentation directories are present", + "remediation": "Provide equivalent docs/zh-CN and docs/en quick-start/API guidance.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-003", + "level": "required", + "status": "pass", + "message": "README or package metadata exposes GitHub, npm, and live Demo links", + "remediation": "Add repository, package, and Demo URLs to README and manifest.", + "path": "README.md", + "evidence": "README.md" + }, + { + "id": "DEMO-001", + "level": "required", + "status": "pass", + "message": "A runnable standalone Demo entry exists", + "remediation": "Add a Demo app with a documented start/build command.", + "path": "apps/demo/index.html", + "evidence": "apps/demo/index.html" + }, + { + "id": "DEMO-002", + "level": "required", + "status": "pass", + "message": "Demo declares Chinese as its initial language", + "remediation": "Set lang or default language state to zh-CN.", + "path": "apps/demo/index.html", + "evidence": "apps/demo/index.html, apps/demo/src/App.tsx, apps/demo/src/ClassFilter.tsx, apps/demo/src/i18n/en.ts, apps/demo/tests/class-filter.spec.ts, apps/demo/tests/target-selection.spec.ts, examples/cdn/index.html, examples/model-variants/index.html, examples/react/index.html, examples/vanilla-vite/index.html, examples/vanilla/index.html, examples/vue/index.html, examples/wechat-webview/index.html" + }, + { + "id": "DEMO-003", + "level": "required", + "status": "pass", + "message": "Demo provides an in-page Chinese/English toggle", + "remediation": "Add a language toggle that does not alter inference state.", + "path": "apps/demo/src/App.tsx", + "evidence": "apps/demo/src/App.tsx, apps/demo/src/ClassFilter.tsx, apps/demo/src/i18n/detection-labels.ts, apps/demo/src/i18n/en.ts, apps/demo/src/i18n/zh-CN.ts, apps/demo/src/styles.css, apps/demo/tests/class-filter.spec.ts, apps/demo/tests/demo.spec.ts, apps/demo/tests/image-export.spec.ts, apps/demo/tests/image-zoom.spec.ts, apps/demo/tests/precision-selection.spec.ts, apps/demo/tests/small-objects.spec.ts, apps/demo/tests/target-selection.spec.ts" + }, + { + "id": "DEMO-004", + "level": "required", + "status": "pass", + "message": "Demo exposes current-model and global cache cleanup controls", + "remediation": "Wire clear-current and clear-all actions to the SDK cache API.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DEMO-005", + "level": "required", + "status": "pass", + "message": "Demo exposes model/runtime/timing information", + "remediation": "Render data-sdk-model-info, data-sdk-runtime-info, and data-sdk-timing regions.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "MODEL-001", + "level": "required", + "status": "pass", + "message": "Manifest declares model identity, precision, size, source, license, and checksum", + "remediation": "Complete the model and asset fields in sdk-manifest.yaml.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "RUNTIME-001", + "level": "required", + "status": "pass", + "message": "Requested/actual backend and execution mode are declared", + "remediation": "Add runtime backends, execution modes, and actual backend reporting.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "PERF-001", + "level": "required", + "status": "pass", + "message": "Standard load and inference timing fields are declared", + "remediation": "Document download, cache, integrity, session, inference, and total timing.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "CACHE-001", + "level": "required", + "status": "pass", + "message": "Cache storage, versioning, estimate, and cleanup are declared", + "remediation": "Add cache contract fields and expose cleanup controls in the Demo.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "EXAMPLE-001", + "level": "required", + "status": "pass", + "message": "Vanilla TypeScript/DOM portability example exists", + "remediation": "Add examples/vanilla with a runnable README.", + "path": "examples/vanilla-vite/README.md", + "evidence": "examples/vanilla-vite/README.md" + }, + { + "id": "EXAMPLE-002", + "level": "required", + "status": "pass", + "message": "React reference example exists", + "remediation": "Add examples/react or explicitly record a migration exception.", + "path": "examples/react/README.md", + "evidence": "examples/react/README.md" + }, + { + "id": "EXAMPLE-003", + "level": "recommended", + "status": "pass", + "message": "Declared target surfaces have runnable examples or explicit unsupported status", + "remediation": "Align examples with manifest target surfaces.", + "path": "examples/vanilla-vite/README.md", + "evidence": "examples/vanilla-vite/README.md, examples/react/README.md, examples/vue/README.md, examples/cdn/README.md, examples/wechat-webview/README.md" + }, + { + "id": "RELEASE-001", + "level": "required", + "status": "pass", + "message": "CI workflow runs verification before release", + "remediation": "Add .github/workflows/ci.yml with tests, typecheck, and build.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "RELEASE-002", + "level": "required", + "status": "pass", + "message": "GitHub Release workflow exists", + "remediation": "Add a tag-triggered release workflow and publish notes.", + "path": ".github/workflows/release.yml", + "evidence": ".github/workflows/release.yml" + }, + { + "id": "RELEASE-003", + "level": "recommended", + "status": "pass", + "message": "Changelog documents user-visible SDK and Demo changes", + "remediation": "Add CHANGELOG.md with the current release entry.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "GOV-001", + "level": "required", + "status": "skip", + "message": "The default branch has an active least-privilege Ruleset for pull requests, current CI checks, resolved conversations, deletion, and force-push protection", + "remediation": "Configure an active default-branch Ruleset using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "GOV-002", + "level": "required", + "status": "skip", + "message": "Release tags are immutable under an active tag Ruleset", + "remediation": "Protect the documented release tag pattern against updates and deletion, minimize bypass, and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "DEPLOY-001", + "level": "required", + "status": "skip", + "message": "The live Demo has a reproducible HTTPS deployment from protected source with least-privilege credentials", + "remediation": "Configure and verify a deployment that meets standards/v1/repository-governance-contract.md, then record its commit and deployment evidence.", + "evidence": "Requires read-only remote-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "PAGES-001", + "level": "required", + "status": "skip", + "message": "A GitHub Pages Demo deploys through GitHub Actions with scoped permissions, environment binding, HTTPS, and concurrency control", + "remediation": "Configure Pages and its deployment workflow using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "UI-001", + "level": "recommended", + "status": "pass", + "message": "Demo styling references the shared token vocabulary", + "remediation": "Import or copy standards/v1/ui-tokens.json values without introducing conflicting status colors.", + "path": "standards/v1/ui-tokens.json", + "evidence": "standards/v1/ui-tokens.json" + }, + { + "id": "LABS-001", + "level": "labs", + "status": "skip", + "message": "Experimental backends and NPU claims have explicit evidence and limits", + "remediation": "Mark WebNN/NPU as labs unless a dated verification matrix exists.", + "evidence": "Labs rule is informational" + } + ], + "current": [ + { + "id": "META-001", + "level": "required", + "status": "pass", + "message": "SDK manifest is present and declares the v1 contract", + "remediation": "Copy standards/v1/templates/sdk-manifest.yaml and fill every required field.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "META-002", + "level": "required", + "status": "pass", + "message": "package.json declares a public package identity and version", + "remediation": "Add a non-private package with a semver version and repository metadata.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-001", + "level": "required", + "status": "pass", + "message": "Chinese-default and English README files link to one another", + "remediation": "Add README.md and README.en.md with reciprocal language links.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-002", + "level": "required", + "status": "pass", + "message": "Chinese and English documentation directories are present", + "remediation": "Provide equivalent docs/zh-CN and docs/en quick-start/API guidance.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-003", + "level": "required", + "status": "pass", + "message": "README or package metadata exposes GitHub, npm, and live Demo links", + "remediation": "Add repository, package, and Demo URLs to README and manifest.", + "path": "README.md", + "evidence": "README.md" + }, + { + "id": "DEMO-001", + "level": "required", + "status": "pass", + "message": "A runnable standalone Demo entry exists", + "remediation": "Add a Demo app with a documented start/build command.", + "path": "apps/demo/index.html", + "evidence": "apps/demo/index.html" + }, + { + "id": "DEMO-002", + "level": "required", + "status": "pass", + "message": "Demo declares Chinese as its initial language", + "remediation": "Set lang or default language state to zh-CN.", + "path": "apps/demo/index.html", + "evidence": "apps/demo/index.html, apps/demo/src/App.tsx, apps/demo/src/ClassFilter.tsx, apps/demo/src/i18n/en.ts, apps/demo/tests/class-filter.spec.ts, apps/demo/tests/target-selection.spec.ts, examples/cdn/index.html, examples/model-variants/index.html, examples/react/index.html, examples/vanilla-vite/index.html, examples/vanilla/index.html, examples/vue/index.html, examples/wechat-webview/index.html" + }, + { + "id": "DEMO-003", + "level": "required", + "status": "pass", + "message": "Demo provides an in-page Chinese/English toggle", + "remediation": "Add a language toggle that does not alter inference state.", + "path": "apps/demo/src/App.tsx", + "evidence": "apps/demo/src/App.tsx, apps/demo/src/ClassFilter.tsx, apps/demo/src/i18n/detection-labels.ts, apps/demo/src/i18n/en.ts, apps/demo/src/i18n/zh-CN.ts, apps/demo/src/styles.css, apps/demo/tests/class-filter.spec.ts, apps/demo/tests/demo.spec.ts, apps/demo/tests/image-export.spec.ts, apps/demo/tests/image-zoom.spec.ts, apps/demo/tests/precision-selection.spec.ts, apps/demo/tests/small-objects.spec.ts, apps/demo/tests/target-selection.spec.ts" + }, + { + "id": "DEMO-004", + "level": "required", + "status": "pass", + "message": "Demo exposes current-model and global cache cleanup controls", + "remediation": "Wire clear-current and clear-all actions to the SDK cache API.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DEMO-005", + "level": "required", + "status": "pass", + "message": "Demo 展示当前模型或算法的信息、运行信息和耗时", + "remediation": "模型使用 data-sdk-model-info,算法使用 data-sdk-algorithm-info;两者保留 data-sdk-runtime-info 和 data-sdk-timing。", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DEMO-006", + "level": "required", + "status": "skip", + "message": "算法 Demo 提供状态复位操作", + "remediation": "使用 data-sdk-state-reset 标记复位控件,并按状态生命周期契约连接 reset。", + "path": "sdk-manifest.yaml", + "evidence": "当前类型为 model,本规则仅适用于 algorithm;清单已通过完整校验。" + }, + { + "id": "ALGORITHM-001", + "level": "required", + "status": "skip", + "message": "算法清单声明身份、家族、来源、许可、输入输出与有状态语义", + "remediation": "按 sdk-manifest.algorithm.yaml 填写 algorithm 字段并记录状态生命周期。", + "path": "sdk-manifest.yaml", + "evidence": "当前类型为 model,本规则仅适用于 algorithm;清单已通过完整校验。" + }, + { + "id": "MODEL-001", + "level": "required", + "status": "pass", + "message": "Manifest declares model identity, precision, size, source, license, and checksum", + "remediation": "Complete the model and asset fields in sdk-manifest.yaml.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "RUNTIME-001", + "level": "required", + "status": "pass", + "message": "Requested/actual backend and execution mode are declared", + "remediation": "Add runtime backends, execution modes, and actual backend reporting.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "PERF-001", + "level": "required", + "status": "pass", + "message": "声明当前 SDK 类型所需的标准耗时字段", + "remediation": "模型保留下载、缓存、校验、会话、推理和总耗时;算法声明 validationMs、predictionMs、associationMs、updateMs、totalMs。", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "CACHE-001", + "level": "required", + "status": "pass", + "message": "Cache storage, versioning, estimate, and cleanup are declared", + "remediation": "Add cache contract fields and expose cleanup controls in the Demo.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "EXAMPLE-001", + "level": "required", + "status": "pass", + "message": "Vanilla TypeScript/DOM portability example exists", + "remediation": "Add examples/vanilla with a runnable README.", + "path": "examples/vanilla-vite/README.md", + "evidence": "examples/vanilla-vite/README.md" + }, + { + "id": "EXAMPLE-002", + "level": "required", + "status": "pass", + "message": "React reference example exists", + "remediation": "Add examples/react or explicitly record a migration exception.", + "path": "examples/react/README.md", + "evidence": "examples/react/README.md" + }, + { + "id": "EXAMPLE-003", + "level": "recommended", + "status": "pass", + "message": "Declared target surfaces have runnable examples or explicit unsupported status", + "remediation": "Align examples with manifest target surfaces.", + "path": "examples/vanilla-vite/README.md", + "evidence": "examples/vanilla-vite/README.md, examples/react/README.md, examples/vue/README.md, examples/cdn/README.md, examples/wechat-webview/README.md" + }, + { + "id": "RELEASE-001", + "level": "required", + "status": "pass", + "message": "CI workflow runs verification before release", + "remediation": "Add .github/workflows/ci.yml with tests, typecheck, and build.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "RELEASE-002", + "level": "required", + "status": "pass", + "message": "GitHub Release workflow exists", + "remediation": "Add a tag-triggered release workflow and publish notes.", + "path": ".github/workflows/release.yml", + "evidence": ".github/workflows/release.yml" + }, + { + "id": "RELEASE-003", + "level": "recommended", + "status": "pass", + "message": "Changelog documents user-visible SDK and Demo changes", + "remediation": "Add CHANGELOG.md with the current release entry.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "GOV-001", + "level": "required", + "status": "skip", + "message": "The default branch has an active least-privilege Ruleset for pull requests, current CI checks, resolved conversations, deletion, and force-push protection", + "remediation": "Configure an active default-branch Ruleset using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "GOV-002", + "level": "required", + "status": "skip", + "message": "Release tags are immutable under an active tag Ruleset", + "remediation": "Protect the documented release tag pattern against updates and deletion, minimize bypass, and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "DEPLOY-001", + "level": "required", + "status": "skip", + "message": "The live Demo has a reproducible HTTPS deployment from protected source with least-privilege credentials", + "remediation": "Configure and verify a deployment that meets standards/v1/repository-governance-contract.md, then record its commit and deployment evidence.", + "evidence": "Requires read-only remote-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "PAGES-001", + "level": "required", + "status": "skip", + "message": "A GitHub Pages Demo deploys through GitHub Actions with scoped permissions, environment binding, HTTPS, and concurrency control", + "remediation": "Configure Pages and its deployment workflow using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "UI-001", + "level": "recommended", + "status": "pass", + "message": "Demo styling references the shared token vocabulary", + "remediation": "Import or copy standards/v1/ui-tokens.json values without introducing conflicting status colors.", + "path": "standards/v1/ui-tokens.json", + "evidence": "standards/v1/ui-tokens.json" + }, + { + "id": "LABS-001", + "level": "labs", + "status": "skip", + "message": "Experimental backends and NPU claims have explicit evidence and limits", + "remediation": "Mark WebNN/NPU as labs unless a dated verification matrix exists.", + "evidence": "Labs rule is informational" + } + ] + }, + { + "repository": "web-sdk-PP-DocLayoutV3", + "commit": "3aedb7b35af49a1c207fd4f846211366a9d09973", + "changes": [], + "baselineFailures": [], + "currentFailures": [], + "baseline": [ + { + "id": "META-001", + "level": "required", + "status": "pass", + "message": "SDK manifest is present and declares the v1 contract", + "remediation": "Copy standards/v1/templates/sdk-manifest.yaml and fill every required field.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "META-002", + "level": "required", + "status": "pass", + "message": "package.json declares a public package identity and version", + "remediation": "Add a non-private package with a semver version and repository metadata.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-001", + "level": "required", + "status": "pass", + "message": "Chinese-default and English README files link to one another", + "remediation": "Add README.md and README.en.md with reciprocal language links.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-002", + "level": "required", + "status": "pass", + "message": "Chinese and English documentation directories are present", + "remediation": "Provide equivalent docs/zh-CN and docs/en quick-start/API guidance.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-003", + "level": "required", + "status": "pass", + "message": "README or package metadata exposes GitHub, npm, and live Demo links", + "remediation": "Add repository, package, and Demo URLs to README and manifest.", + "path": "README.md", + "evidence": "README.md" + }, + { + "id": "DEMO-001", + "level": "required", + "status": "pass", + "message": "A runnable standalone Demo entry exists", + "remediation": "Add a Demo app with a documented start/build command.", + "path": "apps/demo/index.html", + "evidence": "apps/demo/index.html" + }, + { + "id": "DEMO-002", + "level": "required", + "status": "pass", + "message": "Demo declares Chinese as its initial language", + "remediation": "Set lang or default language state to zh-CN.", + "path": "apps/demo/index.html", + "evidence": "apps/demo/index.html, apps/demo/src/App.tsx, apps/demo/src/i18n/en.ts, apps/demo/src/samples.ts, examples/cdn/index.html, examples/react/index.html, examples/vanilla-vite/index.html, examples/vue/index.html, examples/wechat-webview/index.html" + }, + { + "id": "DEMO-003", + "level": "required", + "status": "pass", + "message": "Demo provides an in-page Chinese/English toggle", + "remediation": "Add a language toggle that does not alter inference state.", + "path": "apps/demo/src/App.tsx", + "evidence": "apps/demo/src/App.tsx, apps/demo/src/i18n/en.ts, apps/demo/src/i18n/zh-CN.ts, apps/demo/src/samples.ts, apps/demo/src/styles.css, apps/demo/tests/cache.spec.ts, apps/demo/tests/demo.spec.ts" + }, + { + "id": "DEMO-004", + "level": "required", + "status": "pass", + "message": "Demo exposes current-model and global cache cleanup controls", + "remediation": "Wire clear-current and clear-all actions to the SDK cache API.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DEMO-005", + "level": "required", + "status": "pass", + "message": "Demo exposes model/runtime/timing information", + "remediation": "Render data-sdk-model-info, data-sdk-runtime-info, and data-sdk-timing regions.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "MODEL-001", + "level": "required", + "status": "pass", + "message": "Manifest declares model identity, precision, size, source, license, and checksum", + "remediation": "Complete the model and asset fields in sdk-manifest.yaml.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "RUNTIME-001", + "level": "required", + "status": "pass", + "message": "Requested/actual backend and execution mode are declared", + "remediation": "Add runtime backends, execution modes, and actual backend reporting.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "PERF-001", + "level": "required", + "status": "pass", + "message": "Standard load and inference timing fields are declared", + "remediation": "Document download, cache, integrity, session, inference, and total timing.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "CACHE-001", + "level": "required", + "status": "pass", + "message": "Cache storage, versioning, estimate, and cleanup are declared", + "remediation": "Add cache contract fields and expose cleanup controls in the Demo.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "EXAMPLE-001", + "level": "required", + "status": "pass", + "message": "Vanilla TypeScript/DOM portability example exists", + "remediation": "Add examples/vanilla with a runnable README.", + "path": "examples/vanilla-vite/README.md", + "evidence": "examples/vanilla-vite/README.md" + }, + { + "id": "EXAMPLE-002", + "level": "required", + "status": "pass", + "message": "React reference example exists", + "remediation": "Add examples/react or explicitly record a migration exception.", + "path": "examples/react/README.md", + "evidence": "examples/react/README.md" + }, + { + "id": "EXAMPLE-003", + "level": "recommended", + "status": "pass", + "message": "Declared target surfaces have runnable examples or explicit unsupported status", + "remediation": "Align examples with manifest target surfaces.", + "path": "examples/vanilla-vite/README.md", + "evidence": "examples/vanilla-vite/README.md, examples/react/README.md, examples/vue/README.md, examples/cdn/README.md, examples/wechat-webview/README.md" + }, + { + "id": "RELEASE-001", + "level": "required", + "status": "pass", + "message": "CI workflow runs verification before release", + "remediation": "Add .github/workflows/ci.yml with tests, typecheck, and build.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "RELEASE-002", + "level": "required", + "status": "pass", + "message": "GitHub Release workflow exists", + "remediation": "Add a tag-triggered release workflow and publish notes.", + "path": ".github/workflows/release.yml", + "evidence": ".github/workflows/release.yml" + }, + { + "id": "RELEASE-003", + "level": "recommended", + "status": "pass", + "message": "Changelog documents user-visible SDK and Demo changes", + "remediation": "Add CHANGELOG.md with the current release entry.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "GOV-001", + "level": "required", + "status": "skip", + "message": "The default branch has an active least-privilege Ruleset for pull requests, current CI checks, resolved conversations, deletion, and force-push protection", + "remediation": "Configure an active default-branch Ruleset using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "GOV-002", + "level": "required", + "status": "skip", + "message": "Release tags are immutable under an active tag Ruleset", + "remediation": "Protect the documented release tag pattern against updates and deletion, minimize bypass, and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "DEPLOY-001", + "level": "required", + "status": "skip", + "message": "The live Demo has a reproducible HTTPS deployment from protected source with least-privilege credentials", + "remediation": "Configure and verify a deployment that meets standards/v1/repository-governance-contract.md, then record its commit and deployment evidence.", + "evidence": "Requires read-only remote-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "PAGES-001", + "level": "required", + "status": "skip", + "message": "A GitHub Pages Demo deploys through GitHub Actions with scoped permissions, environment binding, HTTPS, and concurrency control", + "remediation": "Configure Pages and its deployment workflow using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "UI-001", + "level": "recommended", + "status": "pass", + "message": "Demo styling references the shared token vocabulary", + "remediation": "Import or copy standards/v1/ui-tokens.json values without introducing conflicting status colors.", + "path": "standards/v1/ui-tokens.json", + "evidence": "standards/v1/ui-tokens.json" + }, + { + "id": "LABS-001", + "level": "labs", + "status": "skip", + "message": "Experimental backends and NPU claims have explicit evidence and limits", + "remediation": "Mark WebNN/NPU as labs unless a dated verification matrix exists.", + "evidence": "Labs rule is informational" + } + ], + "current": [ + { + "id": "META-001", + "level": "required", + "status": "pass", + "message": "SDK manifest is present and declares the v1 contract", + "remediation": "Copy standards/v1/templates/sdk-manifest.yaml and fill every required field.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "META-002", + "level": "required", + "status": "pass", + "message": "package.json declares a public package identity and version", + "remediation": "Add a non-private package with a semver version and repository metadata.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-001", + "level": "required", + "status": "pass", + "message": "Chinese-default and English README files link to one another", + "remediation": "Add README.md and README.en.md with reciprocal language links.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-002", + "level": "required", + "status": "pass", + "message": "Chinese and English documentation directories are present", + "remediation": "Provide equivalent docs/zh-CN and docs/en quick-start/API guidance.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-003", + "level": "required", + "status": "pass", + "message": "README or package metadata exposes GitHub, npm, and live Demo links", + "remediation": "Add repository, package, and Demo URLs to README and manifest.", + "path": "README.md", + "evidence": "README.md" + }, + { + "id": "DEMO-001", + "level": "required", + "status": "pass", + "message": "A runnable standalone Demo entry exists", + "remediation": "Add a Demo app with a documented start/build command.", + "path": "apps/demo/index.html", + "evidence": "apps/demo/index.html" + }, + { + "id": "DEMO-002", + "level": "required", + "status": "pass", + "message": "Demo declares Chinese as its initial language", + "remediation": "Set lang or default language state to zh-CN.", + "path": "apps/demo/index.html", + "evidence": "apps/demo/index.html, apps/demo/src/App.tsx, apps/demo/src/i18n/en.ts, apps/demo/src/samples.ts, examples/cdn/index.html, examples/react/index.html, examples/vanilla-vite/index.html, examples/vue/index.html, examples/wechat-webview/index.html" + }, + { + "id": "DEMO-003", + "level": "required", + "status": "pass", + "message": "Demo provides an in-page Chinese/English toggle", + "remediation": "Add a language toggle that does not alter inference state.", + "path": "apps/demo/src/App.tsx", + "evidence": "apps/demo/src/App.tsx, apps/demo/src/i18n/en.ts, apps/demo/src/i18n/zh-CN.ts, apps/demo/src/samples.ts, apps/demo/src/styles.css, apps/demo/tests/cache.spec.ts, apps/demo/tests/demo.spec.ts" + }, + { + "id": "DEMO-004", + "level": "required", + "status": "pass", + "message": "Demo exposes current-model and global cache cleanup controls", + "remediation": "Wire clear-current and clear-all actions to the SDK cache API.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DEMO-005", + "level": "required", + "status": "pass", + "message": "Demo 展示当前模型或算法的信息、运行信息和耗时", + "remediation": "模型使用 data-sdk-model-info,算法使用 data-sdk-algorithm-info;两者保留 data-sdk-runtime-info 和 data-sdk-timing。", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DEMO-006", + "level": "required", + "status": "skip", + "message": "算法 Demo 提供状态复位操作", + "remediation": "使用 data-sdk-state-reset 标记复位控件,并按状态生命周期契约连接 reset。", + "path": "sdk-manifest.yaml", + "evidence": "当前类型为 model,本规则仅适用于 algorithm;清单已通过完整校验。" + }, + { + "id": "ALGORITHM-001", + "level": "required", + "status": "skip", + "message": "算法清单声明身份、家族、来源、许可、输入输出与有状态语义", + "remediation": "按 sdk-manifest.algorithm.yaml 填写 algorithm 字段并记录状态生命周期。", + "path": "sdk-manifest.yaml", + "evidence": "当前类型为 model,本规则仅适用于 algorithm;清单已通过完整校验。" + }, + { + "id": "MODEL-001", + "level": "required", + "status": "pass", + "message": "Manifest declares model identity, precision, size, source, license, and checksum", + "remediation": "Complete the model and asset fields in sdk-manifest.yaml.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "RUNTIME-001", + "level": "required", + "status": "pass", + "message": "Requested/actual backend and execution mode are declared", + "remediation": "Add runtime backends, execution modes, and actual backend reporting.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "PERF-001", + "level": "required", + "status": "pass", + "message": "声明当前 SDK 类型所需的标准耗时字段", + "remediation": "模型保留下载、缓存、校验、会话、推理和总耗时;算法声明 validationMs、predictionMs、associationMs、updateMs、totalMs。", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "CACHE-001", + "level": "required", + "status": "pass", + "message": "Cache storage, versioning, estimate, and cleanup are declared", + "remediation": "Add cache contract fields and expose cleanup controls in the Demo.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "EXAMPLE-001", + "level": "required", + "status": "pass", + "message": "Vanilla TypeScript/DOM portability example exists", + "remediation": "Add examples/vanilla with a runnable README.", + "path": "examples/vanilla-vite/README.md", + "evidence": "examples/vanilla-vite/README.md" + }, + { + "id": "EXAMPLE-002", + "level": "required", + "status": "pass", + "message": "React reference example exists", + "remediation": "Add examples/react or explicitly record a migration exception.", + "path": "examples/react/README.md", + "evidence": "examples/react/README.md" + }, + { + "id": "EXAMPLE-003", + "level": "recommended", + "status": "pass", + "message": "Declared target surfaces have runnable examples or explicit unsupported status", + "remediation": "Align examples with manifest target surfaces.", + "path": "examples/vanilla-vite/README.md", + "evidence": "examples/vanilla-vite/README.md, examples/react/README.md, examples/vue/README.md, examples/cdn/README.md, examples/wechat-webview/README.md" + }, + { + "id": "RELEASE-001", + "level": "required", + "status": "pass", + "message": "CI workflow runs verification before release", + "remediation": "Add .github/workflows/ci.yml with tests, typecheck, and build.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "RELEASE-002", + "level": "required", + "status": "pass", + "message": "GitHub Release workflow exists", + "remediation": "Add a tag-triggered release workflow and publish notes.", + "path": ".github/workflows/release.yml", + "evidence": ".github/workflows/release.yml" + }, + { + "id": "RELEASE-003", + "level": "recommended", + "status": "pass", + "message": "Changelog documents user-visible SDK and Demo changes", + "remediation": "Add CHANGELOG.md with the current release entry.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "GOV-001", + "level": "required", + "status": "skip", + "message": "The default branch has an active least-privilege Ruleset for pull requests, current CI checks, resolved conversations, deletion, and force-push protection", + "remediation": "Configure an active default-branch Ruleset using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "GOV-002", + "level": "required", + "status": "skip", + "message": "Release tags are immutable under an active tag Ruleset", + "remediation": "Protect the documented release tag pattern against updates and deletion, minimize bypass, and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "DEPLOY-001", + "level": "required", + "status": "skip", + "message": "The live Demo has a reproducible HTTPS deployment from protected source with least-privilege credentials", + "remediation": "Configure and verify a deployment that meets standards/v1/repository-governance-contract.md, then record its commit and deployment evidence.", + "evidence": "Requires read-only remote-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "PAGES-001", + "level": "required", + "status": "skip", + "message": "A GitHub Pages Demo deploys through GitHub Actions with scoped permissions, environment binding, HTTPS, and concurrency control", + "remediation": "Configure Pages and its deployment workflow using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "UI-001", + "level": "recommended", + "status": "pass", + "message": "Demo styling references the shared token vocabulary", + "remediation": "Import or copy standards/v1/ui-tokens.json values without introducing conflicting status colors.", + "path": "standards/v1/ui-tokens.json", + "evidence": "standards/v1/ui-tokens.json" + }, + { + "id": "LABS-001", + "level": "labs", + "status": "skip", + "message": "Experimental backends and NPU claims have explicit evidence and limits", + "remediation": "Mark WebNN/NPU as labs unless a dated verification matrix exists.", + "evidence": "Labs rule is informational" + } + ] + }, + { + "repository": "web-sdk-PP-LCNet_x1_0_doc_ori", + "commit": "0e404877890ab79442f3db43b44aaf726b81e131", + "changes": [], + "baselineFailures": [], + "currentFailures": [], + "baseline": [ + { + "id": "META-001", + "level": "required", + "status": "pass", + "message": "SDK manifest is present and declares the v1 contract", + "remediation": "Copy standards/v1/templates/sdk-manifest.yaml and fill every required field.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "META-002", + "level": "required", + "status": "pass", + "message": "package.json declares a public package identity and version", + "remediation": "Add a non-private package with a semver version and repository metadata.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-001", + "level": "required", + "status": "pass", + "message": "Chinese-default and English README files link to one another", + "remediation": "Add README.md and README.en.md with reciprocal language links.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-002", + "level": "required", + "status": "pass", + "message": "Chinese and English documentation directories are present", + "remediation": "Provide equivalent docs/zh-CN and docs/en quick-start/API guidance.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-003", + "level": "required", + "status": "pass", + "message": "README or package metadata exposes GitHub, npm, and live Demo links", + "remediation": "Add repository, package, and Demo URLs to README and manifest.", + "path": "README.md", + "evidence": "README.md" + }, + { + "id": "DEMO-001", + "level": "required", + "status": "pass", + "message": "A runnable standalone Demo entry exists", + "remediation": "Add a Demo app with a documented start/build command.", + "path": "apps/demo/index.html", + "evidence": "apps/demo/index.html" + }, + { + "id": "DEMO-002", + "level": "required", + "status": "pass", + "message": "Demo declares Chinese as its initial language", + "remediation": "Set lang or default language state to zh-CN.", + "path": "apps/demo/index.html", + "evidence": "apps/demo/index.html, apps/demo/src/i18n/en.ts, apps/demo/src/i18n/index.ts, apps/demo/src/i18n/types.ts, apps/demo/src/i18n/zh-CN.ts, apps/demo/src/main.ts, examples/react/index.html, examples/react/src/App.tsx, examples/vanilla-vite/index.html" + }, + { + "id": "DEMO-003", + "level": "required", + "status": "pass", + "message": "Demo provides an in-page Chinese/English toggle", + "remediation": "Add a language toggle that does not alter inference state.", + "path": "apps/demo/src/i18n/en.ts", + "evidence": "apps/demo/src/i18n/en.ts, apps/demo/src/i18n/index.ts, apps/demo/src/i18n/types.ts, apps/demo/src/i18n/zh-CN.ts, apps/demo/src/main.ts, apps/demo/src/render.ts, apps/demo/src/styles.css, apps/demo/tests/cache-standard.spec.ts, apps/demo/tests/demo.spec.ts, examples/react/src/App.tsx" + }, + { + "id": "DEMO-004", + "level": "required", + "status": "pass", + "message": "Demo exposes current-model and global cache cleanup controls", + "remediation": "Wire clear-current and clear-all actions to the SDK cache API.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DEMO-005", + "level": "required", + "status": "pass", + "message": "Demo exposes model/runtime/timing information", + "remediation": "Render data-sdk-model-info, data-sdk-runtime-info, and data-sdk-timing regions.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "MODEL-001", + "level": "required", + "status": "pass", + "message": "Manifest declares model identity, precision, size, source, license, and checksum", + "remediation": "Complete the model and asset fields in sdk-manifest.yaml.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "RUNTIME-001", + "level": "required", + "status": "pass", + "message": "Requested/actual backend and execution mode are declared", + "remediation": "Add runtime backends, execution modes, and actual backend reporting.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "PERF-001", + "level": "required", + "status": "pass", + "message": "Standard load and inference timing fields are declared", + "remediation": "Document download, cache, integrity, session, inference, and total timing.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "CACHE-001", + "level": "required", + "status": "pass", + "message": "Cache storage, versioning, estimate, and cleanup are declared", + "remediation": "Add cache contract fields and expose cleanup controls in the Demo.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "EXAMPLE-001", + "level": "required", + "status": "pass", + "message": "Vanilla TypeScript/DOM portability example exists", + "remediation": "Add examples/vanilla with a runnable README.", + "path": "examples/vanilla-vite/README.md", + "evidence": "examples/vanilla-vite/README.md" + }, + { + "id": "EXAMPLE-002", + "level": "required", + "status": "pass", + "message": "React reference example exists", + "remediation": "Add examples/react or explicitly record a migration exception.", + "path": "examples/react/README.md", + "evidence": "examples/react/README.md" + }, + { + "id": "EXAMPLE-003", + "level": "recommended", + "status": "pass", + "message": "Declared target surfaces have runnable examples or explicit unsupported status", + "remediation": "Align examples with manifest target surfaces.", + "path": "examples/vanilla-vite/README.md", + "evidence": "examples/vanilla-vite/README.md, examples/react/README.md, examples/cdn/README.md, examples/wechat-web-view/README.md" + }, + { + "id": "RELEASE-001", + "level": "required", + "status": "pass", + "message": "CI workflow runs verification before release", + "remediation": "Add .github/workflows/ci.yml with tests, typecheck, and build.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "RELEASE-002", + "level": "required", + "status": "pass", + "message": "GitHub Release workflow exists", + "remediation": "Add a tag-triggered release workflow and publish notes.", + "path": ".github/workflows/release.yml", + "evidence": ".github/workflows/release.yml" + }, + { + "id": "RELEASE-003", + "level": "recommended", + "status": "pass", + "message": "Changelog documents user-visible SDK and Demo changes", + "remediation": "Add CHANGELOG.md with the current release entry.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "GOV-001", + "level": "required", + "status": "skip", + "message": "The default branch has an active least-privilege Ruleset for pull requests, current CI checks, resolved conversations, deletion, and force-push protection", + "remediation": "Configure an active default-branch Ruleset using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "GOV-002", + "level": "required", + "status": "skip", + "message": "Release tags are immutable under an active tag Ruleset", + "remediation": "Protect the documented release tag pattern against updates and deletion, minimize bypass, and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "DEPLOY-001", + "level": "required", + "status": "skip", + "message": "The live Demo has a reproducible HTTPS deployment from protected source with least-privilege credentials", + "remediation": "Configure and verify a deployment that meets standards/v1/repository-governance-contract.md, then record its commit and deployment evidence.", + "evidence": "Requires read-only remote-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "PAGES-001", + "level": "required", + "status": "skip", + "message": "A GitHub Pages Demo deploys through GitHub Actions with scoped permissions, environment binding, HTTPS, and concurrency control", + "remediation": "Configure Pages and its deployment workflow using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "UI-001", + "level": "recommended", + "status": "pass", + "message": "Demo styling references the shared token vocabulary", + "remediation": "Import or copy standards/v1/ui-tokens.json values without introducing conflicting status colors.", + "path": "standards/v1/ui-tokens.json", + "evidence": "standards/v1/ui-tokens.json" + }, + { + "id": "LABS-001", + "level": "labs", + "status": "skip", + "message": "Experimental backends and NPU claims have explicit evidence and limits", + "remediation": "Mark WebNN/NPU as labs unless a dated verification matrix exists.", + "evidence": "Labs rule is informational" + } + ], + "current": [ + { + "id": "META-001", + "level": "required", + "status": "pass", + "message": "SDK manifest is present and declares the v1 contract", + "remediation": "Copy standards/v1/templates/sdk-manifest.yaml and fill every required field.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "META-002", + "level": "required", + "status": "pass", + "message": "package.json declares a public package identity and version", + "remediation": "Add a non-private package with a semver version and repository metadata.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-001", + "level": "required", + "status": "pass", + "message": "Chinese-default and English README files link to one another", + "remediation": "Add README.md and README.en.md with reciprocal language links.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-002", + "level": "required", + "status": "pass", + "message": "Chinese and English documentation directories are present", + "remediation": "Provide equivalent docs/zh-CN and docs/en quick-start/API guidance.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-003", + "level": "required", + "status": "pass", + "message": "README or package metadata exposes GitHub, npm, and live Demo links", + "remediation": "Add repository, package, and Demo URLs to README and manifest.", + "path": "README.md", + "evidence": "README.md" + }, + { + "id": "DEMO-001", + "level": "required", + "status": "pass", + "message": "A runnable standalone Demo entry exists", + "remediation": "Add a Demo app with a documented start/build command.", + "path": "apps/demo/index.html", + "evidence": "apps/demo/index.html" + }, + { + "id": "DEMO-002", + "level": "required", + "status": "pass", + "message": "Demo declares Chinese as its initial language", + "remediation": "Set lang or default language state to zh-CN.", + "path": "apps/demo/index.html", + "evidence": "apps/demo/index.html, apps/demo/src/i18n/en.ts, apps/demo/src/i18n/index.ts, apps/demo/src/i18n/types.ts, apps/demo/src/i18n/zh-CN.ts, apps/demo/src/main.ts, examples/react/index.html, examples/react/src/App.tsx, examples/vanilla-vite/index.html" + }, + { + "id": "DEMO-003", + "level": "required", + "status": "pass", + "message": "Demo provides an in-page Chinese/English toggle", + "remediation": "Add a language toggle that does not alter inference state.", + "path": "apps/demo/src/i18n/en.ts", + "evidence": "apps/demo/src/i18n/en.ts, apps/demo/src/i18n/index.ts, apps/demo/src/i18n/types.ts, apps/demo/src/i18n/zh-CN.ts, apps/demo/src/main.ts, apps/demo/src/render.ts, apps/demo/src/styles.css, apps/demo/tests/cache-standard.spec.ts, apps/demo/tests/demo.spec.ts, examples/react/src/App.tsx" + }, + { + "id": "DEMO-004", + "level": "required", + "status": "pass", + "message": "Demo exposes current-model and global cache cleanup controls", + "remediation": "Wire clear-current and clear-all actions to the SDK cache API.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DEMO-005", + "level": "required", + "status": "pass", + "message": "Demo 展示当前模型或算法的信息、运行信息和耗时", + "remediation": "模型使用 data-sdk-model-info,算法使用 data-sdk-algorithm-info;两者保留 data-sdk-runtime-info 和 data-sdk-timing。", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DEMO-006", + "level": "required", + "status": "skip", + "message": "算法 Demo 提供状态复位操作", + "remediation": "使用 data-sdk-state-reset 标记复位控件,并按状态生命周期契约连接 reset。", + "path": "sdk-manifest.yaml", + "evidence": "当前类型为 model,本规则仅适用于 algorithm;清单已通过完整校验。" + }, + { + "id": "ALGORITHM-001", + "level": "required", + "status": "skip", + "message": "算法清单声明身份、家族、来源、许可、输入输出与有状态语义", + "remediation": "按 sdk-manifest.algorithm.yaml 填写 algorithm 字段并记录状态生命周期。", + "path": "sdk-manifest.yaml", + "evidence": "当前类型为 model,本规则仅适用于 algorithm;清单已通过完整校验。" + }, + { + "id": "MODEL-001", + "level": "required", + "status": "pass", + "message": "Manifest declares model identity, precision, size, source, license, and checksum", + "remediation": "Complete the model and asset fields in sdk-manifest.yaml.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "RUNTIME-001", + "level": "required", + "status": "pass", + "message": "Requested/actual backend and execution mode are declared", + "remediation": "Add runtime backends, execution modes, and actual backend reporting.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "PERF-001", + "level": "required", + "status": "pass", + "message": "声明当前 SDK 类型所需的标准耗时字段", + "remediation": "模型保留下载、缓存、校验、会话、推理和总耗时;算法声明 validationMs、predictionMs、associationMs、updateMs、totalMs。", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "CACHE-001", + "level": "required", + "status": "pass", + "message": "Cache storage, versioning, estimate, and cleanup are declared", + "remediation": "Add cache contract fields and expose cleanup controls in the Demo.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "EXAMPLE-001", + "level": "required", + "status": "pass", + "message": "Vanilla TypeScript/DOM portability example exists", + "remediation": "Add examples/vanilla with a runnable README.", + "path": "examples/vanilla-vite/README.md", + "evidence": "examples/vanilla-vite/README.md" + }, + { + "id": "EXAMPLE-002", + "level": "required", + "status": "pass", + "message": "React reference example exists", + "remediation": "Add examples/react or explicitly record a migration exception.", + "path": "examples/react/README.md", + "evidence": "examples/react/README.md" + }, + { + "id": "EXAMPLE-003", + "level": "recommended", + "status": "pass", + "message": "Declared target surfaces have runnable examples or explicit unsupported status", + "remediation": "Align examples with manifest target surfaces.", + "path": "examples/vanilla-vite/README.md", + "evidence": "examples/vanilla-vite/README.md, examples/react/README.md, examples/cdn/README.md, examples/wechat-web-view/README.md" + }, + { + "id": "RELEASE-001", + "level": "required", + "status": "pass", + "message": "CI workflow runs verification before release", + "remediation": "Add .github/workflows/ci.yml with tests, typecheck, and build.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "RELEASE-002", + "level": "required", + "status": "pass", + "message": "GitHub Release workflow exists", + "remediation": "Add a tag-triggered release workflow and publish notes.", + "path": ".github/workflows/release.yml", + "evidence": ".github/workflows/release.yml" + }, + { + "id": "RELEASE-003", + "level": "recommended", + "status": "pass", + "message": "Changelog documents user-visible SDK and Demo changes", + "remediation": "Add CHANGELOG.md with the current release entry.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "GOV-001", + "level": "required", + "status": "skip", + "message": "The default branch has an active least-privilege Ruleset for pull requests, current CI checks, resolved conversations, deletion, and force-push protection", + "remediation": "Configure an active default-branch Ruleset using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "GOV-002", + "level": "required", + "status": "skip", + "message": "Release tags are immutable under an active tag Ruleset", + "remediation": "Protect the documented release tag pattern against updates and deletion, minimize bypass, and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "DEPLOY-001", + "level": "required", + "status": "skip", + "message": "The live Demo has a reproducible HTTPS deployment from protected source with least-privilege credentials", + "remediation": "Configure and verify a deployment that meets standards/v1/repository-governance-contract.md, then record its commit and deployment evidence.", + "evidence": "Requires read-only remote-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "PAGES-001", + "level": "required", + "status": "skip", + "message": "A GitHub Pages Demo deploys through GitHub Actions with scoped permissions, environment binding, HTTPS, and concurrency control", + "remediation": "Configure Pages and its deployment workflow using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "UI-001", + "level": "recommended", + "status": "pass", + "message": "Demo styling references the shared token vocabulary", + "remediation": "Import or copy standards/v1/ui-tokens.json values without introducing conflicting status colors.", + "path": "standards/v1/ui-tokens.json", + "evidence": "standards/v1/ui-tokens.json" + }, + { + "id": "LABS-001", + "level": "labs", + "status": "skip", + "message": "Experimental backends and NPU claims have explicit evidence and limits", + "remediation": "Mark WebNN/NPU as labs unless a dated verification matrix exists.", + "evidence": "Labs rule is informational" + } + ] + }, + { + "repository": "web-sdk-PP-OCRv6", + "commit": "746a44d0dc59b2b21496dc6853cea56adeb1b17b", + "changes": [], + "baselineFailures": [], + "currentFailures": [], + "baseline": [ + { + "id": "META-001", + "level": "required", + "status": "pass", + "message": "SDK manifest is present and declares the v1 contract", + "remediation": "Copy standards/v1/templates/sdk-manifest.yaml and fill every required field.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "META-002", + "level": "required", + "status": "pass", + "message": "package.json declares a public package identity and version", + "remediation": "Add a non-private package with a semver version and repository metadata.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-001", + "level": "required", + "status": "pass", + "message": "Chinese-default and English README files link to one another", + "remediation": "Add README.md and README.en.md with reciprocal language links.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-002", + "level": "required", + "status": "pass", + "message": "Chinese and English documentation directories are present", + "remediation": "Provide equivalent docs/zh-CN and docs/en quick-start/API guidance.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-003", + "level": "required", + "status": "pass", + "message": "README or package metadata exposes GitHub, npm, and live Demo links", + "remediation": "Add repository, package, and Demo URLs to README and manifest.", + "path": "README.md", + "evidence": "README.md" + }, + { + "id": "DEMO-001", + "level": "required", + "status": "pass", + "message": "A runnable standalone Demo entry exists", + "remediation": "Add a Demo app with a documented start/build command.", + "path": "apps/demo/index.html", + "evidence": "apps/demo/index.html" + }, + { + "id": "DEMO-002", + "level": "required", + "status": "pass", + "message": "Demo declares Chinese as its initial language", + "remediation": "Set lang or default language state to zh-CN.", + "path": "apps/demo/index.html", + "evidence": "apps/demo/index.html, apps/demo/src/App.tsx, apps/demo/src/i18n/en.ts, apps/demo/tests/interaction-regression.spec.ts, examples/cdn/index.html, examples/react/index.html, examples/vanilla/index.html, examples/vite/index.html, examples/wechat-web-view/index.html" + }, + { + "id": "DEMO-003", + "level": "required", + "status": "pass", + "message": "Demo provides an in-page Chinese/English toggle", + "remediation": "Add a language toggle that does not alter inference state.", + "path": "apps/demo/src/App.tsx", + "evidence": "apps/demo/src/App.tsx, apps/demo/src/i18n/en.ts, apps/demo/src/i18n/zh-CN.ts, apps/demo/tests/demo.spec.ts, apps/demo/tests/interaction-regression.spec.ts" + }, + { + "id": "DEMO-004", + "level": "required", + "status": "pass", + "message": "Demo exposes current-model and global cache cleanup controls", + "remediation": "Wire clear-current and clear-all actions to the SDK cache API.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DEMO-005", + "level": "required", + "status": "pass", + "message": "Demo exposes model/runtime/timing information", + "remediation": "Render data-sdk-model-info, data-sdk-runtime-info, and data-sdk-timing regions.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "MODEL-001", + "level": "required", + "status": "pass", + "message": "Manifest declares model identity, precision, size, source, license, and checksum", + "remediation": "Complete the model and asset fields in sdk-manifest.yaml.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "RUNTIME-001", + "level": "required", + "status": "pass", + "message": "Requested/actual backend and execution mode are declared", + "remediation": "Add runtime backends, execution modes, and actual backend reporting.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "PERF-001", + "level": "required", + "status": "pass", + "message": "Standard load and inference timing fields are declared", + "remediation": "Document download, cache, integrity, session, inference, and total timing.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "CACHE-001", + "level": "required", + "status": "pass", + "message": "Cache storage, versioning, estimate, and cleanup are declared", + "remediation": "Add cache contract fields and expose cleanup controls in the Demo.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "EXAMPLE-001", + "level": "required", + "status": "pass", + "message": "Vanilla TypeScript/DOM portability example exists", + "remediation": "Add examples/vanilla with a runnable README.", + "path": "examples/vanilla/README.md", + "evidence": "examples/vanilla/README.md" + }, + { + "id": "EXAMPLE-002", + "level": "required", + "status": "pass", + "message": "React reference example exists", + "remediation": "Add examples/react or explicitly record a migration exception.", + "path": "examples/react/App.tsx", + "evidence": "examples/react/App.tsx" + }, + { + "id": "EXAMPLE-003", + "level": "recommended", + "status": "pass", + "message": "Declared target surfaces have runnable examples or explicit unsupported status", + "remediation": "Align examples with manifest target surfaces.", + "path": "examples/vanilla/README.md", + "evidence": "examples/vanilla/README.md, examples/react/App.tsx, examples/cdn/README.md, examples/vite/README.md, examples/wechat-web-view/README.md" + }, + { + "id": "RELEASE-001", + "level": "required", + "status": "pass", + "message": "CI workflow runs verification before release", + "remediation": "Add .github/workflows/ci.yml with tests, typecheck, and build.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "RELEASE-002", + "level": "required", + "status": "pass", + "message": "GitHub Release workflow exists", + "remediation": "Add a tag-triggered release workflow and publish notes.", + "path": ".github/workflows/release.yml", + "evidence": ".github/workflows/release.yml" + }, + { + "id": "RELEASE-003", + "level": "recommended", + "status": "pass", + "message": "Changelog documents user-visible SDK and Demo changes", + "remediation": "Add CHANGELOG.md with the current release entry.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "GOV-001", + "level": "required", + "status": "skip", + "message": "The default branch has an active least-privilege Ruleset for pull requests, current CI checks, resolved conversations, deletion, and force-push protection", + "remediation": "Configure an active default-branch Ruleset using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "GOV-002", + "level": "required", + "status": "skip", + "message": "Release tags are immutable under an active tag Ruleset", + "remediation": "Protect the documented release tag pattern against updates and deletion, minimize bypass, and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "DEPLOY-001", + "level": "required", + "status": "skip", + "message": "The live Demo has a reproducible HTTPS deployment from protected source with least-privilege credentials", + "remediation": "Configure and verify a deployment that meets standards/v1/repository-governance-contract.md, then record its commit and deployment evidence.", + "evidence": "Requires read-only remote-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "PAGES-001", + "level": "required", + "status": "skip", + "message": "A GitHub Pages Demo deploys through GitHub Actions with scoped permissions, environment binding, HTTPS, and concurrency control", + "remediation": "Configure Pages and its deployment workflow using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "UI-001", + "level": "recommended", + "status": "pass", + "message": "Demo styling references the shared token vocabulary", + "remediation": "Import or copy standards/v1/ui-tokens.json values without introducing conflicting status colors.", + "path": "standards/v1/ui-tokens.json", + "evidence": "standards/v1/ui-tokens.json" + }, + { + "id": "LABS-001", + "level": "labs", + "status": "skip", + "message": "Experimental backends and NPU claims have explicit evidence and limits", + "remediation": "Mark WebNN/NPU as labs unless a dated verification matrix exists.", + "evidence": "Labs rule is informational" + } + ], + "current": [ + { + "id": "META-001", + "level": "required", + "status": "pass", + "message": "SDK manifest is present and declares the v1 contract", + "remediation": "Copy standards/v1/templates/sdk-manifest.yaml and fill every required field.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "META-002", + "level": "required", + "status": "pass", + "message": "package.json declares a public package identity and version", + "remediation": "Add a non-private package with a semver version and repository metadata.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-001", + "level": "required", + "status": "pass", + "message": "Chinese-default and English README files link to one another", + "remediation": "Add README.md and README.en.md with reciprocal language links.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-002", + "level": "required", + "status": "pass", + "message": "Chinese and English documentation directories are present", + "remediation": "Provide equivalent docs/zh-CN and docs/en quick-start/API guidance.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-003", + "level": "required", + "status": "pass", + "message": "README or package metadata exposes GitHub, npm, and live Demo links", + "remediation": "Add repository, package, and Demo URLs to README and manifest.", + "path": "README.md", + "evidence": "README.md" + }, + { + "id": "DEMO-001", + "level": "required", + "status": "pass", + "message": "A runnable standalone Demo entry exists", + "remediation": "Add a Demo app with a documented start/build command.", + "path": "apps/demo/index.html", + "evidence": "apps/demo/index.html" + }, + { + "id": "DEMO-002", + "level": "required", + "status": "pass", + "message": "Demo declares Chinese as its initial language", + "remediation": "Set lang or default language state to zh-CN.", + "path": "apps/demo/index.html", + "evidence": "apps/demo/index.html, apps/demo/src/App.tsx, apps/demo/src/i18n/en.ts, apps/demo/tests/interaction-regression.spec.ts, examples/cdn/index.html, examples/react/index.html, examples/vanilla/index.html, examples/vite/index.html, examples/wechat-web-view/index.html" + }, + { + "id": "DEMO-003", + "level": "required", + "status": "pass", + "message": "Demo provides an in-page Chinese/English toggle", + "remediation": "Add a language toggle that does not alter inference state.", + "path": "apps/demo/src/App.tsx", + "evidence": "apps/demo/src/App.tsx, apps/demo/src/i18n/en.ts, apps/demo/src/i18n/zh-CN.ts, apps/demo/tests/demo.spec.ts, apps/demo/tests/interaction-regression.spec.ts" + }, + { + "id": "DEMO-004", + "level": "required", + "status": "pass", + "message": "Demo exposes current-model and global cache cleanup controls", + "remediation": "Wire clear-current and clear-all actions to the SDK cache API.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DEMO-005", + "level": "required", + "status": "pass", + "message": "Demo 展示当前模型或算法的信息、运行信息和耗时", + "remediation": "模型使用 data-sdk-model-info,算法使用 data-sdk-algorithm-info;两者保留 data-sdk-runtime-info 和 data-sdk-timing。", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DEMO-006", + "level": "required", + "status": "skip", + "message": "算法 Demo 提供状态复位操作", + "remediation": "使用 data-sdk-state-reset 标记复位控件,并按状态生命周期契约连接 reset。", + "path": "sdk-manifest.yaml", + "evidence": "当前类型为 model,本规则仅适用于 algorithm;清单已通过完整校验。" + }, + { + "id": "ALGORITHM-001", + "level": "required", + "status": "skip", + "message": "算法清单声明身份、家族、来源、许可、输入输出与有状态语义", + "remediation": "按 sdk-manifest.algorithm.yaml 填写 algorithm 字段并记录状态生命周期。", + "path": "sdk-manifest.yaml", + "evidence": "当前类型为 model,本规则仅适用于 algorithm;清单已通过完整校验。" + }, + { + "id": "MODEL-001", + "level": "required", + "status": "pass", + "message": "Manifest declares model identity, precision, size, source, license, and checksum", + "remediation": "Complete the model and asset fields in sdk-manifest.yaml.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "RUNTIME-001", + "level": "required", + "status": "pass", + "message": "Requested/actual backend and execution mode are declared", + "remediation": "Add runtime backends, execution modes, and actual backend reporting.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "PERF-001", + "level": "required", + "status": "pass", + "message": "声明当前 SDK 类型所需的标准耗时字段", + "remediation": "模型保留下载、缓存、校验、会话、推理和总耗时;算法声明 validationMs、predictionMs、associationMs、updateMs、totalMs。", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "CACHE-001", + "level": "required", + "status": "pass", + "message": "Cache storage, versioning, estimate, and cleanup are declared", + "remediation": "Add cache contract fields and expose cleanup controls in the Demo.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "EXAMPLE-001", + "level": "required", + "status": "pass", + "message": "Vanilla TypeScript/DOM portability example exists", + "remediation": "Add examples/vanilla with a runnable README.", + "path": "examples/vanilla/README.md", + "evidence": "examples/vanilla/README.md" + }, + { + "id": "EXAMPLE-002", + "level": "required", + "status": "pass", + "message": "React reference example exists", + "remediation": "Add examples/react or explicitly record a migration exception.", + "path": "examples/react/App.tsx", + "evidence": "examples/react/App.tsx" + }, + { + "id": "EXAMPLE-003", + "level": "recommended", + "status": "pass", + "message": "Declared target surfaces have runnable examples or explicit unsupported status", + "remediation": "Align examples with manifest target surfaces.", + "path": "examples/vanilla/README.md", + "evidence": "examples/vanilla/README.md, examples/react/App.tsx, examples/cdn/README.md, examples/vite/README.md, examples/wechat-web-view/README.md" + }, + { + "id": "RELEASE-001", + "level": "required", + "status": "pass", + "message": "CI workflow runs verification before release", + "remediation": "Add .github/workflows/ci.yml with tests, typecheck, and build.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "RELEASE-002", + "level": "required", + "status": "pass", + "message": "GitHub Release workflow exists", + "remediation": "Add a tag-triggered release workflow and publish notes.", + "path": ".github/workflows/release.yml", + "evidence": ".github/workflows/release.yml" + }, + { + "id": "RELEASE-003", + "level": "recommended", + "status": "pass", + "message": "Changelog documents user-visible SDK and Demo changes", + "remediation": "Add CHANGELOG.md with the current release entry.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "GOV-001", + "level": "required", + "status": "skip", + "message": "The default branch has an active least-privilege Ruleset for pull requests, current CI checks, resolved conversations, deletion, and force-push protection", + "remediation": "Configure an active default-branch Ruleset using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "GOV-002", + "level": "required", + "status": "skip", + "message": "Release tags are immutable under an active tag Ruleset", + "remediation": "Protect the documented release tag pattern against updates and deletion, minimize bypass, and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "DEPLOY-001", + "level": "required", + "status": "skip", + "message": "The live Demo has a reproducible HTTPS deployment from protected source with least-privilege credentials", + "remediation": "Configure and verify a deployment that meets standards/v1/repository-governance-contract.md, then record its commit and deployment evidence.", + "evidence": "Requires read-only remote-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "PAGES-001", + "level": "required", + "status": "skip", + "message": "A GitHub Pages Demo deploys through GitHub Actions with scoped permissions, environment binding, HTTPS, and concurrency control", + "remediation": "Configure Pages and its deployment workflow using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "UI-001", + "level": "recommended", + "status": "pass", + "message": "Demo styling references the shared token vocabulary", + "remediation": "Import or copy standards/v1/ui-tokens.json values without introducing conflicting status colors.", + "path": "standards/v1/ui-tokens.json", + "evidence": "standards/v1/ui-tokens.json" + }, + { + "id": "LABS-001", + "level": "labs", + "status": "skip", + "message": "Experimental backends and NPU claims have explicit evidence and limits", + "remediation": "Mark WebNN/NPU as labs unless a dated verification matrix exists.", + "evidence": "Labs rule is informational" + } + ] + }, + { + "repository": "web-sdk-PP-RotatedDetection", + "commit": "b54ae15ca124fd111cac6e683409fdbb88a14e13", + "changes": [], + "baselineFailures": [], + "currentFailures": [], + "baseline": [ + { + "id": "META-001", + "level": "required", + "status": "pass", + "message": "SDK manifest is present and declares the v1 contract", + "remediation": "Copy standards/v1/templates/sdk-manifest.yaml and fill every required field.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "META-002", + "level": "required", + "status": "pass", + "message": "package.json declares a public package identity and version", + "remediation": "Add a non-private package with a semver version and repository metadata.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-001", + "level": "required", + "status": "pass", + "message": "Chinese-default and English README files link to one another", + "remediation": "Add README.md and README.en.md with reciprocal language links.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-002", + "level": "required", + "status": "pass", + "message": "Chinese and English documentation directories are present", + "remediation": "Provide equivalent docs/zh-CN and docs/en quick-start/API guidance.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-003", + "level": "required", + "status": "pass", + "message": "README or package metadata exposes GitHub, npm, and live Demo links", + "remediation": "Add repository, package, and Demo URLs to README and manifest.", + "path": "README.md", + "evidence": "README.md" + }, + { + "id": "DEMO-001", + "level": "required", + "status": "pass", + "message": "A runnable standalone Demo entry exists", + "remediation": "Add a Demo app with a documented start/build command.", + "path": "demo/index.html", + "evidence": "demo/index.html" + }, + { + "id": "DEMO-002", + "level": "required", + "status": "pass", + "message": "Demo declares Chinese as its initial language", + "remediation": "Set lang or default language state to zh-CN.", + "path": "demo/index.html", + "evidence": "demo/index.html, demo/src/App.tsx, examples/vanilla/index.html" + }, + { + "id": "DEMO-003", + "level": "required", + "status": "pass", + "message": "Demo provides an in-page Chinese/English toggle", + "remediation": "Add a language toggle that does not alter inference state.", + "path": "demo/src/App.tsx", + "evidence": "demo/src/App.tsx, demo/src/draw-polygon.ts" + }, + { + "id": "DEMO-004", + "level": "required", + "status": "pass", + "message": "Demo exposes current-model and global cache cleanup controls", + "remediation": "Wire clear-current and clear-all actions to the SDK cache API.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DEMO-005", + "level": "required", + "status": "pass", + "message": "Demo exposes model/runtime/timing information", + "remediation": "Render data-sdk-model-info, data-sdk-runtime-info, and data-sdk-timing regions.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "MODEL-001", + "level": "required", + "status": "pass", + "message": "Manifest declares model identity, precision, size, source, license, and checksum", + "remediation": "Complete the model and asset fields in sdk-manifest.yaml.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "RUNTIME-001", + "level": "required", + "status": "pass", + "message": "Requested/actual backend and execution mode are declared", + "remediation": "Add runtime backends, execution modes, and actual backend reporting.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "PERF-001", + "level": "required", + "status": "pass", + "message": "Standard load and inference timing fields are declared", + "remediation": "Document download, cache, integrity, session, inference, and total timing.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "CACHE-001", + "level": "required", + "status": "pass", + "message": "Cache storage, versioning, estimate, and cleanup are declared", + "remediation": "Add cache contract fields and expose cleanup controls in the Demo.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "EXAMPLE-001", + "level": "required", + "status": "pass", + "message": "Vanilla TypeScript/DOM portability example exists", + "remediation": "Add examples/vanilla with a runnable README.", + "path": "examples/vanilla/README.md", + "evidence": "examples/vanilla/README.md" + }, + { + "id": "EXAMPLE-002", + "level": "required", + "status": "pass", + "message": "React reference example exists", + "remediation": "Add examples/react or explicitly record a migration exception.", + "path": "examples/react/README.md", + "evidence": "examples/react/README.md" + }, + { + "id": "EXAMPLE-003", + "level": "recommended", + "status": "pass", + "message": "Declared target surfaces have runnable examples or explicit unsupported status", + "remediation": "Align examples with manifest target surfaces.", + "path": "examples/vanilla/README.md", + "evidence": "examples/vanilla/README.md, examples/react/README.md, examples/vite/README.md" + }, + { + "id": "RELEASE-001", + "level": "required", + "status": "pass", + "message": "CI workflow runs verification before release", + "remediation": "Add .github/workflows/ci.yml with tests, typecheck, and build.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "RELEASE-002", + "level": "required", + "status": "pass", + "message": "GitHub Release workflow exists", + "remediation": "Add a tag-triggered release workflow and publish notes.", + "path": ".github/workflows/release.yml", + "evidence": ".github/workflows/release.yml" + }, + { + "id": "RELEASE-003", + "level": "recommended", + "status": "pass", + "message": "Changelog documents user-visible SDK and Demo changes", + "remediation": "Add CHANGELOG.md with the current release entry.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "GOV-001", + "level": "required", + "status": "skip", + "message": "The default branch has an active least-privilege Ruleset for pull requests, current CI checks, resolved conversations, deletion, and force-push protection", + "remediation": "Configure an active default-branch Ruleset using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "GOV-002", + "level": "required", + "status": "skip", + "message": "Release tags are immutable under an active tag Ruleset", + "remediation": "Protect the documented release tag pattern against updates and deletion, minimize bypass, and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "DEPLOY-001", + "level": "required", + "status": "skip", + "message": "The live Demo has a reproducible HTTPS deployment from protected source with least-privilege credentials", + "remediation": "Configure and verify a deployment that meets standards/v1/repository-governance-contract.md, then record its commit and deployment evidence.", + "evidence": "Requires read-only remote-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "PAGES-001", + "level": "required", + "status": "skip", + "message": "A GitHub Pages Demo deploys through GitHub Actions with scoped permissions, environment binding, HTTPS, and concurrency control", + "remediation": "Configure Pages and its deployment workflow using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "UI-001", + "level": "recommended", + "status": "pass", + "message": "Demo styling references the shared token vocabulary", + "remediation": "Import or copy standards/v1/ui-tokens.json values without introducing conflicting status colors.", + "path": "standards/v1/ui-tokens.json", + "evidence": "standards/v1/ui-tokens.json" + }, + { + "id": "LABS-001", + "level": "labs", + "status": "skip", + "message": "Experimental backends and NPU claims have explicit evidence and limits", + "remediation": "Mark WebNN/NPU as labs unless a dated verification matrix exists.", + "evidence": "Labs rule is informational" + } + ], + "current": [ + { + "id": "META-001", + "level": "required", + "status": "pass", + "message": "SDK manifest is present and declares the v1 contract", + "remediation": "Copy standards/v1/templates/sdk-manifest.yaml and fill every required field.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "META-002", + "level": "required", + "status": "pass", + "message": "package.json declares a public package identity and version", + "remediation": "Add a non-private package with a semver version and repository metadata.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-001", + "level": "required", + "status": "pass", + "message": "Chinese-default and English README files link to one another", + "remediation": "Add README.md and README.en.md with reciprocal language links.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-002", + "level": "required", + "status": "pass", + "message": "Chinese and English documentation directories are present", + "remediation": "Provide equivalent docs/zh-CN and docs/en quick-start/API guidance.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-003", + "level": "required", + "status": "pass", + "message": "README or package metadata exposes GitHub, npm, and live Demo links", + "remediation": "Add repository, package, and Demo URLs to README and manifest.", + "path": "README.md", + "evidence": "README.md" + }, + { + "id": "DEMO-001", + "level": "required", + "status": "pass", + "message": "A runnable standalone Demo entry exists", + "remediation": "Add a Demo app with a documented start/build command.", + "path": "demo/index.html", + "evidence": "demo/index.html" + }, + { + "id": "DEMO-002", + "level": "required", + "status": "pass", + "message": "Demo declares Chinese as its initial language", + "remediation": "Set lang or default language state to zh-CN.", + "path": "demo/index.html", + "evidence": "demo/index.html, demo/src/App.tsx, examples/vanilla/index.html" + }, + { + "id": "DEMO-003", + "level": "required", + "status": "pass", + "message": "Demo provides an in-page Chinese/English toggle", + "remediation": "Add a language toggle that does not alter inference state.", + "path": "demo/src/App.tsx", + "evidence": "demo/src/App.tsx, demo/src/draw-polygon.ts" + }, + { + "id": "DEMO-004", + "level": "required", + "status": "pass", + "message": "Demo exposes current-model and global cache cleanup controls", + "remediation": "Wire clear-current and clear-all actions to the SDK cache API.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DEMO-005", + "level": "required", + "status": "pass", + "message": "Demo 展示当前模型或算法的信息、运行信息和耗时", + "remediation": "模型使用 data-sdk-model-info,算法使用 data-sdk-algorithm-info;两者保留 data-sdk-runtime-info 和 data-sdk-timing。", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DEMO-006", + "level": "required", + "status": "skip", + "message": "算法 Demo 提供状态复位操作", + "remediation": "使用 data-sdk-state-reset 标记复位控件,并按状态生命周期契约连接 reset。", + "path": "sdk-manifest.yaml", + "evidence": "当前类型为 model,本规则仅适用于 algorithm;清单已通过完整校验。" + }, + { + "id": "ALGORITHM-001", + "level": "required", + "status": "skip", + "message": "算法清单声明身份、家族、来源、许可、输入输出与有状态语义", + "remediation": "按 sdk-manifest.algorithm.yaml 填写 algorithm 字段并记录状态生命周期。", + "path": "sdk-manifest.yaml", + "evidence": "当前类型为 model,本规则仅适用于 algorithm;清单已通过完整校验。" + }, + { + "id": "MODEL-001", + "level": "required", + "status": "pass", + "message": "Manifest declares model identity, precision, size, source, license, and checksum", + "remediation": "Complete the model and asset fields in sdk-manifest.yaml.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "RUNTIME-001", + "level": "required", + "status": "pass", + "message": "Requested/actual backend and execution mode are declared", + "remediation": "Add runtime backends, execution modes, and actual backend reporting.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "PERF-001", + "level": "required", + "status": "pass", + "message": "声明当前 SDK 类型所需的标准耗时字段", + "remediation": "模型保留下载、缓存、校验、会话、推理和总耗时;算法声明 validationMs、predictionMs、associationMs、updateMs、totalMs。", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "CACHE-001", + "level": "required", + "status": "pass", + "message": "Cache storage, versioning, estimate, and cleanup are declared", + "remediation": "Add cache contract fields and expose cleanup controls in the Demo.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "EXAMPLE-001", + "level": "required", + "status": "pass", + "message": "Vanilla TypeScript/DOM portability example exists", + "remediation": "Add examples/vanilla with a runnable README.", + "path": "examples/vanilla/README.md", + "evidence": "examples/vanilla/README.md" + }, + { + "id": "EXAMPLE-002", + "level": "required", + "status": "pass", + "message": "React reference example exists", + "remediation": "Add examples/react or explicitly record a migration exception.", + "path": "examples/react/README.md", + "evidence": "examples/react/README.md" + }, + { + "id": "EXAMPLE-003", + "level": "recommended", + "status": "pass", + "message": "Declared target surfaces have runnable examples or explicit unsupported status", + "remediation": "Align examples with manifest target surfaces.", + "path": "examples/vanilla/README.md", + "evidence": "examples/vanilla/README.md, examples/react/README.md, examples/vite/README.md" + }, + { + "id": "RELEASE-001", + "level": "required", + "status": "pass", + "message": "CI workflow runs verification before release", + "remediation": "Add .github/workflows/ci.yml with tests, typecheck, and build.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "RELEASE-002", + "level": "required", + "status": "pass", + "message": "GitHub Release workflow exists", + "remediation": "Add a tag-triggered release workflow and publish notes.", + "path": ".github/workflows/release.yml", + "evidence": ".github/workflows/release.yml" + }, + { + "id": "RELEASE-003", + "level": "recommended", + "status": "pass", + "message": "Changelog documents user-visible SDK and Demo changes", + "remediation": "Add CHANGELOG.md with the current release entry.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "GOV-001", + "level": "required", + "status": "skip", + "message": "The default branch has an active least-privilege Ruleset for pull requests, current CI checks, resolved conversations, deletion, and force-push protection", + "remediation": "Configure an active default-branch Ruleset using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "GOV-002", + "level": "required", + "status": "skip", + "message": "Release tags are immutable under an active tag Ruleset", + "remediation": "Protect the documented release tag pattern against updates and deletion, minimize bypass, and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "DEPLOY-001", + "level": "required", + "status": "skip", + "message": "The live Demo has a reproducible HTTPS deployment from protected source with least-privilege credentials", + "remediation": "Configure and verify a deployment that meets standards/v1/repository-governance-contract.md, then record its commit and deployment evidence.", + "evidence": "Requires read-only remote-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "PAGES-001", + "level": "required", + "status": "skip", + "message": "A GitHub Pages Demo deploys through GitHub Actions with scoped permissions, environment binding, HTTPS, and concurrency control", + "remediation": "Configure Pages and its deployment workflow using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "UI-001", + "level": "recommended", + "status": "pass", + "message": "Demo styling references the shared token vocabulary", + "remediation": "Import or copy standards/v1/ui-tokens.json values without introducing conflicting status colors.", + "path": "standards/v1/ui-tokens.json", + "evidence": "standards/v1/ui-tokens.json" + }, + { + "id": "LABS-001", + "level": "labs", + "status": "skip", + "message": "Experimental backends and NPU claims have explicit evidence and limits", + "remediation": "Mark WebNN/NPU as labs unless a dated verification matrix exists.", + "evidence": "Labs rule is informational" + } + ] + }, + { + "repository": "web-sdk-PP-Segmentation", + "commit": "89b350d30305ecbc275780e455d1c115a250570f", + "changes": [], + "baselineFailures": [], + "currentFailures": [], + "baseline": [ + { + "id": "META-001", + "level": "required", + "status": "pass", + "message": "SDK manifest is present and declares the v1 contract", + "remediation": "Copy standards/v1/templates/sdk-manifest.yaml and fill every required field.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "META-002", + "level": "required", + "status": "pass", + "message": "package.json declares a public package identity and version", + "remediation": "Add a non-private package with a semver version and repository metadata.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-001", + "level": "required", + "status": "pass", + "message": "Chinese-default and English README files link to one another", + "remediation": "Add README.md and README.en.md with reciprocal language links.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-002", + "level": "required", + "status": "pass", + "message": "Chinese and English documentation directories are present", + "remediation": "Provide equivalent docs/zh-CN and docs/en quick-start/API guidance.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-003", + "level": "required", + "status": "pass", + "message": "README or package metadata exposes GitHub, npm, and live Demo links", + "remediation": "Add repository, package, and Demo URLs to README and manifest.", + "path": "README.md", + "evidence": "README.md" + }, + { + "id": "DEMO-001", + "level": "required", + "status": "pass", + "message": "A runnable standalone Demo entry exists", + "remediation": "Add a Demo app with a documented start/build command.", + "path": "demo/index.html", + "evidence": "demo/index.html" + }, + { + "id": "DEMO-002", + "level": "required", + "status": "pass", + "message": "Demo declares Chinese as its initial language", + "remediation": "Set lang or default language state to zh-CN.", + "path": "demo/index.html", + "evidence": "demo/index.html, demo/src/App.tsx, examples/vanilla/index.html" + }, + { + "id": "DEMO-003", + "level": "required", + "status": "pass", + "message": "Demo provides an in-page Chinese/English toggle", + "remediation": "Add a language toggle that does not alter inference state.", + "path": "demo/src/App.tsx", + "evidence": "demo/src/App.tsx" + }, + { + "id": "DEMO-004", + "level": "required", + "status": "pass", + "message": "Demo exposes current-model and global cache cleanup controls", + "remediation": "Wire clear-current and clear-all actions to the SDK cache API.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DEMO-005", + "level": "required", + "status": "pass", + "message": "Demo exposes model/runtime/timing information", + "remediation": "Render data-sdk-model-info, data-sdk-runtime-info, and data-sdk-timing regions.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "MODEL-001", + "level": "required", + "status": "pass", + "message": "Manifest declares model identity, precision, size, source, license, and checksum", + "remediation": "Complete the model and asset fields in sdk-manifest.yaml.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "RUNTIME-001", + "level": "required", + "status": "pass", + "message": "Requested/actual backend and execution mode are declared", + "remediation": "Add runtime backends, execution modes, and actual backend reporting.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "PERF-001", + "level": "required", + "status": "pass", + "message": "Standard load and inference timing fields are declared", + "remediation": "Document download, cache, integrity, session, inference, and total timing.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "CACHE-001", + "level": "required", + "status": "pass", + "message": "Cache storage, versioning, estimate, and cleanup are declared", + "remediation": "Add cache contract fields and expose cleanup controls in the Demo.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "EXAMPLE-001", + "level": "required", + "status": "pass", + "message": "Vanilla TypeScript/DOM portability example exists", + "remediation": "Add examples/vanilla with a runnable README.", + "path": "examples/vanilla/README.md", + "evidence": "examples/vanilla/README.md" + }, + { + "id": "EXAMPLE-002", + "level": "required", + "status": "pass", + "message": "React reference example exists", + "remediation": "Add examples/react or explicitly record a migration exception.", + "path": "examples/react/README.md", + "evidence": "examples/react/README.md" + }, + { + "id": "EXAMPLE-003", + "level": "recommended", + "status": "fail", + "message": "Declared target surfaces have runnable examples or explicit unsupported status", + "remediation": "Align examples with manifest target surfaces.", + "path": "examples/vanilla/README.md" + }, + { + "id": "RELEASE-001", + "level": "required", + "status": "pass", + "message": "CI workflow runs verification before release", + "remediation": "Add .github/workflows/ci.yml with tests, typecheck, and build.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "RELEASE-002", + "level": "required", + "status": "pass", + "message": "GitHub Release workflow exists", + "remediation": "Add a tag-triggered release workflow and publish notes.", + "path": ".github/workflows/release.yml", + "evidence": ".github/workflows/release.yml" + }, + { + "id": "RELEASE-003", + "level": "recommended", + "status": "pass", + "message": "Changelog documents user-visible SDK and Demo changes", + "remediation": "Add CHANGELOG.md with the current release entry.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "GOV-001", + "level": "required", + "status": "skip", + "message": "The default branch has an active least-privilege Ruleset for pull requests, current CI checks, resolved conversations, deletion, and force-push protection", + "remediation": "Configure an active default-branch Ruleset using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "GOV-002", + "level": "required", + "status": "skip", + "message": "Release tags are immutable under an active tag Ruleset", + "remediation": "Protect the documented release tag pattern against updates and deletion, minimize bypass, and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "DEPLOY-001", + "level": "required", + "status": "skip", + "message": "The live Demo has a reproducible HTTPS deployment from protected source with least-privilege credentials", + "remediation": "Configure and verify a deployment that meets standards/v1/repository-governance-contract.md, then record its commit and deployment evidence.", + "evidence": "Requires read-only remote-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "PAGES-001", + "level": "required", + "status": "skip", + "message": "A GitHub Pages Demo deploys through GitHub Actions with scoped permissions, environment binding, HTTPS, and concurrency control", + "remediation": "Configure Pages and its deployment workflow using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "UI-001", + "level": "recommended", + "status": "pass", + "message": "Demo styling references the shared token vocabulary", + "remediation": "Import or copy standards/v1/ui-tokens.json values without introducing conflicting status colors.", + "path": "standards/v1/ui-tokens.json", + "evidence": "standards/v1/ui-tokens.json" + }, + { + "id": "LABS-001", + "level": "labs", + "status": "skip", + "message": "Experimental backends and NPU claims have explicit evidence and limits", + "remediation": "Mark WebNN/NPU as labs unless a dated verification matrix exists.", + "evidence": "Labs rule is informational" + } + ], + "current": [ + { + "id": "META-001", + "level": "required", + "status": "pass", + "message": "SDK manifest is present and declares the v1 contract", + "remediation": "Copy standards/v1/templates/sdk-manifest.yaml and fill every required field.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "META-002", + "level": "required", + "status": "pass", + "message": "package.json declares a public package identity and version", + "remediation": "Add a non-private package with a semver version and repository metadata.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-001", + "level": "required", + "status": "pass", + "message": "Chinese-default and English README files link to one another", + "remediation": "Add README.md and README.en.md with reciprocal language links.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-002", + "level": "required", + "status": "pass", + "message": "Chinese and English documentation directories are present", + "remediation": "Provide equivalent docs/zh-CN and docs/en quick-start/API guidance.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-003", + "level": "required", + "status": "pass", + "message": "README or package metadata exposes GitHub, npm, and live Demo links", + "remediation": "Add repository, package, and Demo URLs to README and manifest.", + "path": "README.md", + "evidence": "README.md" + }, + { + "id": "DEMO-001", + "level": "required", + "status": "pass", + "message": "A runnable standalone Demo entry exists", + "remediation": "Add a Demo app with a documented start/build command.", + "path": "demo/index.html", + "evidence": "demo/index.html" + }, + { + "id": "DEMO-002", + "level": "required", + "status": "pass", + "message": "Demo declares Chinese as its initial language", + "remediation": "Set lang or default language state to zh-CN.", + "path": "demo/index.html", + "evidence": "demo/index.html, demo/src/App.tsx, examples/vanilla/index.html" + }, + { + "id": "DEMO-003", + "level": "required", + "status": "pass", + "message": "Demo provides an in-page Chinese/English toggle", + "remediation": "Add a language toggle that does not alter inference state.", + "path": "demo/src/App.tsx", + "evidence": "demo/src/App.tsx" + }, + { + "id": "DEMO-004", + "level": "required", + "status": "pass", + "message": "Demo exposes current-model and global cache cleanup controls", + "remediation": "Wire clear-current and clear-all actions to the SDK cache API.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DEMO-005", + "level": "required", + "status": "pass", + "message": "Demo 展示当前模型或算法的信息、运行信息和耗时", + "remediation": "模型使用 data-sdk-model-info,算法使用 data-sdk-algorithm-info;两者保留 data-sdk-runtime-info 和 data-sdk-timing。", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DEMO-006", + "level": "required", + "status": "skip", + "message": "算法 Demo 提供状态复位操作", + "remediation": "使用 data-sdk-state-reset 标记复位控件,并按状态生命周期契约连接 reset。", + "path": "sdk-manifest.yaml", + "evidence": "当前类型为 model,本规则仅适用于 algorithm;清单已通过完整校验。" + }, + { + "id": "ALGORITHM-001", + "level": "required", + "status": "skip", + "message": "算法清单声明身份、家族、来源、许可、输入输出与有状态语义", + "remediation": "按 sdk-manifest.algorithm.yaml 填写 algorithm 字段并记录状态生命周期。", + "path": "sdk-manifest.yaml", + "evidence": "当前类型为 model,本规则仅适用于 algorithm;清单已通过完整校验。" + }, + { + "id": "MODEL-001", + "level": "required", + "status": "pass", + "message": "Manifest declares model identity, precision, size, source, license, and checksum", + "remediation": "Complete the model and asset fields in sdk-manifest.yaml.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "RUNTIME-001", + "level": "required", + "status": "pass", + "message": "Requested/actual backend and execution mode are declared", + "remediation": "Add runtime backends, execution modes, and actual backend reporting.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "PERF-001", + "level": "required", + "status": "pass", + "message": "声明当前 SDK 类型所需的标准耗时字段", + "remediation": "模型保留下载、缓存、校验、会话、推理和总耗时;算法声明 validationMs、predictionMs、associationMs、updateMs、totalMs。", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "CACHE-001", + "level": "required", + "status": "pass", + "message": "Cache storage, versioning, estimate, and cleanup are declared", + "remediation": "Add cache contract fields and expose cleanup controls in the Demo.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "EXAMPLE-001", + "level": "required", + "status": "pass", + "message": "Vanilla TypeScript/DOM portability example exists", + "remediation": "Add examples/vanilla with a runnable README.", + "path": "examples/vanilla/README.md", + "evidence": "examples/vanilla/README.md" + }, + { + "id": "EXAMPLE-002", + "level": "required", + "status": "pass", + "message": "React reference example exists", + "remediation": "Add examples/react or explicitly record a migration exception.", + "path": "examples/react/README.md", + "evidence": "examples/react/README.md" + }, + { + "id": "EXAMPLE-003", + "level": "recommended", + "status": "fail", + "message": "Declared target surfaces have runnable examples or explicit unsupported status", + "remediation": "Align examples with manifest target surfaces.", + "path": "examples/vanilla/README.md" + }, + { + "id": "RELEASE-001", + "level": "required", + "status": "pass", + "message": "CI workflow runs verification before release", + "remediation": "Add .github/workflows/ci.yml with tests, typecheck, and build.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "RELEASE-002", + "level": "required", + "status": "pass", + "message": "GitHub Release workflow exists", + "remediation": "Add a tag-triggered release workflow and publish notes.", + "path": ".github/workflows/release.yml", + "evidence": ".github/workflows/release.yml" + }, + { + "id": "RELEASE-003", + "level": "recommended", + "status": "pass", + "message": "Changelog documents user-visible SDK and Demo changes", + "remediation": "Add CHANGELOG.md with the current release entry.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "GOV-001", + "level": "required", + "status": "skip", + "message": "The default branch has an active least-privilege Ruleset for pull requests, current CI checks, resolved conversations, deletion, and force-push protection", + "remediation": "Configure an active default-branch Ruleset using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "GOV-002", + "level": "required", + "status": "skip", + "message": "Release tags are immutable under an active tag Ruleset", + "remediation": "Protect the documented release tag pattern against updates and deletion, minimize bypass, and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "DEPLOY-001", + "level": "required", + "status": "skip", + "message": "The live Demo has a reproducible HTTPS deployment from protected source with least-privilege credentials", + "remediation": "Configure and verify a deployment that meets standards/v1/repository-governance-contract.md, then record its commit and deployment evidence.", + "evidence": "Requires read-only remote-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "PAGES-001", + "level": "required", + "status": "skip", + "message": "A GitHub Pages Demo deploys through GitHub Actions with scoped permissions, environment binding, HTTPS, and concurrency control", + "remediation": "Configure Pages and its deployment workflow using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "UI-001", + "level": "recommended", + "status": "pass", + "message": "Demo styling references the shared token vocabulary", + "remediation": "Import or copy standards/v1/ui-tokens.json values without introducing conflicting status colors.", + "path": "standards/v1/ui-tokens.json", + "evidence": "standards/v1/ui-tokens.json" + }, + { + "id": "LABS-001", + "level": "labs", + "status": "skip", + "message": "Experimental backends and NPU claims have explicit evidence and limits", + "remediation": "Mark WebNN/NPU as labs unless a dated verification matrix exists.", + "evidence": "Labs rule is informational" + } + ] + }, + { + "repository": "web-sdk-PP-TinyPose", + "commit": "70fe7e5a2377be64cd86efc8b306f03121b6f5c9", + "changes": [], + "baselineFailures": [], + "currentFailures": [], + "baseline": [ + { + "id": "META-001", + "level": "required", + "status": "pass", + "message": "SDK manifest is present and declares the v1 contract", + "remediation": "Copy standards/v1/templates/sdk-manifest.yaml and fill every required field.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "META-002", + "level": "required", + "status": "pass", + "message": "package.json declares a public package identity and version", + "remediation": "Add a non-private package with a semver version and repository metadata.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-001", + "level": "required", + "status": "pass", + "message": "Chinese-default and English README files link to one another", + "remediation": "Add README.md and README.en.md with reciprocal language links.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-002", + "level": "required", + "status": "pass", + "message": "Chinese and English documentation directories are present", + "remediation": "Provide equivalent docs/zh-CN and docs/en quick-start/API guidance.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-003", + "level": "required", + "status": "pass", + "message": "README or package metadata exposes GitHub, npm, and live Demo links", + "remediation": "Add repository, package, and Demo URLs to README and manifest.", + "path": "README.md", + "evidence": "README.md" + }, + { + "id": "DEMO-001", + "level": "required", + "status": "pass", + "message": "A runnable standalone Demo entry exists", + "remediation": "Add a Demo app with a documented start/build command.", + "path": "demo/index.html", + "evidence": "demo/index.html" + }, + { + "id": "DEMO-002", + "level": "required", + "status": "pass", + "message": "Demo declares Chinese as its initial language", + "remediation": "Set lang or default language state to zh-CN.", + "path": "demo/index.html", + "evidence": "demo/index.html, demo/public/examples/vanilla.html, demo/src/App.tsx" + }, + { + "id": "DEMO-003", + "level": "required", + "status": "pass", + "message": "Demo provides an in-page Chinese/English toggle", + "remediation": "Add a language toggle that does not alter inference state.", + "path": "demo/src/App.tsx", + "evidence": "demo/src/App.tsx, demo/src/media/MediaWorkspace.tsx, demo/src/style.css" + }, + { + "id": "DEMO-004", + "level": "required", + "status": "pass", + "message": "Demo exposes current-model and global cache cleanup controls", + "remediation": "Wire clear-current and clear-all actions to the SDK cache API.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DEMO-005", + "level": "required", + "status": "pass", + "message": "Demo exposes model/runtime/timing information", + "remediation": "Render data-sdk-model-info, data-sdk-runtime-info, and data-sdk-timing regions.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "MODEL-001", + "level": "required", + "status": "pass", + "message": "Manifest declares model identity, precision, size, source, license, and checksum", + "remediation": "Complete the model and asset fields in sdk-manifest.yaml.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "RUNTIME-001", + "level": "required", + "status": "pass", + "message": "Requested/actual backend and execution mode are declared", + "remediation": "Add runtime backends, execution modes, and actual backend reporting.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "PERF-001", + "level": "required", + "status": "pass", + "message": "Standard load and inference timing fields are declared", + "remediation": "Document download, cache, integrity, session, inference, and total timing.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "CACHE-001", + "level": "required", + "status": "pass", + "message": "Cache storage, versioning, estimate, and cleanup are declared", + "remediation": "Add cache contract fields and expose cleanup controls in the Demo.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "EXAMPLE-001", + "level": "required", + "status": "pass", + "message": "Vanilla TypeScript/DOM portability example exists", + "remediation": "Add examples/vanilla with a runnable README.", + "path": "examples/vanilla/README.md", + "evidence": "examples/vanilla/README.md" + }, + { + "id": "EXAMPLE-002", + "level": "required", + "status": "pass", + "message": "React reference example exists", + "remediation": "Add examples/react or explicitly record a migration exception.", + "path": "examples/react/README.md", + "evidence": "examples/react/README.md" + }, + { + "id": "EXAMPLE-003", + "level": "recommended", + "status": "pass", + "message": "Declared target surfaces have runnable examples or explicit unsupported status", + "remediation": "Align examples with manifest target surfaces.", + "path": "examples/vanilla/README.md", + "evidence": "examples/vanilla/README.md, examples/react/README.md, examples/vite/README.md" + }, + { + "id": "RELEASE-001", + "level": "required", + "status": "pass", + "message": "CI workflow runs verification before release", + "remediation": "Add .github/workflows/ci.yml with tests, typecheck, and build.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "RELEASE-002", + "level": "required", + "status": "pass", + "message": "GitHub Release workflow exists", + "remediation": "Add a tag-triggered release workflow and publish notes.", + "path": ".github/workflows/release.yml", + "evidence": ".github/workflows/release.yml" + }, + { + "id": "RELEASE-003", + "level": "recommended", + "status": "pass", + "message": "Changelog documents user-visible SDK and Demo changes", + "remediation": "Add CHANGELOG.md with the current release entry.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "GOV-001", + "level": "required", + "status": "skip", + "message": "The default branch has an active least-privilege Ruleset for pull requests, current CI checks, resolved conversations, deletion, and force-push protection", + "remediation": "Configure an active default-branch Ruleset using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "GOV-002", + "level": "required", + "status": "skip", + "message": "Release tags are immutable under an active tag Ruleset", + "remediation": "Protect the documented release tag pattern against updates and deletion, minimize bypass, and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "DEPLOY-001", + "level": "required", + "status": "skip", + "message": "The live Demo has a reproducible HTTPS deployment from protected source with least-privilege credentials", + "remediation": "Configure and verify a deployment that meets standards/v1/repository-governance-contract.md, then record its commit and deployment evidence.", + "evidence": "Requires read-only remote-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "PAGES-001", + "level": "required", + "status": "skip", + "message": "A GitHub Pages Demo deploys through GitHub Actions with scoped permissions, environment binding, HTTPS, and concurrency control", + "remediation": "Configure Pages and its deployment workflow using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "UI-001", + "level": "recommended", + "status": "pass", + "message": "Demo styling references the shared token vocabulary", + "remediation": "Import or copy standards/v1/ui-tokens.json values without introducing conflicting status colors.", + "path": "standards/v1/ui-tokens.json", + "evidence": "standards/v1/ui-tokens.json" + }, + { + "id": "LABS-001", + "level": "labs", + "status": "skip", + "message": "Experimental backends and NPU claims have explicit evidence and limits", + "remediation": "Mark WebNN/NPU as labs unless a dated verification matrix exists.", + "evidence": "Labs rule is informational" + } + ], + "current": [ + { + "id": "META-001", + "level": "required", + "status": "pass", + "message": "SDK manifest is present and declares the v1 contract", + "remediation": "Copy standards/v1/templates/sdk-manifest.yaml and fill every required field.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "META-002", + "level": "required", + "status": "pass", + "message": "package.json declares a public package identity and version", + "remediation": "Add a non-private package with a semver version and repository metadata.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-001", + "level": "required", + "status": "pass", + "message": "Chinese-default and English README files link to one another", + "remediation": "Add README.md and README.en.md with reciprocal language links.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-002", + "level": "required", + "status": "pass", + "message": "Chinese and English documentation directories are present", + "remediation": "Provide equivalent docs/zh-CN and docs/en quick-start/API guidance.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-003", + "level": "required", + "status": "pass", + "message": "README or package metadata exposes GitHub, npm, and live Demo links", + "remediation": "Add repository, package, and Demo URLs to README and manifest.", + "path": "README.md", + "evidence": "README.md" + }, + { + "id": "DEMO-001", + "level": "required", + "status": "pass", + "message": "A runnable standalone Demo entry exists", + "remediation": "Add a Demo app with a documented start/build command.", + "path": "demo/index.html", + "evidence": "demo/index.html" + }, + { + "id": "DEMO-002", + "level": "required", + "status": "pass", + "message": "Demo declares Chinese as its initial language", + "remediation": "Set lang or default language state to zh-CN.", + "path": "demo/index.html", + "evidence": "demo/index.html, demo/public/examples/vanilla.html, demo/src/App.tsx" + }, + { + "id": "DEMO-003", + "level": "required", + "status": "pass", + "message": "Demo provides an in-page Chinese/English toggle", + "remediation": "Add a language toggle that does not alter inference state.", + "path": "demo/src/App.tsx", + "evidence": "demo/src/App.tsx, demo/src/media/MediaWorkspace.tsx, demo/src/style.css" + }, + { + "id": "DEMO-004", + "level": "required", + "status": "pass", + "message": "Demo exposes current-model and global cache cleanup controls", + "remediation": "Wire clear-current and clear-all actions to the SDK cache API.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DEMO-005", + "level": "required", + "status": "pass", + "message": "Demo 展示当前模型或算法的信息、运行信息和耗时", + "remediation": "模型使用 data-sdk-model-info,算法使用 data-sdk-algorithm-info;两者保留 data-sdk-runtime-info 和 data-sdk-timing。", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DEMO-006", + "level": "required", + "status": "skip", + "message": "算法 Demo 提供状态复位操作", + "remediation": "使用 data-sdk-state-reset 标记复位控件,并按状态生命周期契约连接 reset。", + "path": "sdk-manifest.yaml", + "evidence": "当前类型为 model,本规则仅适用于 algorithm;清单已通过完整校验。" + }, + { + "id": "ALGORITHM-001", + "level": "required", + "status": "skip", + "message": "算法清单声明身份、家族、来源、许可、输入输出与有状态语义", + "remediation": "按 sdk-manifest.algorithm.yaml 填写 algorithm 字段并记录状态生命周期。", + "path": "sdk-manifest.yaml", + "evidence": "当前类型为 model,本规则仅适用于 algorithm;清单已通过完整校验。" + }, + { + "id": "MODEL-001", + "level": "required", + "status": "pass", + "message": "Manifest declares model identity, precision, size, source, license, and checksum", + "remediation": "Complete the model and asset fields in sdk-manifest.yaml.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "RUNTIME-001", + "level": "required", + "status": "pass", + "message": "Requested/actual backend and execution mode are declared", + "remediation": "Add runtime backends, execution modes, and actual backend reporting.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "PERF-001", + "level": "required", + "status": "pass", + "message": "声明当前 SDK 类型所需的标准耗时字段", + "remediation": "模型保留下载、缓存、校验、会话、推理和总耗时;算法声明 validationMs、predictionMs、associationMs、updateMs、totalMs。", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "CACHE-001", + "level": "required", + "status": "pass", + "message": "Cache storage, versioning, estimate, and cleanup are declared", + "remediation": "Add cache contract fields and expose cleanup controls in the Demo.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "EXAMPLE-001", + "level": "required", + "status": "pass", + "message": "Vanilla TypeScript/DOM portability example exists", + "remediation": "Add examples/vanilla with a runnable README.", + "path": "examples/vanilla/README.md", + "evidence": "examples/vanilla/README.md" + }, + { + "id": "EXAMPLE-002", + "level": "required", + "status": "pass", + "message": "React reference example exists", + "remediation": "Add examples/react or explicitly record a migration exception.", + "path": "examples/react/README.md", + "evidence": "examples/react/README.md" + }, + { + "id": "EXAMPLE-003", + "level": "recommended", + "status": "pass", + "message": "Declared target surfaces have runnable examples or explicit unsupported status", + "remediation": "Align examples with manifest target surfaces.", + "path": "examples/vanilla/README.md", + "evidence": "examples/vanilla/README.md, examples/react/README.md, examples/vite/README.md" + }, + { + "id": "RELEASE-001", + "level": "required", + "status": "pass", + "message": "CI workflow runs verification before release", + "remediation": "Add .github/workflows/ci.yml with tests, typecheck, and build.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "RELEASE-002", + "level": "required", + "status": "pass", + "message": "GitHub Release workflow exists", + "remediation": "Add a tag-triggered release workflow and publish notes.", + "path": ".github/workflows/release.yml", + "evidence": ".github/workflows/release.yml" + }, + { + "id": "RELEASE-003", + "level": "recommended", + "status": "pass", + "message": "Changelog documents user-visible SDK and Demo changes", + "remediation": "Add CHANGELOG.md with the current release entry.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "GOV-001", + "level": "required", + "status": "skip", + "message": "The default branch has an active least-privilege Ruleset for pull requests, current CI checks, resolved conversations, deletion, and force-push protection", + "remediation": "Configure an active default-branch Ruleset using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "GOV-002", + "level": "required", + "status": "skip", + "message": "Release tags are immutable under an active tag Ruleset", + "remediation": "Protect the documented release tag pattern against updates and deletion, minimize bypass, and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "DEPLOY-001", + "level": "required", + "status": "skip", + "message": "The live Demo has a reproducible HTTPS deployment from protected source with least-privilege credentials", + "remediation": "Configure and verify a deployment that meets standards/v1/repository-governance-contract.md, then record its commit and deployment evidence.", + "evidence": "Requires read-only remote-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "PAGES-001", + "level": "required", + "status": "skip", + "message": "A GitHub Pages Demo deploys through GitHub Actions with scoped permissions, environment binding, HTTPS, and concurrency control", + "remediation": "Configure Pages and its deployment workflow using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "UI-001", + "level": "recommended", + "status": "pass", + "message": "Demo styling references the shared token vocabulary", + "remediation": "Import or copy standards/v1/ui-tokens.json values without introducing conflicting status colors.", + "path": "standards/v1/ui-tokens.json", + "evidence": "standards/v1/ui-tokens.json" + }, + { + "id": "LABS-001", + "level": "labs", + "status": "skip", + "message": "Experimental backends and NPU claims have explicit evidence and limits", + "remediation": "Mark WebNN/NPU as labs unless a dated verification matrix exists.", + "evidence": "Labs rule is informational" + } + ] + } + ] +} diff --git a/reports/tracking/2026-09-19-foundation/verify-standard-regression.mjs b/reports/tracking/2026-09-19-foundation/verify-standard-regression.mjs new file mode 100644 index 0000000..7c9e82f --- /dev/null +++ b/reports/tracking/2026-09-19-foundation/verify-standard-regression.mjs @@ -0,0 +1,72 @@ +import assert from 'node:assert/strict'; +import fs from 'node:fs/promises'; +import path from 'node:path'; +import os from 'node:os'; +import { fileURLToPath, pathToFileURL } from 'node:url'; +import { createHash } from 'node:crypto'; +import { execFileSync } from 'node:child_process'; + +const root = path.dirname(fileURLToPath(import.meta.url)); +const portal = path.resolve(root, '../../..'); +const args = process.argv.slice(2); +const mode = args.shift() ?? 'verify'; +assert.ok(['verify', 'rerun'].includes(mode), '模式为 verify 或 rerun'); +const options = { 'repo-root': path.resolve(portal, '..'), out: path.join(portal, '.tmp/standard-regression-rerun.json') }; +for (let i = 0; i < args.length; i += 2) { + const key = args[i].replace(/^--/, ''); + assert.ok(args[i].startsWith('--') && Object.hasOwn(options, key) && args[i + 1], '参数为 --repo-root 路径、--out 路径'); + options[key] = path.resolve(args[i + 1]); +} +assert.ok(!options.out.startsWith(root + path.sep), '复跑不得覆盖固定报告'); +const bytes = await fs.readFile(path.join(root, 'standard-regression.json')); +assert.equal(createHash('sha256').update(bytes).digest('hex'), '3c20f94d6e7f099cfc4e14d1848c120ae259249bfd47237ee1394de86af712d4', '原始标准回归证据已变更'); +const report = JSON.parse(bytes); +assert.equal(report.records.length, 7); +for (const record of report.records) { + assert.match(record.commit, /^[a-f0-9]{40}$/); + const previous = new Map(record.baseline.map(x => [x.id, x])); + const changes = record.current.filter(x => previous.has(x.id) && x.status !== previous.get(x.id).status).map(x => ({ id: x.id, before: previous.get(x.id).status, after: x.status })); + assert.deepEqual(changes, record.changes); + assert.deepEqual(changes, []); + for (const side of ['baseline', 'current']) { + assert.deepEqual(record[side].filter(x => x.level === 'required' && x.status === 'fail').map(x => x.id), record[side + 'Failures']); + assert.deepEqual(record[side + 'Failures'], []); + } +} +if (mode === 'verify') { + console.log('原始报告摘要与7个SDK的既有规则状态核对通过;未重新扫描或修改SDK。'); +} else { + // 全部基于记录中的不可变提交,不读取用户工作目录的未提交内容。 + const work = await fs.mkdtemp(path.join(os.tmpdir(), 'tracking-standards-')); + const unpack = async (repository, commit, name, paths = []) => { + const target = path.join(work, name); + await fs.mkdir(target); + const archive = path.join(work, name + '.zip'); + execFileSync('git', ['-C', repository, 'archive', '--format=zip', '--output', archive, commit, ...paths]); + // Windows系统tar对既有中文截图路径报错;Python标准库按ZIP的UTF-8文件名解包。 + execFileSync('python', ['-m', 'zipfile', '-e', archive, target]); + return target; + }; + const baselineCommit = 'df2863ec22e73790f35fcf57c673a55afd4a1be0'; + const currentCommit = '063e8c215b1791d43d3ee704bcb83006ddc43611'; + const oldRoot = await unpack(portal, baselineCommit, 'baseline', ['standards/v1', 'tools/sdk-standard-check/src']); + const newRoot = await unpack(portal, currentCommit, 'current', ['standards/v1', 'tools/sdk-standard-check/src']); + // 快照检查器复用门户已安装依赖;不复制生产SDK实现到门户。 + await fs.symlink(path.join(portal, 'node_modules'), path.join(oldRoot, 'node_modules'), 'junction'); + await fs.symlink(path.join(portal, 'node_modules'), path.join(newRoot, 'node_modules'), 'junction'); + const oldScan = (await import(pathToFileURL(path.join(oldRoot, 'tools/sdk-standard-check/src/check.mjs')))).scanRepository; + const newScan = (await import(pathToFileURL(path.join(newRoot, 'tools/sdk-standard-check/src/check.mjs')))).scanRepository; + const records = []; + for (const original of report.records) { + const snapshot = await unpack(path.join(options['repo-root'], original.repository), original.commit, original.repository); + const baseline = await oldScan(snapshot, { standardRoot: path.join(oldRoot, 'standards/v1') }); + const current = await newScan(snapshot, { standardRoot: path.join(newRoot, 'standards/v1') }); + const statuses = findings => findings.map(({ id, level, status }) => ({ id, level, status })); + assert.deepEqual(statuses(baseline.findings), statuses(original.baseline), original.repository + '旧规则状态偏离'); + assert.deepEqual(statuses(current.findings), statuses(original.current), original.repository + '新规则状态偏离'); + records.push({ repository: original.repository, commit: original.commit, baseline: baseline.findings, current: current.findings }); + } + await fs.mkdir(path.dirname(options.out), { recursive: true }); + await fs.writeFile(options.out, JSON.stringify({ verifiedAt: new Date().toISOString(), baselineCommit, currentCommit, snapshots: work, records }, null, 2) + '\n'); + console.log('7个固定提交快照复跑通过:' + options.out + ';临时快照保留于 ' + work); +} From 62bbb2681d02be6f747847a40ea9be4cb453c6c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E9=BB=98=E6=B6=B5?= <21739308@qq.com> Date: Sat, 19 Sep 2026 09:57:06 +0800 Subject: [PATCH 05/11] =?UTF-8?q?=E4=BF=9D=E7=95=99=E8=B7=9F=E8=B8=AA?= =?UTF-8?q?=E6=A0=B8=E5=BF=83=E9=98=B6=E6=AE=B5=E6=A0=87=E5=87=86=E6=A3=80?= =?UTF-8?q?=E6=9F=A5=E5=8E=9F=E5=A7=8B=E6=8A=A5=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sdk-standard/pp-tracking-core-after.json | 248 ++++++++++++++++++ 1 file changed, 248 insertions(+) create mode 100644 reports/sdk-standard/pp-tracking-core-after.json diff --git a/reports/sdk-standard/pp-tracking-core-after.json b/reports/sdk-standard/pp-tracking-core-after.json new file mode 100644 index 0000000..36ea561 --- /dev/null +++ b/reports/sdk-standard/pp-tracking-core-after.json @@ -0,0 +1,248 @@ +{ + "standardVersion": "1.2.0", + "repositories": [ + { + "standardVersion": "1.2.0", + "repository": "web-sdk-PP-Tracking", + "summary": { + "requiredPassed": 2, + "requiredFailed": 16, + "requiredSkipped": 6, + "requiredUnknown": 0, + "requiredNotApplicable": 0, + "recommendedPassed": 0, + "recommendedFailed": 3, + "labs": 1, + "status": "partial" + }, + "findings": [ + { + "id": "ALGORITHM-001", + "level": "required", + "status": "skip", + "message": "算法清单声明身份、家族、来源、许可、输入输出与有状态语义", + "remediation": "按 sdk-manifest.algorithm.yaml 填写 algorithm 字段并记录状态生命周期。", + "evidence": "当前类型为 model,本规则仅适用于 algorithm;未声明清单,沿用旧版模型检查,不豁免模型规则。" + }, + { + "id": "CACHE-001", + "level": "required", + "status": "fail", + "message": "Cache storage, versioning, estimate, and cleanup are declared", + "remediation": "Add cache contract fields and expose cleanup controls in the Demo.", + "path": "AGENTS.md" + }, + { + "id": "DEMO-001", + "level": "required", + "status": "fail", + "message": "A runnable standalone Demo entry exists", + "remediation": "Add a Demo app with a documented start/build command.", + "path": "AGENTS.md" + }, + { + "id": "DEMO-002", + "level": "required", + "status": "fail", + "message": "Demo declares Chinese as its initial language", + "remediation": "Set lang or default language state to zh-CN.", + "path": "AGENTS.md" + }, + { + "id": "DEMO-003", + "level": "required", + "status": "fail", + "message": "Demo provides an in-page Chinese/English toggle", + "remediation": "Add a language toggle that does not alter inference state.", + "path": "AGENTS.md" + }, + { + "id": "DEMO-004", + "level": "required", + "status": "fail", + "message": "Demo exposes current-model and global cache cleanup controls", + "remediation": "Wire clear-current and clear-all actions to the SDK cache API.", + "path": "AGENTS.md" + }, + { + "id": "DEMO-005", + "level": "required", + "status": "fail", + "message": "Demo 展示当前模型或算法的信息、运行信息和耗时", + "remediation": "模型使用 data-sdk-model-info,算法使用 data-sdk-algorithm-info;两者保留 data-sdk-runtime-info 和 data-sdk-timing。", + "path": "AGENTS.md" + }, + { + "id": "DEMO-006", + "level": "required", + "status": "skip", + "message": "算法 Demo 提供状态复位操作", + "remediation": "使用 data-sdk-state-reset 标记复位控件,并按状态生命周期契约连接 reset。", + "evidence": "当前类型为 model,本规则仅适用于 algorithm;未声明清单,沿用旧版模型检查,不豁免模型规则。" + }, + { + "id": "DEPLOY-001", + "level": "required", + "status": "skip", + "message": "The live Demo has a reproducible HTTPS deployment from protected source with least-privilege credentials", + "remediation": "Configure and verify a deployment that meets standards/v1/repository-governance-contract.md, then record its commit and deployment evidence.", + "evidence": "Requires read-only remote-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "DOC-001", + "level": "required", + "status": "fail", + "message": "Chinese-default and English README files link to one another", + "remediation": "Add README.md and README.en.md with reciprocal language links.", + "path": "AGENTS.md" + }, + { + "id": "DOC-002", + "level": "required", + "status": "pass", + "message": "Chinese and English documentation directories are present", + "remediation": "Provide equivalent docs/zh-CN and docs/en quick-start/API guidance.", + "path": "AGENTS.md", + "evidence": "AGENTS.md" + }, + { + "id": "DOC-003", + "level": "required", + "status": "fail", + "message": "README or package metadata exposes GitHub, npm, and live Demo links", + "remediation": "Add repository, package, and Demo URLs to README and manifest.", + "path": "AGENTS.md" + }, + { + "id": "EXAMPLE-001", + "level": "required", + "status": "fail", + "message": "Vanilla TypeScript/DOM portability example exists", + "remediation": "Add examples/vanilla with a runnable README.", + "path": "AGENTS.md" + }, + { + "id": "EXAMPLE-002", + "level": "required", + "status": "fail", + "message": "React reference example exists", + "remediation": "Add examples/react or explicitly record a migration exception.", + "path": "AGENTS.md" + }, + { + "id": "EXAMPLE-003", + "level": "recommended", + "status": "fail", + "message": "Declared target surfaces have runnable examples or explicit unsupported status", + "remediation": "Align examples with manifest target surfaces.", + "path": "AGENTS.md" + }, + { + "id": "GOV-001", + "level": "required", + "status": "skip", + "message": "The default branch has an active least-privilege Ruleset for pull requests, current CI checks, resolved conversations, deletion, and force-push protection", + "remediation": "Configure an active default-branch Ruleset using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "GOV-002", + "level": "required", + "status": "skip", + "message": "Release tags are immutable under an active tag Ruleset", + "remediation": "Protect the documented release tag pattern against updates and deletion, minimize bypass, and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "LABS-001", + "level": "labs", + "status": "skip", + "message": "Experimental backends and NPU claims have explicit evidence and limits", + "remediation": "Mark WebNN/NPU as labs unless a dated verification matrix exists.", + "evidence": "Labs rule is informational" + }, + { + "id": "META-001", + "level": "required", + "status": "fail", + "message": "SDK manifest is present and declares the v1 contract", + "remediation": "Copy standards/v1/templates/sdk-manifest.yaml and fill every required field.", + "path": "AGENTS.md" + }, + { + "id": "META-002", + "level": "required", + "status": "pass", + "message": "package.json declares a public package identity and version", + "remediation": "Add a non-private package with a semver version and repository metadata.", + "path": "AGENTS.md", + "evidence": "AGENTS.md" + }, + { + "id": "MODEL-001", + "level": "required", + "status": "fail", + "message": "Manifest declares model identity, precision, size, source, license, and checksum", + "remediation": "Complete the model and asset fields in sdk-manifest.yaml.", + "path": "AGENTS.md" + }, + { + "id": "PAGES-001", + "level": "required", + "status": "skip", + "message": "A GitHub Pages Demo deploys through GitHub Actions with scoped permissions, environment binding, HTTPS, and concurrency control", + "remediation": "Configure Pages and its deployment workflow using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "PERF-001", + "level": "required", + "status": "fail", + "message": "声明当前 SDK 类型所需的标准耗时字段", + "remediation": "模型保留下载、缓存、校验、会话、推理和总耗时;算法声明 validationMs、predictionMs、associationMs、updateMs、totalMs。", + "path": "AGENTS.md" + }, + { + "id": "RELEASE-001", + "level": "required", + "status": "fail", + "message": "CI workflow runs verification before release", + "remediation": "Add .github/workflows/ci.yml with tests, typecheck, and build.", + "path": "AGENTS.md" + }, + { + "id": "RELEASE-002", + "level": "required", + "status": "fail", + "message": "GitHub Release workflow exists", + "remediation": "Add a tag-triggered release workflow and publish notes.", + "path": "AGENTS.md" + }, + { + "id": "RELEASE-003", + "level": "recommended", + "status": "fail", + "message": "Changelog documents user-visible SDK and Demo changes", + "remediation": "Add CHANGELOG.md with the current release entry.", + "path": "AGENTS.md" + }, + { + "id": "RUNTIME-001", + "level": "required", + "status": "fail", + "message": "Requested/actual backend and execution mode are declared", + "remediation": "Add runtime backends, execution modes, and actual backend reporting.", + "path": "AGENTS.md" + }, + { + "id": "UI-001", + "level": "recommended", + "status": "fail", + "message": "Demo styling references the shared token vocabulary", + "remediation": "Import or copy standards/v1/ui-tokens.json values without introducing conflicting status colors.", + "path": "AGENTS.md" + } + ] + } + ] +} From 72ca1e51a55ff84908c02f907941a4358748f5f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E9=BB=98=E6=B6=B5?= <21739308@qq.com> Date: Sat, 19 Sep 2026 10:15:40 +0800 Subject: [PATCH 06/11] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E6=A0=87=E5=87=86?= =?UTF-8?q?=E5=9B=9E=E5=BD=92=E5=BD=92=E6=A1=A3=E8=BE=93=E5=87=BA=E5=88=AB?= =?UTF-8?q?=E5=90=8D=E4=BF=9D=E6=8A=A4=E5=B9=B6=E6=8B=92=E7=BB=9D=E8=A6=86?= =?UTF-8?q?=E7=9B=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../tracking/2026-09-19-foundation/README.md | 2 + .../check-output-path.mjs | 37 +++++++++++++++++ .../2026-09-19-foundation/output-path.mjs | 40 +++++++++++++++++++ .../verify-standard-regression.mjs | 6 +-- 4 files changed, 82 insertions(+), 3 deletions(-) create mode 100644 reports/tracking/2026-09-19-foundation/check-output-path.mjs create mode 100644 reports/tracking/2026-09-19-foundation/output-path.mjs diff --git a/reports/tracking/2026-09-19-foundation/README.md b/reports/tracking/2026-09-19-foundation/README.md index 82e8cd4..d8ec7a0 100644 --- a/reports/tracking/2026-09-19-foundation/README.md +++ b/reports/tracking/2026-09-19-foundation/README.md @@ -28,6 +28,8 @@ pnpm --config.verify-deps-before-run=false --config.manage-package-manager-versi `verify`固定原JSON SHA256为 `3c20f94d6e7f099cfc4e14d1848c120ae259249bfd47237ee1394de86af712d4`,重新推导7个SDK的差异和失败列表。`rerun`还从Git提交重新导出并比较每条规则id/level/status,新报告保留新执行时间与新证据路径,不覆盖原报告。源码仓库位置由 `--repo-root` 指定,临时快照在系统临时目录;快照在任务结束后保留供检查。Windows系统tar无法正确解包TinyPose的既有中文截图名,入口改用Python标准库zipfile解包git archive ZIP;这不是原始报告扫描失败。 +输出路径会解析现有父目录真实位置,拒绝归档目录本身、Windows大小写和symlink/junction别名,写入前重新检查并以排他方式创建文件。已有输出返回 `EEXIST`,复跑请选择新 `--out` 文件名。定向回归命令为 `node --test reports/tracking/2026-09-19-foundation/check-output-path.mjs`。 + ## 门户和SDK证据 - [portal-test.log](portal-test.log):Task1完整原始日志,11文件93测试通过。 diff --git a/reports/tracking/2026-09-19-foundation/check-output-path.mjs b/reports/tracking/2026-09-19-foundation/check-output-path.mjs new file mode 100644 index 0000000..237072a --- /dev/null +++ b/reports/tracking/2026-09-19-foundation/check-output-path.mjs @@ -0,0 +1,37 @@ +import assert from 'node:assert/strict'; +import fs from 'node:fs/promises'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { spawnSync } from 'node:child_process'; +import { test } from 'node:test'; + +const root = path.dirname(fileURLToPath(import.meta.url)); +const scratch = await fs.mkdtemp(path.resolve(root, '../../../.tmp/output-safety-')); +const alias = path.join(scratch, 'archive-alias'); +await fs.symlink(root, alias, 'junction'); +function run(out) { + return spawnSync(process.execPath, [path.join(root, 'verify-standard-regression.mjs'), 'verify', '--out', out], { encoding: 'utf8' }); +} +function rejected(out) { + const result = run(out); + assert.equal(result.status, 1, '归档目标必须在执行复核前拒绝:' + out); + assert.match(result.stderr, /归档/); +} +test('拒绝规范归档路径及归档目录本身', () => { + rejected(path.join(root, 'standard-regression.json')); rejected(root); +}); +test('拒绝Windows大小写别名', { skip: process.platform !== 'win32' }, () => { + rejected(path.join(root, 'standard-regression.json').toLowerCase()); +}); +test('拒绝junction别名及其尚不存在的子目录', () => { + rejected(path.join(alias, 'standard-regression.json')); rejected(path.join(alias, 'new-directory/new.json')); +}); +test('允许.tmp新输出,写入时仍拒绝归档别名且不覆盖既有文件', async () => { + const output = path.join(scratch, 'new/result.json'); + assert.equal(run(output).status, 0); + const { writeReport } = await import('./output-path.mjs'); + await writeReport(output, '首份输出', root); + await assert.rejects(writeReport(output, '覆盖输出', root), /EEXIST/); + assert.equal(await fs.readFile(output, 'utf8'), '首份输出'); + await assert.rejects(writeReport(path.join(alias, 'new-directory/new.json'), '禁止写入', root), /归档/); +}); diff --git a/reports/tracking/2026-09-19-foundation/output-path.mjs b/reports/tracking/2026-09-19-foundation/output-path.mjs new file mode 100644 index 0000000..d17b6aa --- /dev/null +++ b/reports/tracking/2026-09-19-foundation/output-path.mjs @@ -0,0 +1,40 @@ +import fs from 'node:fs/promises'; +import { lstatSync, realpathSync } from 'node:fs'; +import path from 'node:path'; + +// 目标可能尚不存在:从最近的既有祖先解析junction/symlink,再接回未建部分。 +function resolvedPath(target) { + let ancestor = path.resolve(target); + const missing = []; + while (true) { + try { lstatSync(ancestor); break; } + catch (error) { + if (error.code !== 'ENOENT') throw error; + const parent = path.dirname(ancestor); + if (parent === ancestor) throw error; + missing.unshift(path.basename(ancestor)); + ancestor = parent; + } + } + // 悬空链接不会被当作普通的未建目录;realpath失败时直接拒绝。 + return path.join(realpathSync.native(ancestor), ...missing); +} + +export function protectOutput(output, archive) { + const actualOutput = resolvedPath(output); + const actualArchive = resolvedPath(archive); + const fold = value => process.platform === 'win32' ? value.toLowerCase() : value; + const relative = path.relative(fold(actualArchive), fold(actualOutput)); + if (relative === '' || (!relative.startsWith('..' + path.sep) && relative !== '..' && !path.isAbsolute(relative))) { + throw new Error('复跑输出不得写入固定历史归档,请使用 .tmp 中的新文件'); + } + return actualOutput; +} + +export async function writeReport(output, content, archive) { + const target = protectOutput(output, archive); + await fs.mkdir(path.dirname(target), { recursive: true }); + const checked = protectOutput(target, archive); + // 排他创建阻止已有文件、硬链接及检查后的既有文件别名被覆盖。 + await fs.writeFile(checked, content, { flag: 'wx' }); +} diff --git a/reports/tracking/2026-09-19-foundation/verify-standard-regression.mjs b/reports/tracking/2026-09-19-foundation/verify-standard-regression.mjs index 7c9e82f..925283e 100644 --- a/reports/tracking/2026-09-19-foundation/verify-standard-regression.mjs +++ b/reports/tracking/2026-09-19-foundation/verify-standard-regression.mjs @@ -5,6 +5,7 @@ import os from 'node:os'; import { fileURLToPath, pathToFileURL } from 'node:url'; import { createHash } from 'node:crypto'; import { execFileSync } from 'node:child_process'; +import { protectOutput, writeReport } from './output-path.mjs'; const root = path.dirname(fileURLToPath(import.meta.url)); const portal = path.resolve(root, '../../..'); @@ -17,7 +18,7 @@ for (let i = 0; i < args.length; i += 2) { assert.ok(args[i].startsWith('--') && Object.hasOwn(options, key) && args[i + 1], '参数为 --repo-root 路径、--out 路径'); options[key] = path.resolve(args[i + 1]); } -assert.ok(!options.out.startsWith(root + path.sep), '复跑不得覆盖固定报告'); +protectOutput(options.out, root); const bytes = await fs.readFile(path.join(root, 'standard-regression.json')); assert.equal(createHash('sha256').update(bytes).digest('hex'), '3c20f94d6e7f099cfc4e14d1848c120ae259249bfd47237ee1394de86af712d4', '原始标准回归证据已变更'); const report = JSON.parse(bytes); @@ -66,7 +67,6 @@ if (mode === 'verify') { assert.deepEqual(statuses(current.findings), statuses(original.current), original.repository + '新规则状态偏离'); records.push({ repository: original.repository, commit: original.commit, baseline: baseline.findings, current: current.findings }); } - await fs.mkdir(path.dirname(options.out), { recursive: true }); - await fs.writeFile(options.out, JSON.stringify({ verifiedAt: new Date().toISOString(), baselineCommit, currentCommit, snapshots: work, records }, null, 2) + '\n'); + await writeReport(options.out, JSON.stringify({ verifiedAt: new Date().toISOString(), baselineCommit, currentCommit, snapshots: work, records }, null, 2) + '\n', root); console.log('7个固定提交快照复跑通过:' + options.out + ';临时快照保留于 ' + work); } From 4d79c0299fd6e5dcf76a8b87aae29fabecde9941 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E9=BB=98=E6=B6=B5?= <21739308@qq.com> Date: Sat, 19 Sep 2026 11:15:51 +0800 Subject: [PATCH 07/11] =?UTF-8?q?=E5=AE=8C=E6=88=90=E8=B7=9F=E8=B8=AASDK?= =?UTF-8?q?=E6=9C=AC=E5=9C=B0=E4=BA=A4=E4=BB=98=E5=B9=B6=E5=BD=92=E6=A1=A3?= =?UTF-8?q?=E6=9C=80=E7=BB=88=E5=AE=A1=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...2026-08-17-web-model-sdk-portal-roadmap.md | 4 +- ...-09-13-pp-detection-multi-model-roadmap.md | 2 +- .../2026-09-19-pp-tracking-foundation.md | 54 +++++++++++------- .../tracking/2026-09-19-foundation/README.md | 4 +- .../2026-09-19-foundation/final-review.md | 55 +++++++++++++++++++ 5 files changed, 94 insertions(+), 25 deletions(-) create mode 100644 reports/tracking/2026-09-19-foundation/final-review.md diff --git a/docs/superpowers/plans/2026-08-17-web-model-sdk-portal-roadmap.md b/docs/superpowers/plans/2026-08-17-web-model-sdk-portal-roadmap.md index 79952f0..8ebb41b 100644 --- a/docs/superpowers/plans/2026-08-17-web-model-sdk-portal-roadmap.md +++ b/docs/superpowers/plans/2026-08-17-web-model-sdk-portal-roadmap.md @@ -14,7 +14,7 @@ readiness, not by the number of models mentioned in the catalog. TinyPose 0.3.0、PP-Segmentation 0.1.0 与 PP-RotatedDetection 0.1.0 已正式发布。分割以 PP-YOLOE_seg_s 640 FP32 提供单帧图片、原图框与二值 ROI 掩码;旋转框以 PP-YOLOE-R-s 1024 FP32 提供 DOTA 15 类和原图四点框。两者均完成双源、npm、Release 和 HTTPS Demo 回读;旋转框固定发布证据见[独立 SDK 回执](https://github.com/chenmohan123/web-sdk-PP-RotatedDetection/tree/b54ae15ca124fd111cac6e683409fdbb88a14e13/reports/2026-09-18-release)。门户已登记第七个 SDK,并保留各自桌面验证边界。 -旋转框首版已按可行性结论发布,当前不扩展切片、媒体、手机或 NPU 承诺。多目标跟踪在[前期固定参考评估](../../../reports/tracking/2026-09-18-feasibility/README.md)后,已完成纯算法标准1.2.0和独立 `web-sdk-pp-tracking@0.1.0` 本地首版:ByteTrack高低分机制、数学定义独立实现、Apache-2.0来源说明、实例状态、CPU/main API、双语Demo及Vanilla/React示例。前期参考代码的许可疑点仍在历史报告中;本产品不复制或分发这些代码。29单测、9组Chromium153交互与实际包通过;Chromium151的10/50/100框各600次warm样本和129次独立API更新已归档,[7SDK新旧标准回归](../../../reports/tracking/2026-09-19-foundation/README.md)既有规则状态无变化。当前仅本地交付、待整分支独立审查,未发布npm/Release/在线Demo,不登记第8个在线条目;真实视频MOT精度、手机和远程治理尚未验证。分割精度变体和媒体扩展留待后续,Workflow继续暂缓。 +旋转框首版已按可行性结论发布,当前不扩展切片、媒体、手机或 NPU 承诺。多目标跟踪在[前期固定参考评估](../../../reports/tracking/2026-09-18-feasibility/README.md)后,已完成纯算法标准1.2.0和独立 `web-sdk-pp-tracking@0.1.0` 本地首版:ByteTrack高低分机制、数学定义独立实现、Apache-2.0来源说明、实例状态、CPU/main API、双语Demo及Vanilla/React示例。前期参考代码的许可疑点仍在历史报告中;本产品不复制或分发这些代码。29单测、9组Chromium153交互与实际包通过;Chromium151的10/50/100框各600次warm样本和129次独立API更新已归档,[7SDK新旧标准回归](../../../reports/tracking/2026-09-19-foundation/README.md)既有规则状态无变化。独立整分支审查已通过,当前仅本地交付,未发布npm/Release/在线Demo,不登记第8个在线条目;真实视频MOT精度、手机和远程治理尚未验证。分割精度变体和媒体扩展留待后续,Workflow继续暂缓。 ## Current Baseline @@ -155,4 +155,4 @@ APIs remain separate Labs work and are not prerequisites for the MVP. ## Next Concrete Task -PP-Segmentation 与 PP-RotatedDetection 门户登记均已上线。跟踪独立本地SDK、算法标准和桌面合成验收已完成;下一步独立整分支审查、本地Demo交付,再按明确范围安排发布和真实授权视频质量验证。当前没有已发布的跟踪npm/在线Demo,不进入可用门户目录;390px仅为桌面布局证据。上方各Phase保留为历史总体路线;Workflow实施仍暂缓。 +PP-Segmentation 与 PP-RotatedDetection 门户登记均已上线。跟踪独立本地SDK、算法标准、桌面合成验收及独立整分支审查已完成,本地Demo已交付;下一步按明确范围安排首次发布和真实授权视频质量验证。当前没有已发布的跟踪npm/在线Demo,不进入可用门户目录;390px仅为桌面布局证据。上方各Phase保留为历史总体路线;Workflow实施仍暂缓。 diff --git a/docs/superpowers/plans/2026-09-13-pp-detection-multi-model-roadmap.md b/docs/superpowers/plans/2026-09-13-pp-detection-multi-model-roadmap.md index 87c87c2..2ce3439 100644 --- a/docs/superpowers/plans/2026-09-13-pp-detection-multi-model-roadmap.md +++ b/docs/superpowers/plans/2026-09-13-pp-detection-multi-model-roadmap.md @@ -32,7 +32,7 @@ PP-YOLOE-R-s 单尺度 FP32 已建立独立 SDK,完成四点坐标、原图还原、Blob/RGBA、浏览器预处理、显式 CPU/GPU、Worker、取消/释放、缓存、完整性校验、双源分发与正式发布。极端长宽比、平分排序与大图切片边界仍需另行验证;未经验证的能力不进入当前承诺。 -旋转框任务保持独立,不加入现有轴对齐 Detection API。分割 FP16/量化和媒体能力、HRNet 等姿态扩展保留后续;跟踪已完成独立本地实现和桌面合成验证,当前等待整分支独立审查及后续发布安排,真实授权视频MOT质量尚未验证。继续桌面优先、模型 SDK 的 ModelScope 默认并保留 Hugging Face,以及独立 Demo 统一风格的要求;无权重算法不得伪造模型资产/缓存或 GPU 支持。390px桌面视口不等同手机验证,Workflow仍暂缓。 +旋转框任务保持独立,不加入现有轴对齐 Detection API。分割 FP16/量化和媒体能力、HRNet 等姿态扩展保留后续;跟踪已完成独立本地实现、桌面合成验证和整分支独立审查,本地Demo已交付,后续按明确范围安排首次发布,真实授权视频MOT质量尚未验证。继续桌面优先、模型 SDK 的 ModelScope 默认并保留 Hugging Face,以及独立 Demo 统一风格的要求;无权重算法不得伪造模型资产/缓存或 GPU 支持。390px桌面视口不等同手机验证,Workflow仍暂缓。 后文日期更早的“下一阶段”保留为历史实施记录,当前优先级以本节为准。 diff --git a/docs/superpowers/plans/2026-09-19-pp-tracking-foundation.md b/docs/superpowers/plans/2026-09-19-pp-tracking-foundation.md index 856eecd..4bcd09a 100644 --- a/docs/superpowers/plans/2026-09-19-pp-tracking-foundation.md +++ b/docs/superpowers/plans/2026-09-19-pp-tracking-foundation.md @@ -22,22 +22,22 @@ **Files:** standards/v1的README双语、schema、rules、runtime/demo/performance/docs契约、算法manifest模板和checklist;tools/sdk-standard-check的manifest/discover/rules和测试;skills/web-model-sdk-standard/references/scaffold.md。 **Interfaces:** 生产1.2.0 `kind: algorithm` 与设计定义的algorithm字段;新DEMO-006状态复位、ALGORITHM-001规则;模型与算法分支互斥,旧清单兼容。 -- [ ] 加失败用例:有效算法、缺algorithm、带model/cache、旧版kind算法、伪造kind、缺算法timing、缺复位标记、旧模型缺assets仍失败。 +- [x] 加失败用例:有效算法、缺algorithm、带model/cache、旧版kind算法、伪造kind、缺算法timing、缺复位标记、旧模型缺assets仍失败。 ```ts expect((await scanRepository(algorithmRoot)).findings.filter(x=>x.level==='required'&&x.status==='fail')).toEqual([]); // 将算法清单schemaVersion改为1.1.0后必须有CONFIG-001,不能获得模型规则豁免。 ``` -- [ ] 运行 `pnpm ... sdk:check:test` 确认新增回归会失败,再实现schema条件、适用规则、发现器和模板。不能让无效manifest跳过必需检查;skip需证据说明。 -- [ ] 更新标准双语入口及对应契约,模型要求保留;算法cold/warm指新实例/复用状态,无模型耗时。 -- [ ] 运行 `pnpm ... sdk:check:test`、`pnpm ... test`、`pnpm ... build`;提交 `扩展纯算法SDK标准并保持模型契约兼容`。 +- [x] 运行 `pnpm ... sdk:check:test` 确认新增回归会失败,再实现schema条件、适用规则、发现器和模板。不能让无效manifest跳过必需检查;skip需证据说明。 +- [x] 更新标准双语入口及对应契约,模型要求保留;算法cold/warm指新实例/复用状态,无模型耗时。 +- [x] 运行 `pnpm ... sdk:check:test`、`pnpm ... test`、`pnpm ... build`;提交 `扩展纯算法SDK标准并保持模型契约兼容`。 ### Task 2: 独立跟踪核心与来源证据 **Files:** SDK新建AGENTS.md、package.json、tsconfig/vitest、scripts/build.mjs及check-package.mjs、src/{types,errors,kalman,assignment,tracker,index}.ts、tests/*.test.ts、tests/fixtures/math-reference.json及生成脚本、LICENSE、NOTICE、docs/zh-CN/algorithm.md与docs/en/algorithm.md。 **Interfaces:** createTracker/update/reset/dispose和全部字段/默认值严格依设计;runtime={requestedBackend:'cpu',actualBackend:'cpu',executionMode:'main',runtimeVersion:'web-sdk-pp-tracking@0.1.0'};timings五项。 -- [ ] SDK空目录先运行门户 `pnpm ... sdk:check -- --repo --format json --out reports/sdk-standard/pp-tracking-before.json`,保留预期缺失。 -- [ ] 初始化 `codex/tracking-foundation` 本地Git,先写数学/匹配/生命周期失败测试;不复制其他SDK的runtime。 +- [x] SDK空目录先运行门户 `pnpm ... sdk:check -- --repo --format json --out reports/sdk-standard/pp-tracking-before.json`,保留预期缺失。 +- [x] 初始化 `codex/tracking-foundation` 本地Git,先写数学/匹配/生命周期失败测试;不复制其他SDK的runtime。 ```ts const a=createTracker({minHits:1}), b=createTracker({minHits:1}); const frame={timestampMs:0,imageSize:{width:640,height:480},detections:[{box:{x:20,y:20,width:40,height:80},score:0.9,classId:0}]}; @@ -46,34 +46,46 @@ expect(b.update(frame).tracks[0].id).toBe(1); expect(()=>a.update(frame)).toThrow(); a.reset(); expect(a.update(frame).generation).toBe(1); ``` -- [ ] 实现8维Kalman,文档定义F、H、P/Q/R与dt,使用小型矩阵运算/Joseph covariance保证数值;独立Python/NumPy参考至少预测、连续更新、不同dt。 -- [ ] 实现确定性门限内全局分配(匈牙利或等价),低分不新建、类别隔离、tentative/tracked/lost/removed、时间上限/数目上限;校验和计算事务性提交,dispose幂等。 -- [ ] 补充非法输入/配置、引用隔离、capacity、big-gap、score边界、lost不可低分复活、贪心反例、门限反例测试。跟踪ID只在该实例,移除事件只返回一次。 -- [ ] 运行 `pnpm ... test`、`typecheck`、`build`、`check:package`;记录源码基础/限制和npm pack实际内容;提交核心。 +- [x] 实现8维Kalman,文档定义F、H、P/Q/R与dt,使用小型矩阵运算/Joseph covariance保证数值;独立Python/NumPy参考至少预测、连续更新、不同dt。 +- [x] 实现确定性门限内全局分配(匈牙利或等价),低分不新建、类别隔离、tentative/tracked/lost/removed、时间上限/数目上限;校验和计算事务性提交,dispose幂等。 +- [x] 补充非法输入/配置、引用隔离、capacity、big-gap、score边界、lost不可低分复活、贪心反例、门限反例测试。跟踪ID只在该实例,移除事件只返回一次。 +- [x] 运行 `pnpm ... test`、`typecheck`、`build`、`check:package`;记录源码基础/限制和npm pack实际内容;提交核心。 ### Task 3: 独立Demo、双语文档、示例及发布脚本 **Files:** SDK demo/{index.html,vite.config.ts,ui-tokens.json,src/*}、examples/vanilla与examples/react、README双语、docs双语六组指南、sdk-manifest.yaml、CHANGELOG、.github/workflows/{ci,pages,release}.yml、tests/browser.mjs及Demo数据测试。 **Interfaces:** 消费Task2实际公开API,不改核心契约;manifest按Task1模板。Demo输入JSON={frames:TrackingFrame[]};文件5MiB、帧数3000、每帧100框上限。 -- [ ] 阅读旋转Demo和PP-Detection样式文件作为布局参考,复用门户ui-tokens,不复制推理;实现原创合成序列与schema完整导入校验。 +- [x] 阅读旋转Demo和PP-Detection样式文件作为布局参考,复用门户ui-tokens,不复制推理;实现原创合成序列与schema完整导入校验。 ```ts // 导入失败保留现有序列与结果;回放每步使用frame.timestampMs,seek先reset再顺序推进。 // 当前状态显示CPU / JavaScript / Main;丢失轨迹虚线,无权重/缓存/后端选择器。 ``` -- [ ] 实现品牌栏、紧凑控制、SVG结果/轨迹、播放暂停单步重播、文件导入、结果JSON下载、语言切换;结果优先显示,详情折叠,390px可用。 -- [ ] 从标准模板创建manifest/README/checklist,文档明确低分检测保留、seek/reset和ID非身份;全部未发布链接标记为计划地址,安装使用本地包方式,不声称npm已存在。 -- [ ] Vanilla示例真正调用构建包API,React引用Demo,构建两者;CI测试类型和打包及浏览器,Pages使用官方actions和最小权限;release要求先通过验证后以OIDC发布,首次发布尚待授权。 -- [ ] 浏览器自动覆盖中文/英文、单步/播放暂停/重播、低分/遮挡、导入成功失败、导出、无横向滚动、Vanilla;运行unit、typecheck:demo、build:demo、test:browser及sdk:check;提交完整Demo与文档。 +- [x] 实现品牌栏、紧凑控制、SVG结果/轨迹、播放暂停单步重播、文件导入、结果JSON下载、语言切换;结果优先显示,详情折叠,390px可用。 +- [x] 从标准模板创建manifest/README/checklist,文档明确低分检测保留、seek/reset和ID非身份;全部未发布链接标记为计划地址,安装使用本地包方式,不声称npm已存在。 +- [x] Vanilla示例真正调用构建包API,React引用Demo,构建两者;CI测试类型和打包及浏览器,Pages使用官方actions和最小权限;release要求先通过验证后以OIDC发布,首次发布尚待授权。 +- [x] 浏览器自动覆盖中文/英文、单步/播放暂停/重播、低分/遮挡、导入成功失败、导出、无横向滚动、Vanilla;运行unit、typecheck:demo、build:demo、test:browser及sdk:check;提交完整Demo与文档。 ### Task 4: 桌面验收证据与交付复核 **Files:** SDK scripts/evaluation/*、reports/2026-09-19-desktop/*、docs兼容/性能指南、manifest.verification;门户reports/sdk-standard/pp-tracking-after.json与计划验收记录,以及docs/superpowers/plans/2026-09-13-pp-detection-multi-model-roadmap.md、2026-08-17-web-model-sdk-portal-roadmap.md中的当前跟踪进度。 **Interfaces:** 消费已完成SDK,不能用静态声明替代运行;证据区分机制正确性和浏览器性能,无移动端/MOT精度承诺。 -- [ ] 可复现合成序列性能测量至少10、50、100框,注明浏览器/OS/CPU/日期及样本数、p50/p95;包含cold创建与warm更新,固定输入可重跑,无绝对性能断言。 -- [ ] 记录真实浏览器主线程运行、两种语言、390px、Vanilla及导入导出,保留机器可读摘要和命令。截图放忽略目录,报告只列路径。 -- [ ] 校验npm pack、无runtime依赖和源码来源;运行全部SDK tests/typechecks/builds/browser与门户sdk:check/tests/build。 -- [ ] 更新verification只填实际测试的浏览器版本和设备,sdk:check远程skip如实保留;记录发布尚未执行及真实视频质量未测。 -- [ ] 同步两份总路线的当前状态为实际完成的本地SDK/桌面证据,保留旧阶段历史说明,不把尚未发布的Tracking登记为可用门户条目;归档主代理的7个现有SDK新旧标准检查差异报告。 -- [ ] 提交证据和兼容说明,独立整分支审查后交付本地Demo及可审查代码。 +- [x] 可复现合成序列性能测量至少10、50、100框,注明浏览器/OS/CPU/日期及样本数、p50/p95;包含cold创建与warm更新,固定输入可重跑,无绝对性能断言。 +- [x] 记录真实浏览器主线程运行、两种语言、390px、Vanilla及导入导出,保留机器可读摘要和命令。截图放忽略目录,报告只列路径。 +- [x] 校验npm pack、无runtime依赖和源码来源;运行全部SDK tests/typechecks/builds/browser与门户sdk:check/tests/build。 +- [x] 更新verification只填实际测试的浏览器版本和设备,sdk:check远程skip如实保留;记录发布尚未执行及真实视频质量未测。 +- [x] 同步两份总路线的当前状态为实际完成的本地SDK/桌面证据,保留旧阶段历史说明,不把尚未发布的Tracking登记为可用门户条目;归档主代理的7个现有SDK新旧标准检查差异报告。 +- [x] 提交证据和兼容说明,独立整分支审查后交付本地Demo及可审查代码。 + +## 本地交付记录(2026-09-19) + +四个任务及独立整分支审查已完成,SDK审查提交 `96f32f0`,门户审查提交 `72ca1e5`。 +核心与Demo的完整验证、归档保护修复及标准回归见[验收报告](../../../reports/tracking/2026-09-19-foundation/README.md); +最终审查为0项严重、0项重要、1项轻微,详见[审查记录](../../../reports/tracking/2026-09-19-foundation/final-review.md)。 +轻微项为静态DOM标记的后缀误匹配,当前真实Demo不受影响,留后续检查器修补。 + +本地Demo为 `http://127.0.0.1:4196/`,可用 `npm run dev:demo` 在SDK目录重新启动。 +保留 `codex/tracking-foundation` 和 `codex/tracking-sdk-foundation` 本地分支。 +npm、Release、在线Demo与远程治理尚未发布或核验,真实授权视频MOT质量尚未测量; +Workflow继续暂缓,不登记未发布的第8个在线SDK。 diff --git a/reports/tracking/2026-09-19-foundation/README.md b/reports/tracking/2026-09-19-foundation/README.md index d8ec7a0..79e6364 100644 --- a/reports/tracking/2026-09-19-foundation/README.md +++ b/reports/tracking/2026-09-19-foundation/README.md @@ -41,4 +41,6 @@ pnpm --config.verify-deps-before-run=false --config.manage-package-manager-versi 性能核心提交为518f94a,ESM SHA256为 `fdd721bf78b7dc2ea3c554619418c914041971e37a2274ef3851e251997d4935`。Chromium151.0.7922.34对应性能,Chromium153.0.8010.12对应完整Demo交互,两者没有混为一次验证。390px是桌面视口;不存在真实手机或真实视频MOT精度结论。独立Apache-2.0实现避免分发前期存在来源义务疑点的参考代码,但不宣称上游逐值等价。 -本轮只更新本地证据、兼容说明与两份总路线。待独立整分支审查后交付本地Demo;正式发布、远程治理核验和真实授权序列质量需另行安排。 +本轮只更新本地证据、兼容说明与两份总路线。独立整分支审查已通过,见[最终审查记录](final-review.md):严重0、重要0、轻微1;轻微项是检查器DOM标记后缀误匹配,不影响真实Demo。SDK审查提交为 `96f32f0`,门户审查提交为 `72ca1e5`。 + +本地Demo `http://127.0.0.1:4196/` 已交付;SDK目录运行 `npm run dev:demo` 可重新启动。正式发布、远程治理核验和真实授权序列质量需另行安排,两个本地分支及工作树保留。 diff --git a/reports/tracking/2026-09-19-foundation/final-review.md b/reports/tracking/2026-09-19-foundation/final-review.md new file mode 100644 index 0000000..53885cd --- /dev/null +++ b/reports/tracking/2026-09-19-foundation/final-review.md @@ -0,0 +1,55 @@ +# PP-Tracking 本地首版整分支独立审查 + +日期:2026-09-19。结论:**可按本轮授权范围交付本地版本**。发现共1项:Critical 0、Important 0、Minor/P3 1。该项不影响当前 SDK 或真实 Demo 的运行,也不推翻已完成的浏览器验证。 + +## 范围与审查依据 + +- 门户:`C:/Users/chenm/.codex/worktrees/segmentation-portal/chenmohan123.github.io`,`df2863ec22e73790f35fcf57c673a55afd4a1be0..72ca1e51a55ff84908c02f907941a4358748f5f7`。 +- SDK:`F:/git/00_chenmohan/github/web-sdk-PP-Tracking`,`193bc8825e89784023434a3923a4975b21f5f627..96f32f030398a217e57975bfcaac1dc66a95d228`。 +- 依据 `final-review-context.md`、2026-09-19设计与实施计划、标准入口与受影响契约、四任务报告和两次修复复审。按指定 code-reviewer.md 独立检查,未派子代理。 +- 分批核查冻结提交中的代码、测试、文档、工作流模板及证据;原始大型JSON采用结构化读取。去除审查包说明头并统一换行后,两份冻结diff均与对应Git区间一致(门户使用10行上下文)。产品与Git保持只读,仅写本报告。 + +## 已确认的优点与计划对齐 + +1. **标准与既有模型边界清楚。** schema只允许1.2.0算法分支,model/cache与algorithm互斥;完整清单校验失败时,扫描器向规则层传空清单,不授予类型豁免。模型assets和原必需耗时/缓存要求保留。七SDK固定提交回归记录全部既有规则状态相同,新增算法规则保持不适用skip。 +2. **运行时独立且状态提交一致。** CPU/main结果与实际实现相符,无网络、模型或React生产依赖。校验先于状态操作,计算使用独立工作副本,结果构造成功才提交时钟、ID和轨迹;输出、输入与内部状态隔离。reset/dispose、代次、容量、超时、低分关联和类别隔离与设计一致。 +3. **数学和关联具有可审查的依据。** 八维恒速状态、P/Q/R、Joseph更新、尺寸投影及数值失败边界已文档化,NumPy公式参考与生产消元实现独立。关联将门限放入优化,允许未匹配,最大匹配数后的最小代价目标明确;729矩阵穷举、贪心与门限反例覆盖关键行为。原同框IoU问题修复未加入任意门限容差。 +4. **Demo按公开API消费。** Demo与Vanilla使用公开包入口;JSON导入先全量结构校验,再复位并替换序列;非法文件与参数保留原结果。seek复位顺序重算,播放使用记录时间,语言切换不复位;预测轨迹显示虚线且score为null。参数应用的新实例行为已有明确说明。 +5. **打包与验证范围真实。** ESM/CJS/类型声明通过实际tarball消费,包文件白名单和零生产依赖有运行日志。工作流为未来模板,双语文档、manifest、路线和报告均明确未发布;未将390px等同手机,也未把合成机制或性能当作真实视频MOT精度。 +6. **归档修复与证据链合理。** 两仓输出保护解析既有祖先真实路径、Windows大小写和junction别名,写前重检,使用wx拒绝覆盖。历史JSON摘要固定,当前构建与测量入口一致;新复跑结果另存,未以归档日期冒充重新测量。门户14个e2e如实标为会话摘要,rawLogFile为null;默认dev超时和7条历史Astro hints未被隐去。 + +## 问题 + +### Critical + +无。 + +### Important + +无。 + +### Minor / P3 + +**1. 算法DOM标记的词边界会接受连字符后缀,造成静态检查假阳性。** + +- 位置:`C:/Users/chenm/.codex/worktrees/segmentation-portal/chenmohan123.github.io/tools/sdk-standard-check/src/discover.mjs:63`、`:69`、`:72`。 +- 触发:Demo只有 `data-sdk-state-reset-old`,或三个区域分别只含 `data-sdk-algorithm-info-old`、`data-sdk-runtime-info-old`、`data-sdk-timing-old`。因为连字符不是正则单词字符,现有 `标记名\\b` 仍命中。 +- 影响:DEMO-005/DEMO-006可能通过,但标准要求的完整属性并不存在,按精确属性选择器检查时找不到对应区域或控件。只读Node探针确认四种后缀均返回true。 +- 建议:至少约束完整属性名的前后边界,并加入连字符后缀/属性名前缀负例;若以后要证明真实DOM存在,应在浏览器层校验精确选择器,保持静态检查与交互验证的区别。 +- 定级理由:本轮真实App使用准确的四个标记,9组浏览器交互已经独立验证复位、耗时和状态。标准也明确静态声明不能代替交互证据。因此这是检查器稳健性改进,当前本地交付可不阻断;不应把它升级为SDK功能故障或远程合规结论。 + +## 本次核对与验证依据 + +- 实际读取并核对SDK冻结提交中9份原始证据/清单SHA256,全部匹配验证器固定值;当前8个构建文件摘要全部匹配归档清单。 +- 结构化核对性能的10/50/100框、每档600个warm和3个cold;重新计算warm统计,与原p50/p95/min/max一致。10场景输入和输出帧数逐场景对应,总129次更新。确认性能Chromium151与产品交互Chromium153为不同证据。 +- 结构化核对NumPy fixture四个dt及8维均值/8×8协方差结构;核对锁文件根依赖全部是开发依赖,与package清单一致。 +- 核对门户7SDK原报告固定摘要、逐SDK旧/新required失败列表均为空;before/core-after/after阶段报告分别如实保留18/16/0项required失败。最终17项required通过、7项required skip,状态仅locally-compliant。 +- 核对既有SDK完整verify日志:29单测、类型、真实包、Demo/Vanilla/React构建、9组浏览器交互和零pageerror;门户93单测、18页构建及14个生产preview e2e会话摘要。本次未重复这些套件或性能测量。 +- 针对同框IoU修复可能受中心坐标往返精度影响的具体疑点,使用当前匹配摘要的公开构建入口做4个小数框、门限1的定向只读探针,均保留ID;未发现新增问题。此探针不扩大浏览器或精度声明。 +- 核对Task2/Task4定向修复、测试及复审,原两项Important均已解决;输出路径测试分别4/4的既有结果与当前实现一致。 + +## 建议与交付判断 + +**可交付:是,仅限独立SDK本地首版、算法标准与已有桌面证据。** 当前实现与四任务之间的API、manifest、状态、Demo及证据契约一致,无需为本轮交付追加发布、手机或真实视频验证。计划复选框与“待整分支审查”的收尾文案应在本结论后更新,保留原始证据不变。 + +上述P3建议列入检查器后续修补。首次远程发布、远程治理、真实授权序列质量及其他设备/浏览器验证继续按已经明确的后续范围安排;本结论不表示这些事项已经通过。 From 651f229581218958a6eb1ca90fbf15d32d765591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E9=BB=98=E6=B6=B5?= <21739308@qq.com> Date: Sat, 19 Sep 2026 11:45:02 +0800 Subject: [PATCH 08/11] =?UTF-8?q?=E6=98=8E=E7=A1=AE=E8=B7=9F=E8=B8=AA?= =?UTF-8?q?=E7=9C=9F=E5=AE=9E=E5=BA=8F=E5=88=97=E8=AF=84=E6=B5=8B=E4=B8=8E?= =?UTF-8?q?=E9=A6=96=E7=89=88=E5=8F=91=E5=B8=83=E6=96=B9=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../2026-09-19-pp-tracking-first-release.md | 52 +++++++++++++++++++ .../2026-09-19-pp-tracking-release-design.md | 31 +++++++++++ 2 files changed, 83 insertions(+) create mode 100644 docs/superpowers/plans/2026-09-19-pp-tracking-first-release.md create mode 100644 docs/superpowers/specs/2026-09-19-pp-tracking-release-design.md diff --git a/docs/superpowers/plans/2026-09-19-pp-tracking-first-release.md b/docs/superpowers/plans/2026-09-19-pp-tracking-first-release.md new file mode 100644 index 0000000..08ac9d5 --- /dev/null +++ b/docs/superpowers/plans/2026-09-19-pp-tracking-first-release.md @@ -0,0 +1,52 @@ +# PP-Tracking 首版发布实施计划 + +> **For agentic workers:** REQUIRED SUB-SKILL: 使用superpowers:subagent-driven-development执行本轮任务及独立审查。主代理负责远程发布,子代理不得再派代理。 + +**Goal:** 以真实序列证据发布独立PP-Tracking 0.1.0并登记门户。 +**Architecture:** 本地MOT17公开检测框进入现有SDK,固定TrackEval评分;SDK原子状态及CPU/main接口保持;门户增加算法目录类型,仅发布后登记。 +**Tech Stack:** TypeScript/Node、Python/TrackEval、Playwright、Astro、GitHub Actions、npm OIDC。 +**Spec:** `docs/superpowers/specs/2026-09-19-pp-tracking-release-design.md` + +## Global Constraints + +- SDK根 `F:/git/00_chenmohan/github/web-sdk-PP-Tracking`;门户根 `C:/Users/chenm/.codex/worktrees/segmentation-portal/chenmohan123.github.io`。原门户用户工作区不动。 +- 文档、回复、注释、提交中文,公开指南保留等价英文;手动编辑apply_patch,明确文件git add,禁git add .。 +- pnpm必须附 `--config.verify-deps-before-run=false --config.manage-package-manager-versions=false`。 +- 用户已确认真实评测及首次发布/门户登记,旧AGENTS本地限定在本次明确授权内失效;子代理只做本地,远程由主代理处理。 +- 不复制来源不明Kalman/SORT实现;TrackEval只作为离线MIT评分工具,不进生产依赖。 +- 原MOT检测/GT、逐轨输出、媒体放.tmp;公开来源哈希和汇总,无数据许可扩张。保留既有历史证据。 +- 每项完成有实测证据与审查;不重复未变化的大套测试。sdk:check前后报告写各任务不同路径。 + +### Task 1: 真实检测序列评测与复现 + +**Files:** SDK `scripts/evaluation/mot17/*`、`tests/mot17-adapter.test.*`、`reports/2026-09-19-mot17/*`、`docs/zh-CN/performance.md`、`docs/en/performance.md`、compatibility双语、NOTICE评测来源说明。 +**Interfaces:** 现有createTracker的TrackingFrame与TrackingResult;输入官方MOT17Labels.zip,七段FRCNN默认与低分消融;输出summary.json和双语解读,输入/输出/评分器完整摘要。 + +- [ ] 固定数据包身份、官方训练用途和引用依据、TrackEval MIT源码commit与依赖;保留来源快照于.tmp及JSON摘要,拒绝错误hash。不得读旧Kalman源码。 +- [ ] 用小型原创输入验证适配:MOT帧号/fps到timestamp、越界框裁剪和空框剔除、低分保留、observed+tracked导出、GT不进入SDK。示例断言:`expect(frame.timestampMs).toBe(1000 / fps); expect(result.detections[0].box.x).toBe(0)`;无效文件不得静默截断。 +- [ ] 实现下载/适配/运行/评分的可复跑CLI,默认输出新.tmp目录,固定归档禁止覆盖;用官方MotChallenge2DBox预处理+Identity/CLEAR计算标准指标。评分器用原创完美匹配/换ID/漏检小fixture核对定义。 +- [ ] 跑全部5316帧默认与low=high消融,记录每段及合计IDF1/IDSW/MOTA/FP/FN、运行耗时、容量、原始输出hash;确定性复核同输入同配置非timing输出相同。 +- [ ] Chromium至少一完整真实序列非耗时输出对齐,记录具体版本/CPU/main/日期;不展示未经许可的媒体。 +- [ ] 写真实失败模式和发布建议,运行新增相关测试+必要类型/构建以及前后checker;中文提交,给主代理报告。不做远程发布。 + +### Task 2: 发布候选与纯算法门户支持 + +**Files:** 门户 `standards/v1/portal-contract.md`、`src/lib/registry/{schema,types,labels,query}.ts`及相关测试、`src/pages/models/[slug].astro`、目录筛选、`src/content/models/pp-tracking.yaml`;SDK `AGENTS.md`、README双语、docs双语发布/兼容/性能指南、`sdk-manifest.yaml`、CHANGELOG、`.github/workflows/*`、Demo公开链接文案。 +**Interfaces:** 消费Task1已审查summary;门户kind互斥模型与算法,CPU后端、multi-object-tracking分类、algorithm元数据;PP-Tracking条目仅本地待发布验证,远程合并由主代理控制。 + +- [ ] 先扩标准:模型继续要求assets非空;算法assets=[]且algorithm必填,不接收假模型权重。schema测试覆盖旧7模型、正确算法、缺来源/算法带权重拒绝、模型缺资产拒绝。示例:`expect(modelSchema.safeParse({...tracking, assets:[modelAsset]}).success).toBe(false)`。 +- [ ] 实现目录/详情/筛选算法展示,CPU/JavaScript清楚,保留现有视觉令牌。单SDK详情不导入任何runtime。支持390px布局。 +- [ ] 更新发布候选双语文档与Demo远程链接、真实评测限制、版本0.1.0;旧本地验收报告保持历史原样。AGENTS远程规则改成明确授权下可执行,不给未来无限授权。 +- [ ] 发布工作流统一npm环境,先验证tag版本。支持首次手工npm已存在相同版本时安全核对并跳过重复发布,不能吞掉不一致或真正发布错误;未来版本走OIDC。Pages和CI最小权限,按实际线上/离线边界写说明。 +- [ ] 最终候选完整SDK verify一次;门户checker/相关tests/build与浏览器目录/详情/CPU过滤/390px,保存报告;更新两份路线至待发布候选。两仓分别提交,独立审查后交主代理。 + +### Task 3: 首次发布、线上核验与门户合并 + +**Files:** SDK `reports/2026-09-19-release/*`、双语发布清单;门户路线、发布证据与最终SDK条目。 +**Interfaces:** 已通过审查的SDK候选,GitHub仓库/main/Rulesets/v0.1.0、npm web-sdk-pp-tracking@0.1.0、Pages URL、Trusted Publisher仓库/release.yml/npm。 + +- [ ] 主代理核验远程不存在冲突,创建SDK仓库,建立main基线并通过PR/CI引入审查候选;绑定并API回读默认分支与标签规则、npm/github-pages环境、Pages。 +- [ ] 首次npm发布使用宿主登录;遇服务端要求本人安全验证时立即给当前有效链接并继续独立准备工作。不输出token/OTP,不重复已完成的登录。 +- [ ] 配置Trusted Publishing并回读;发布不可变v0.1.0与GitHub Release,验证Actions、HTTPS Demo、npm dist/tarball实际API消费,保存不含凭据的回执。 +- [ ] 仅公开链接及部署回读通过后推送门户PR,包含此前未合并算法标准基础和本轮算法目录,核对main差异/CI后合并;attach_artifact挂载全部PR。 +- [ ] 生产门户第八条、筛选/详情、Demo回放与中英文、远程治理通过;报告真实评测指标与范围,关闭计划并保留工作树/历史分支。 diff --git a/docs/superpowers/specs/2026-09-19-pp-tracking-release-design.md b/docs/superpowers/specs/2026-09-19-pp-tracking-release-design.md new file mode 100644 index 0000000..7b4d724 --- /dev/null +++ b/docs/superpowers/specs/2026-09-19-pp-tracking-release-design.md @@ -0,0 +1,31 @@ +# PP-Tracking 真实序列评测与首版发布设计 + +用户已确认本轮顺序:真实视频序列评测、处理影响使用的问题、发布0.1.0、门户登记第八个SDK。授权包含GitHub仓库/PR合并、npm、Release、HTTPS Demo与Trusted Publishing;服务端要求的本人验证仍由用户完成。 + +## 范围与工作区 + +SDK:`F:/git/00_chenmohan/github/web-sdk-PP-Tracking`,当前8d4a554;门户沿用隔离工作树 `C:/Users/chenm/.codex/worktrees/segmentation-portal/chenmohan123.github.io`,当前4d79c02。保留原门户用户文件和旧分支;本轮建立codex/tracking-first-release本地分支。SDK独立消费检测框,不接入Detection运行时,不创建Workflow。门户只登记元数据。 + +## 真实序列与评测口径 + +采用MOT17官方无图像压缩包 `https://motchallenge.net/data/MOT17Labels.zip`,10107022字节,SHA256 `0aa79322e91583369f42f17c4d79a0b145380d8732487bba59272048dc82b2b9`。固定七段FRCNN训练序列02/04/05/09/10/11/13,共5316帧;不按结果挑选片段,不下载或发布视频图片。 + +官方原数据页提供公开下载,FAQ明确允许在训练集比较算法设置并要求引用MOT16和相应序列论文。当前站点已静态归档,保留实际页面及可读历史FAQ的URL、检索日期与摘要。未找到覆盖全部素材的统一再分发许可时,不杜撰CC许可证或把数据归入Apache-2.0;仅在本地按公开基准用途评测,公开代码、来源/文件哈希和汇总指标,原检测框、GT及逐轨输出保留忽略目录。若此依据不足以覆盖选中数据的评测用途,先解决来源问题或替换有明确评测授权的数据。 + +固定默认参数运行,不用这些训练序列调参。另做lowScoreThreshold=highScoreThreshold的机制消融,其他参数相同;这是同实现的消融对照,不是官方ByteTrack精度排名。MOT时间按seqinfo原fps,像素坐标通过明确的MOT转SDK适配,越界检测框裁到图像内、空框剔除并统计;GT按官方评分处理,不能用GT选择输入。每帧只将observed且state=tracked的结果写MOT输出,预测lost与未确认tentative不充当检测。 + +用固定提交的MIT TrackEval官方MOTChallenge预处理和Identity/CLEAR指标,记录IDF1、IDSW、MOTA、FP/FN、每序列及合计,不用自造ID变化计数冒充IDSW。记录SDK/输入/评分器hash、固定参数、执行环境、CPU时间、最大输入/轨迹数和容量丢弃数。原始数据、输出及评分日志可本地复跑,汇总报告不得声称MOT17测试集/排行榜成绩或完整端到端速度。至少一个完整真实序列再在桌面Chromium运行,核对与Node非耗时输出相同。 + +发布门槛:全帧无异常、无非有限框/重复ID/容量丢弃,来源/评分定义明确,真实问题与限制记录完整;指标只代表固定公开检测器和这些训练序列,不设事后挑选的准确率门槛或宣称高精度。发现功能错误则定向修复并复测相关证据;真实交叉/机位运动等固有限制如实记录。 + +## 发布及门户 + +SDK保持0.1.0,更新双语指南、Demo链接、CHANGELOG、验证环境和发布清单,保留原合成测量历史。发布工作流先验证tag与包版本,再验证/构建/发布;首次npm引导发布和后续OIDC不能重复发布同版本。Trusted Publisher固定仓库 `chenmohan123/web-sdk-PP-Tracking`、`release.yml`、`npm`环境;首版是否具备provenance以实际回执为准。 + +GitHub仓库公开,默认main,PR与最新CI门禁、会话解决、防强推/删分支、v*不可变标签、无常驻管理员bypass;Pages Actions+HTTPS,部署只给必要权限、串行且绑定提交。本机gh复用APPDATA/GitHub CLI并恢复原GH_CONFIG_DIR。npm本人验证只通过安全页面,不索取验证码或密码。 + +门户先在standards/v1/portal-contract.md补充算法目录契约,再修改registry schema/UI:缺省kind保持model,算法kind=algorithm,task=multi-object-tracking,CPU/JavaScript真实后端,assets空数组且algorithm来源/版本/家族/状态字段必填;模型仍要求非空assets,不得伪造0字节ONNX。展示算法信息与无需权重状态;目录筛选/详情/比较不复制runtime。PP-Tracking公开链接核验通过后才合并正式条目。 + +## 验证与交付 + +SDK修改前后sdk:check;新的数据适配/评分边界使用有意义的定向测试,完整SDK verify在最终发布候选上执行一次。门户schema/查询/详情/列表测试及生产构建浏览器回归,保留七SDK兼容。发布前独立代码审查;发布后GitHub/npm/HTTPS Demo及治理API回读,线上Demo中英文与回放冒烟,npm tarball实际消费验证。完成后同步规划与日期化证据;仅遇到本人安全验证或真实外部阻塞才请求用户参与。 From 9db2871a8339cc585b5cac6bd24190a72429ae38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E9=BB=98=E6=B6=B5?= <21739308@qq.com> Date: Sat, 19 Sep 2026 11:53:58 +0800 Subject: [PATCH 09/11] =?UTF-8?q?=E8=A1=A5=E5=85=85=E9=97=A8=E6=88=B7?= =?UTF-8?q?=E9=83=A8=E7=BD=B2=E6=9D=83=E9=99=90=E4=B8=8E=E4=B8=B2=E8=A1=8C?= =?UTF-8?q?=E5=8F=91=E5=B8=83=E9=AA=8C=E6=94=B6=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../superpowers/plans/2026-09-19-pp-tracking-first-release.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/superpowers/plans/2026-09-19-pp-tracking-first-release.md b/docs/superpowers/plans/2026-09-19-pp-tracking-first-release.md index 08ac9d5..a56761b 100644 --- a/docs/superpowers/plans/2026-09-19-pp-tracking-first-release.md +++ b/docs/superpowers/plans/2026-09-19-pp-tracking-first-release.md @@ -31,13 +31,13 @@ ### Task 2: 发布候选与纯算法门户支持 -**Files:** 门户 `standards/v1/portal-contract.md`、`src/lib/registry/{schema,types,labels,query}.ts`及相关测试、`src/pages/models/[slug].astro`、目录筛选、`src/content/models/pp-tracking.yaml`;SDK `AGENTS.md`、README双语、docs双语发布/兼容/性能指南、`sdk-manifest.yaml`、CHANGELOG、`.github/workflows/*`、Demo公开链接文案。 +**Files:** 门户 `standards/v1/portal-contract.md`、`src/lib/registry/{schema,types,labels,query}.ts`及相关测试、`src/pages/models/[slug].astro`、目录筛选、`src/content/models/pp-tracking.yaml`、`.github/workflows/deploy.yml`;SDK `AGENTS.md`、README双语、docs双语发布/兼容/性能指南、`sdk-manifest.yaml`、CHANGELOG、`.github/workflows/*`、Demo公开链接文案。 **Interfaces:** 消费Task1已审查summary;门户kind互斥模型与算法,CPU后端、multi-object-tracking分类、algorithm元数据;PP-Tracking条目仅本地待发布验证,远程合并由主代理控制。 - [ ] 先扩标准:模型继续要求assets非空;算法assets=[]且algorithm必填,不接收假模型权重。schema测试覆盖旧7模型、正确算法、缺来源/算法带权重拒绝、模型缺资产拒绝。示例:`expect(modelSchema.safeParse({...tracking, assets:[modelAsset]}).success).toBe(false)`。 - [ ] 实现目录/详情/筛选算法展示,CPU/JavaScript清楚,保留现有视觉令牌。单SDK详情不导入任何runtime。支持390px布局。 - [ ] 更新发布候选双语文档与Demo远程链接、真实评测限制、版本0.1.0;旧本地验收报告保持历史原样。AGENTS远程规则改成明确授权下可执行,不给未来无限授权。 -- [ ] 发布工作流统一npm环境,先验证tag版本。支持首次手工npm已存在相同版本时安全核对并跳过重复发布,不能吞掉不一致或真正发布错误;未来版本走OIDC。Pages和CI最小权限,按实际线上/离线边界写说明。 +- [ ] 发布工作流统一npm环境,先验证tag版本。支持首次手工npm已存在相同版本时安全核对并跳过重复发布,不能吞掉不一致或真正发布错误;未来版本走OIDC。Pages和CI最小权限,按实际线上/离线边界写说明。门户deploy.yml的pages/id-token写权限移至deploy job,concurrency.cancel-in-progress=false,以满足现行发布契约并保持原构建命令。 - [ ] 最终候选完整SDK verify一次;门户checker/相关tests/build与浏览器目录/详情/CPU过滤/390px,保存报告;更新两份路线至待发布候选。两仓分别提交,独立审查后交主代理。 ### Task 3: 首次发布、线上核验与门户合并 From 2e8c4582510091956242dc245aba4e1e6881258a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E9=BB=98=E6=B6=B5?= <21739308@qq.com> Date: Sat, 19 Sep 2026 12:48:23 +0800 Subject: [PATCH 10/11] =?UTF-8?q?=E7=99=BB=E8=AE=B0=E8=B7=9F=E8=B8=AA?= =?UTF-8?q?=E5=8F=91=E5=B8=83=E5=80=99=E9=80=89=E5=B9=B6=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E7=BA=AF=E7=AE=97=E6=B3=95=E9=97=A8=E6=88=B7=E5=A5=91=E7=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yml | 8 +- ...2026-08-17-web-model-sdk-portal-roadmap.md | 4 +- ...-09-13-pp-detection-multi-model-roadmap.md | 4 +- .../pp-tracking-2026-09-19-release-after.json | 269 ++++++++++++++++++ ...pp-tracking-2026-09-19-release-before.json | 269 ++++++++++++++++++ .../2026-09-19-release-candidate/README.md | 27 ++ .../tracking-detail-1280.png | Bin 0 -> 125449 bytes .../tracking-detail-390.png | Bin 0 -> 113463 bytes .../tracking-directory-1280.png | Bin 0 -> 53085 bytes .../tracking-directory-390.png | Bin 0 -> 48723 bytes src/components/model/BackendTable.astro | 3 +- .../registry/ModelDirectory.test.tsx | 15 +- src/components/registry/ModelDirectory.tsx | 16 +- src/content/models/pp-tracking.yaml | 39 +++ src/content/models/registry.test.ts | 2 +- src/lib/registry/labels.ts | 3 + src/lib/registry/query.test.ts | 7 + src/lib/registry/query.ts | 2 +- src/lib/registry/schema.test.ts | 28 ++ src/lib/registry/schema.ts | 16 +- src/lib/registry/types.ts | 1 + src/pages/index.astro | 8 +- src/pages/models/[slug].astro | 9 +- src/pages/tasks/[task].astro | 3 +- standards/v1/portal-contract.md | 14 + tests/portal.spec.ts | 35 ++- 26 files changed, 750 insertions(+), 32 deletions(-) create mode 100644 reports/sdk-standard/pp-tracking-2026-09-19-release-after.json create mode 100644 reports/sdk-standard/pp-tracking-2026-09-19-release-before.json create mode 100644 reports/tracking/2026-09-19-release-candidate/README.md create mode 100644 reports/tracking/2026-09-19-release-candidate/tracking-detail-1280.png create mode 100644 reports/tracking/2026-09-19-release-candidate/tracking-detail-390.png create mode 100644 reports/tracking/2026-09-19-release-candidate/tracking-directory-1280.png create mode 100644 reports/tracking/2026-09-19-release-candidate/tracking-directory-390.png create mode 100644 src/content/models/pp-tracking.yaml create mode 100644 src/lib/registry/schema.test.ts diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 3418682..af17c3e 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -5,11 +5,9 @@ on: workflow_dispatch: permissions: contents: read - pages: write - id-token: write concurrency: group: pages - cancel-in-progress: true + cancel-in-progress: false jobs: build: runs-on: ubuntu-latest @@ -24,6 +22,10 @@ jobs: - uses: actions/upload-pages-artifact@v3 with: { path: dist } deploy: + permissions: + contents: read + pages: write + id-token: write environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} diff --git a/docs/superpowers/plans/2026-08-17-web-model-sdk-portal-roadmap.md b/docs/superpowers/plans/2026-08-17-web-model-sdk-portal-roadmap.md index 8ebb41b..24d3f1b 100644 --- a/docs/superpowers/plans/2026-08-17-web-model-sdk-portal-roadmap.md +++ b/docs/superpowers/plans/2026-08-17-web-model-sdk-portal-roadmap.md @@ -14,7 +14,7 @@ readiness, not by the number of models mentioned in the catalog. TinyPose 0.3.0、PP-Segmentation 0.1.0 与 PP-RotatedDetection 0.1.0 已正式发布。分割以 PP-YOLOE_seg_s 640 FP32 提供单帧图片、原图框与二值 ROI 掩码;旋转框以 PP-YOLOE-R-s 1024 FP32 提供 DOTA 15 类和原图四点框。两者均完成双源、npm、Release 和 HTTPS Demo 回读;旋转框固定发布证据见[独立 SDK 回执](https://github.com/chenmohan123/web-sdk-PP-RotatedDetection/tree/b54ae15ca124fd111cac6e683409fdbb88a14e13/reports/2026-09-18-release)。门户已登记第七个 SDK,并保留各自桌面验证边界。 -旋转框首版已按可行性结论发布,当前不扩展切片、媒体、手机或 NPU 承诺。多目标跟踪在[前期固定参考评估](../../../reports/tracking/2026-09-18-feasibility/README.md)后,已完成纯算法标准1.2.0和独立 `web-sdk-pp-tracking@0.1.0` 本地首版:ByteTrack高低分机制、数学定义独立实现、Apache-2.0来源说明、实例状态、CPU/main API、双语Demo及Vanilla/React示例。前期参考代码的许可疑点仍在历史报告中;本产品不复制或分发这些代码。29单测、9组Chromium153交互与实际包通过;Chromium151的10/50/100框各600次warm样本和129次独立API更新已归档,[7SDK新旧标准回归](../../../reports/tracking/2026-09-19-foundation/README.md)既有规则状态无变化。独立整分支审查已通过,当前仅本地交付,未发布npm/Release/在线Demo,不登记第8个在线条目;真实视频MOT精度、手机和远程治理尚未验证。分割精度变体和媒体扩展留待后续,Workflow继续暂缓。 +旋转框首版已按可行性结论发布,当前不扩展切片、媒体、手机或 NPU 承诺。多目标跟踪已进入独立 `web-sdk-pp-tracking@0.1.0` 发布候选:纯算法标准 1.2.0、ByteTrack 高低分思想的数学独立实现、Apache-2.0、实例状态、CPU/main API、双语 Demo 及 Vanilla/React 示例保持原边界。[前期参考评估](../../../reports/tracking/2026-09-18-feasibility/README.md)中的许可疑点保留为历史,本产品不复制或分发旧参考代码;[原本地验收和七 SDK 标准回归](../../../reports/tracking/2026-09-19-foundation/README.md)也不改写。2026-09-19 固定 MOT17 七段 FRCNN 训练序列 5316 帧已评测,默认 IDF1 48.2922%、IDSW 1101、MOTA 44.4010%,消融 IDF1 48.3465%、IDSW 1066;低分续接减少漏检但增加误检和切 ID,不是测试集排行榜或官方复现。门户本地准备第八个算法条目、CPU 分类和无权重详情,生产合并等待 npm/Release/HTTPS Demo 链接可用及远程治理核验。尚不声明已线上发布、手机或跨设备兼容。分割精度变体与媒体扩展保留后续,Workflow 继续暂缓。 ## Current Baseline @@ -155,4 +155,4 @@ APIs remain separate Labs work and are not prerequisites for the MVP. ## Next Concrete Task -PP-Segmentation 与 PP-RotatedDetection 门户登记均已上线。跟踪独立本地SDK、算法标准、桌面合成验收及独立整分支审查已完成,本地Demo已交付;下一步按明确范围安排首次发布和真实授权视频质量验证。当前没有已发布的跟踪npm/在线Demo,不进入可用门户目录;390px仅为桌面布局证据。上方各Phase保留为历史总体路线;Workflow实施仍暂缓。 +PP-Segmentation 与 PP-RotatedDetection 门户登记均已上线。跟踪现为 0.1.0 发布候选,已补充固定 MOT17 训练序列评测及真实身份连续性限制;门户第八条算法记录在本地就绪,生产合并等待 npm/Release/HTTPS Demo 和远程治理核验。390px 仅为桌面布局证据。上方各 Phase 保留为历史总体路线;Workflow 实施仍暂缓。 diff --git a/docs/superpowers/plans/2026-09-13-pp-detection-multi-model-roadmap.md b/docs/superpowers/plans/2026-09-13-pp-detection-multi-model-roadmap.md index 2ce3439..ce1cd97 100644 --- a/docs/superpowers/plans/2026-09-13-pp-detection-multi-model-roadmap.md +++ b/docs/superpowers/plans/2026-09-13-pp-detection-multi-model-roadmap.md @@ -18,7 +18,7 @@ - **TinyPose:0.3.0 已发布。** 三个稳定变体,单帧/人体框 API,独立 Demo 支持图片、本地视频和摄像头。物理摄像头、手机及 NPU 的验证边界保持不变。 - **PP-Segmentation:0.1.0 已发布。** PP-YOLOE_seg_s 640 FP32,单帧 Blob/RGBA、原图框和独立二值 ROI 掩码。64图四模式严格质量验收通过,最小掩码IoU为0.9987084870848708;双源八组合、npm、GitHub Release和HTTPS Demo均已核验。原始官方裁边口径失败记录保留,最终采用原图整数尺寸独立参考。[固定发布记录](https://github.com/chenmohan123/web-sdk-PP-Segmentation/tree/89b350d30305ecbc275780e455d1c115a250570f/reports/2026-09-18-release-readiness)包含许可、双源、质量和交付回执。Trusted Publishing已配置,实际OIDC发布待下一次新版本验证。门户登记为第六个SDK。 - **PP-RotatedDetection:0.1.0 已发布。** PP-YOLOE-R-s 1024 FP32,单帧 Blob/RGBA、DOTA 15 类和原图四点框。双源 × WASM/WebGPU × 主线程/Worker、npm、GitHub Release 与 HTTPS Demo 均已核验;[固定发布记录](https://github.com/chenmohan123/web-sdk-PP-RotatedDetection/tree/b54ae15ca124fd111cac6e683409fdbb88a14e13/reports/2026-09-18-release)保留模型身份、数值和交付回执。门户登记为第七个 SDK;大图切片、媒体、手机与 NPU 不在当前验证范围。 -- **多目标跟踪:独立SDK本地首版与桌面证据已完成,尚未发布。** [前期60组合成评估](../../../reports/tracking/2026-09-18-feasibility/README.md)保留参考实现的许可追溯疑点和身份交换等限制。现采用ByteTrack高低分关联机制,依据数学定义独立实现Kalman、全局分配、实例状态与时间策略,以Apache-2.0提供本地 `web-sdk-pp-tracking@0.1.0`,不复制或分发旧参考代码。纯算法标准1.2.0、框架无关CPU/main API、双语Demo及Vanilla/React示例已完成;29单测、9组Chromium153产品交互和实际包通过,Chromium151的10/50/100框各600次warm性能样本已归档。[本地验收与7SDK标准回归](../../../reports/tracking/2026-09-19-foundation/README.md)保留提交、哈希和边界。尚未发布npm/Release/在线Demo,不登记第8个在线SDK;真实授权视频质量、移动端和远程治理另行验证,Workflow继续暂缓。 +- **多目标跟踪:0.1.0 发布候选,等待远程交付核验。** 纯算法标准 1.2.0、ByteTrack 高低分思想的独立数学实现、CPU/main、有状态 API、双语 Demo 及 Vanilla/React 示例已完成,代码 Apache-2.0,不复制或分发旧参考实现。[前期评估](../../../reports/tracking/2026-09-18-feasibility/README.md)和[原本地验收](../../../reports/tracking/2026-09-19-foundation/README.md)保持历史原貌。新增固定 MOT17 七段 FRCNN 训练序列 5316 帧:默认 IDF1 48.2922%、IDSW 1101、MOTA 44.4010%、FP 4169、FN 57166;低分消融 IDF1 48.3465%、IDSW 1066,说明低分关联不是普遍精度提升。这不是测试集、官方 ByteTrack 排名或视频端到端速度。门户本地准备第八个纯算法条目,远程合并等待 npm、Release、HTTPS Demo 和治理证据;尚不声称线上发布。移动设备及其他后端仍未验证,Workflow 继续暂缓。 ### 已完成:旋转框独立 SDK 可行性 @@ -32,7 +32,7 @@ PP-YOLOE-R-s 单尺度 FP32 已建立独立 SDK,完成四点坐标、原图还原、Blob/RGBA、浏览器预处理、显式 CPU/GPU、Worker、取消/释放、缓存、完整性校验、双源分发与正式发布。极端长宽比、平分排序与大图切片边界仍需另行验证;未经验证的能力不进入当前承诺。 -旋转框任务保持独立,不加入现有轴对齐 Detection API。分割 FP16/量化和媒体能力、HRNet 等姿态扩展保留后续;跟踪已完成独立本地实现、桌面合成验证和整分支独立审查,本地Demo已交付,后续按明确范围安排首次发布,真实授权视频MOT质量尚未验证。继续桌面优先、模型 SDK 的 ModelScope 默认并保留 Hugging Face,以及独立 Demo 统一风格的要求;无权重算法不得伪造模型资产/缓存或 GPU 支持。390px桌面视口不等同手机验证,Workflow仍暂缓。 +旋转框任务保持独立,不加入现有轴对齐 Detection API。分割 FP16/量化和媒体能力、HRNet 等姿态扩展保留后续;跟踪已完成独立实现及固定 MOT17 训练序列评测,现为待远程核验的首次发布候选,精度与身份连续性限制按报告保留。继续桌面优先、模型 SDK 的 ModelScope 默认并保留 Hugging Face,以及独立 Demo 统一风格的要求;无权重算法不得伪造模型资产/缓存或 GPU 支持。390px 桌面视口不等同手机验证,Workflow 仍暂缓。 后文日期更早的“下一阶段”保留为历史实施记录,当前优先级以本节为准。 diff --git a/reports/sdk-standard/pp-tracking-2026-09-19-release-after.json b/reports/sdk-standard/pp-tracking-2026-09-19-release-after.json new file mode 100644 index 0000000..3e63971 --- /dev/null +++ b/reports/sdk-standard/pp-tracking-2026-09-19-release-after.json @@ -0,0 +1,269 @@ +{ + "standardVersion": "1.2.0", + "repositories": [ + { + "standardVersion": "1.2.0", + "repository": "web-sdk-PP-Tracking", + "summary": { + "requiredPassed": 17, + "requiredFailed": 0, + "requiredSkipped": 7, + "requiredUnknown": 0, + "requiredNotApplicable": 0, + "recommendedPassed": 3, + "recommendedFailed": 0, + "labs": 1, + "status": "locally-compliant" + }, + "findings": [ + { + "id": "ALGORITHM-001", + "level": "required", + "status": "pass", + "message": "算法清单声明身份、家族、来源、许可、输入输出与有状态语义", + "remediation": "按 sdk-manifest.algorithm.yaml 填写 algorithm 字段并记录状态生命周期。", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "CACHE-001", + "level": "required", + "status": "skip", + "message": "Cache storage, versioning, estimate, and cleanup are declared", + "remediation": "Add cache contract fields and expose cleanup controls in the Demo.", + "path": "sdk-manifest.yaml", + "evidence": "当前类型为 algorithm,本规则仅适用于 model;清单已通过完整校验。" + }, + { + "id": "DEMO-001", + "level": "required", + "status": "pass", + "message": "A runnable standalone Demo entry exists", + "remediation": "Add a Demo app with a documented start/build command.", + "path": "demo/index.html", + "evidence": "demo/index.html" + }, + { + "id": "DEMO-002", + "level": "required", + "status": "pass", + "message": "Demo declares Chinese as its initial language", + "remediation": "Set lang or default language state to zh-CN.", + "path": "demo/index.html", + "evidence": "demo/index.html, demo/src/App.tsx, examples/vanilla/index.html" + }, + { + "id": "DEMO-003", + "level": "required", + "status": "pass", + "message": "Demo provides an in-page Chinese/English toggle", + "remediation": "Add a language toggle that does not alter inference state.", + "path": "demo/src/App.tsx", + "evidence": "demo/src/App.tsx" + }, + { + "id": "DEMO-004", + "level": "required", + "status": "skip", + "message": "Demo exposes current-model and global cache cleanup controls", + "remediation": "Wire clear-current and clear-all actions to the SDK cache API.", + "path": "sdk-manifest.yaml", + "evidence": "当前类型为 algorithm,本规则仅适用于 model;清单已通过完整校验。" + }, + { + "id": "DEMO-005", + "level": "required", + "status": "pass", + "message": "Demo 展示当前模型或算法的信息、运行信息和耗时", + "remediation": "模型使用 data-sdk-model-info,算法使用 data-sdk-algorithm-info;两者保留 data-sdk-runtime-info 和 data-sdk-timing。", + "path": "demo/src/App.tsx", + "evidence": "demo/src/App.tsx" + }, + { + "id": "DEMO-006", + "level": "required", + "status": "pass", + "message": "算法 Demo 提供状态复位操作", + "remediation": "使用 data-sdk-state-reset 标记复位控件,并按状态生命周期契约连接 reset。", + "path": "demo/src/App.tsx", + "evidence": "demo/src/App.tsx" + }, + { + "id": "DEPLOY-001", + "level": "required", + "status": "skip", + "message": "The live Demo has a reproducible HTTPS deployment from protected source with least-privilege credentials", + "remediation": "Configure and verify a deployment that meets standards/v1/repository-governance-contract.md, then record its commit and deployment evidence.", + "evidence": "Requires read-only remote-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "DOC-001", + "level": "required", + "status": "pass", + "message": "Chinese-default and English README files link to one another", + "remediation": "Add README.md and README.en.md with reciprocal language links.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-002", + "level": "required", + "status": "pass", + "message": "Chinese and English documentation directories are present", + "remediation": "Provide equivalent docs/zh-CN and docs/en quick-start/API guidance.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-003", + "level": "required", + "status": "pass", + "message": "README or package metadata exposes GitHub, npm, and live Demo links", + "remediation": "Add repository, package, and Demo URLs to README and manifest.", + "path": "README.md", + "evidence": "README.md" + }, + { + "id": "EXAMPLE-001", + "level": "required", + "status": "pass", + "message": "Vanilla TypeScript/DOM portability example exists", + "remediation": "Add examples/vanilla with a runnable README.", + "path": "examples/vanilla/README.en.md", + "evidence": "examples/vanilla/README.en.md" + }, + { + "id": "EXAMPLE-002", + "level": "required", + "status": "pass", + "message": "React reference example exists", + "remediation": "Add examples/react or explicitly record a migration exception.", + "path": "examples/react/README.en.md", + "evidence": "examples/react/README.en.md" + }, + { + "id": "EXAMPLE-003", + "level": "recommended", + "status": "pass", + "message": "Declared target surfaces have runnable examples or explicit unsupported status", + "remediation": "Align examples with manifest target surfaces.", + "path": "examples/vanilla/README.en.md", + "evidence": "examples/vanilla/README.en.md, examples/react/README.en.md" + }, + { + "id": "GOV-001", + "level": "required", + "status": "skip", + "message": "The default branch has an active least-privilege Ruleset for pull requests, current CI checks, resolved conversations, deletion, and force-push protection", + "remediation": "Configure an active default-branch Ruleset using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "GOV-002", + "level": "required", + "status": "skip", + "message": "Release tags are immutable under an active tag Ruleset", + "remediation": "Protect the documented release tag pattern against updates and deletion, minimize bypass, and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "LABS-001", + "level": "labs", + "status": "skip", + "message": "Experimental backends and NPU claims have explicit evidence and limits", + "remediation": "Mark WebNN/NPU as labs unless a dated verification matrix exists.", + "evidence": "Labs rule is informational" + }, + { + "id": "META-001", + "level": "required", + "status": "pass", + "message": "SDK manifest is present and declares the v1 contract", + "remediation": "Copy standards/v1/templates/sdk-manifest.yaml and fill every required field.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "META-002", + "level": "required", + "status": "pass", + "message": "package.json declares a public package identity and version", + "remediation": "Add a non-private package with a semver version and repository metadata.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "MODEL-001", + "level": "required", + "status": "skip", + "message": "Manifest declares model identity, precision, size, source, license, and checksum", + "remediation": "Complete the model and asset fields in sdk-manifest.yaml.", + "path": "sdk-manifest.yaml", + "evidence": "当前类型为 algorithm,本规则仅适用于 model;清单已通过完整校验。" + }, + { + "id": "PAGES-001", + "level": "required", + "status": "skip", + "message": "A GitHub Pages Demo deploys through GitHub Actions with scoped permissions, environment binding, HTTPS, and concurrency control", + "remediation": "Configure Pages and its deployment workflow using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "PERF-001", + "level": "required", + "status": "pass", + "message": "声明当前 SDK 类型所需的标准耗时字段", + "remediation": "模型保留下载、缓存、校验、会话、推理和总耗时;算法声明 validationMs、predictionMs、associationMs、updateMs、totalMs。", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "RELEASE-001", + "level": "required", + "status": "pass", + "message": "CI workflow runs verification before release", + "remediation": "Add .github/workflows/ci.yml with tests, typecheck, and build.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "RELEASE-002", + "level": "required", + "status": "pass", + "message": "GitHub Release workflow exists", + "remediation": "Add a tag-triggered release workflow and publish notes.", + "path": ".github/workflows/release.yml", + "evidence": ".github/workflows/release.yml" + }, + { + "id": "RELEASE-003", + "level": "recommended", + "status": "pass", + "message": "Changelog documents user-visible SDK and Demo changes", + "remediation": "Add CHANGELOG.md with the current release entry.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "RUNTIME-001", + "level": "required", + "status": "pass", + "message": "Requested/actual backend and execution mode are declared", + "remediation": "Add runtime backends, execution modes, and actual backend reporting.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "UI-001", + "level": "recommended", + "status": "pass", + "message": "Demo styling references the shared token vocabulary", + "remediation": "Import or copy standards/v1/ui-tokens.json values without introducing conflicting status colors.", + "path": "standards/v1/ui-tokens.json", + "evidence": "standards/v1/ui-tokens.json" + } + ] + } + ] +} diff --git a/reports/sdk-standard/pp-tracking-2026-09-19-release-before.json b/reports/sdk-standard/pp-tracking-2026-09-19-release-before.json new file mode 100644 index 0000000..3e63971 --- /dev/null +++ b/reports/sdk-standard/pp-tracking-2026-09-19-release-before.json @@ -0,0 +1,269 @@ +{ + "standardVersion": "1.2.0", + "repositories": [ + { + "standardVersion": "1.2.0", + "repository": "web-sdk-PP-Tracking", + "summary": { + "requiredPassed": 17, + "requiredFailed": 0, + "requiredSkipped": 7, + "requiredUnknown": 0, + "requiredNotApplicable": 0, + "recommendedPassed": 3, + "recommendedFailed": 0, + "labs": 1, + "status": "locally-compliant" + }, + "findings": [ + { + "id": "ALGORITHM-001", + "level": "required", + "status": "pass", + "message": "算法清单声明身份、家族、来源、许可、输入输出与有状态语义", + "remediation": "按 sdk-manifest.algorithm.yaml 填写 algorithm 字段并记录状态生命周期。", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "CACHE-001", + "level": "required", + "status": "skip", + "message": "Cache storage, versioning, estimate, and cleanup are declared", + "remediation": "Add cache contract fields and expose cleanup controls in the Demo.", + "path": "sdk-manifest.yaml", + "evidence": "当前类型为 algorithm,本规则仅适用于 model;清单已通过完整校验。" + }, + { + "id": "DEMO-001", + "level": "required", + "status": "pass", + "message": "A runnable standalone Demo entry exists", + "remediation": "Add a Demo app with a documented start/build command.", + "path": "demo/index.html", + "evidence": "demo/index.html" + }, + { + "id": "DEMO-002", + "level": "required", + "status": "pass", + "message": "Demo declares Chinese as its initial language", + "remediation": "Set lang or default language state to zh-CN.", + "path": "demo/index.html", + "evidence": "demo/index.html, demo/src/App.tsx, examples/vanilla/index.html" + }, + { + "id": "DEMO-003", + "level": "required", + "status": "pass", + "message": "Demo provides an in-page Chinese/English toggle", + "remediation": "Add a language toggle that does not alter inference state.", + "path": "demo/src/App.tsx", + "evidence": "demo/src/App.tsx" + }, + { + "id": "DEMO-004", + "level": "required", + "status": "skip", + "message": "Demo exposes current-model and global cache cleanup controls", + "remediation": "Wire clear-current and clear-all actions to the SDK cache API.", + "path": "sdk-manifest.yaml", + "evidence": "当前类型为 algorithm,本规则仅适用于 model;清单已通过完整校验。" + }, + { + "id": "DEMO-005", + "level": "required", + "status": "pass", + "message": "Demo 展示当前模型或算法的信息、运行信息和耗时", + "remediation": "模型使用 data-sdk-model-info,算法使用 data-sdk-algorithm-info;两者保留 data-sdk-runtime-info 和 data-sdk-timing。", + "path": "demo/src/App.tsx", + "evidence": "demo/src/App.tsx" + }, + { + "id": "DEMO-006", + "level": "required", + "status": "pass", + "message": "算法 Demo 提供状态复位操作", + "remediation": "使用 data-sdk-state-reset 标记复位控件,并按状态生命周期契约连接 reset。", + "path": "demo/src/App.tsx", + "evidence": "demo/src/App.tsx" + }, + { + "id": "DEPLOY-001", + "level": "required", + "status": "skip", + "message": "The live Demo has a reproducible HTTPS deployment from protected source with least-privilege credentials", + "remediation": "Configure and verify a deployment that meets standards/v1/repository-governance-contract.md, then record its commit and deployment evidence.", + "evidence": "Requires read-only remote-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "DOC-001", + "level": "required", + "status": "pass", + "message": "Chinese-default and English README files link to one another", + "remediation": "Add README.md and README.en.md with reciprocal language links.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-002", + "level": "required", + "status": "pass", + "message": "Chinese and English documentation directories are present", + "remediation": "Provide equivalent docs/zh-CN and docs/en quick-start/API guidance.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "DOC-003", + "level": "required", + "status": "pass", + "message": "README or package metadata exposes GitHub, npm, and live Demo links", + "remediation": "Add repository, package, and Demo URLs to README and manifest.", + "path": "README.md", + "evidence": "README.md" + }, + { + "id": "EXAMPLE-001", + "level": "required", + "status": "pass", + "message": "Vanilla TypeScript/DOM portability example exists", + "remediation": "Add examples/vanilla with a runnable README.", + "path": "examples/vanilla/README.en.md", + "evidence": "examples/vanilla/README.en.md" + }, + { + "id": "EXAMPLE-002", + "level": "required", + "status": "pass", + "message": "React reference example exists", + "remediation": "Add examples/react or explicitly record a migration exception.", + "path": "examples/react/README.en.md", + "evidence": "examples/react/README.en.md" + }, + { + "id": "EXAMPLE-003", + "level": "recommended", + "status": "pass", + "message": "Declared target surfaces have runnable examples or explicit unsupported status", + "remediation": "Align examples with manifest target surfaces.", + "path": "examples/vanilla/README.en.md", + "evidence": "examples/vanilla/README.en.md, examples/react/README.en.md" + }, + { + "id": "GOV-001", + "level": "required", + "status": "skip", + "message": "The default branch has an active least-privilege Ruleset for pull requests, current CI checks, resolved conversations, deletion, and force-push protection", + "remediation": "Configure an active default-branch Ruleset using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "GOV-002", + "level": "required", + "status": "skip", + "message": "Release tags are immutable under an active tag Ruleset", + "remediation": "Protect the documented release tag pattern against updates and deletion, minimize bypass, and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "LABS-001", + "level": "labs", + "status": "skip", + "message": "Experimental backends and NPU claims have explicit evidence and limits", + "remediation": "Mark WebNN/NPU as labs unless a dated verification matrix exists.", + "evidence": "Labs rule is informational" + }, + { + "id": "META-001", + "level": "required", + "status": "pass", + "message": "SDK manifest is present and declares the v1 contract", + "remediation": "Copy standards/v1/templates/sdk-manifest.yaml and fill every required field.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "META-002", + "level": "required", + "status": "pass", + "message": "package.json declares a public package identity and version", + "remediation": "Add a non-private package with a semver version and repository metadata.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "MODEL-001", + "level": "required", + "status": "skip", + "message": "Manifest declares model identity, precision, size, source, license, and checksum", + "remediation": "Complete the model and asset fields in sdk-manifest.yaml.", + "path": "sdk-manifest.yaml", + "evidence": "当前类型为 algorithm,本规则仅适用于 model;清单已通过完整校验。" + }, + { + "id": "PAGES-001", + "level": "required", + "status": "skip", + "message": "A GitHub Pages Demo deploys through GitHub Actions with scoped permissions, environment binding, HTTPS, and concurrency control", + "remediation": "Configure Pages and its deployment workflow using standards/v1/repository-governance-contract.md and capture dated GitHub API evidence.", + "evidence": "Requires read-only github-api verification; offline sdk:check did not evaluate this rule." + }, + { + "id": "PERF-001", + "level": "required", + "status": "pass", + "message": "声明当前 SDK 类型所需的标准耗时字段", + "remediation": "模型保留下载、缓存、校验、会话、推理和总耗时;算法声明 validationMs、predictionMs、associationMs、updateMs、totalMs。", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "RELEASE-001", + "level": "required", + "status": "pass", + "message": "CI workflow runs verification before release", + "remediation": "Add .github/workflows/ci.yml with tests, typecheck, and build.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "RELEASE-002", + "level": "required", + "status": "pass", + "message": "GitHub Release workflow exists", + "remediation": "Add a tag-triggered release workflow and publish notes.", + "path": ".github/workflows/release.yml", + "evidence": ".github/workflows/release.yml" + }, + { + "id": "RELEASE-003", + "level": "recommended", + "status": "pass", + "message": "Changelog documents user-visible SDK and Demo changes", + "remediation": "Add CHANGELOG.md with the current release entry.", + "path": ".github/workflows/ci.yml", + "evidence": ".github/workflows/ci.yml" + }, + { + "id": "RUNTIME-001", + "level": "required", + "status": "pass", + "message": "Requested/actual backend and execution mode are declared", + "remediation": "Add runtime backends, execution modes, and actual backend reporting.", + "path": "sdk-manifest.yaml", + "evidence": "sdk-manifest.yaml" + }, + { + "id": "UI-001", + "level": "recommended", + "status": "pass", + "message": "Demo styling references the shared token vocabulary", + "remediation": "Import or copy standards/v1/ui-tokens.json values without introducing conflicting status colors.", + "path": "standards/v1/ui-tokens.json", + "evidence": "standards/v1/ui-tokens.json" + } + ] + } + ] +} diff --git a/reports/tracking/2026-09-19-release-candidate/README.md b/reports/tracking/2026-09-19-release-candidate/README.md new file mode 100644 index 0000000..3ab0027 --- /dev/null +++ b/reports/tracking/2026-09-19-release-candidate/README.md @@ -0,0 +1,27 @@ +# 2026-09-19 门户纯算法发布候选验证 + +本地分支基于 `9db2871a8339cc585b5cac6bd24190a72429ae38`,仅准备 PP-Tracking 第八条记录。生产合并等待独立 SDK 的 npm、Release、HTTPS Demo 和治理证据,不声明已上线。 + +先扩 `standards/v1/portal-contract.md`,再实现模型/算法互斥 schema:缺省模型继续要求 assets 非空且禁止 algorithm;算法 assets=[],完整 algorithm 元数据必填。保留原七款模型;新增 CPU / JavaScript、多目标跟踪、来源与状态详情;门户无跟踪 runtime。 + +## 测试证据 + +所有 pnpm 命令均带 `--config.verify-deps-before-run=false --config.manage-package-manager-versions=false`。 + +- RED:`pnpm ... test src/lib/registry/schema.test.ts`,4 项中 2 失败:CPU 算法不能解析、模型 algorithm 被静默丢弃。符合新增契约预期。 +- GREEN:`pnpm ... test src/lib/registry src/content/models/registry.test.ts src/components/registry/ModelDirectory.test.tsx`,4 文件 20 项通过。 +- `pnpm ... test`:12 文件 99 项全部通过,含 checker/schema 回归。 +- `pnpm ... build`:Astro check 0 errors、0 warnings、7 hints;21 页面构建成功。hints 为已有历史报告脚本类型提示及原 z.string().url() 弃用提示,未扩大本轮范围。 +- `pnpm ... test:e2e`:16 项全过,覆盖原七款模型与新增算法;1280px/390px 的 CPU 筛选、任务分类、详情来源/状态/无权重、准确远程链接和无横向溢出。环境存在既有 NO_COLOR/FORCE_COLOR 提示,不影响结果。 +- SDK 修改前后 `pnpm ... sdk:check -- --repo F:/git/00_chenmohan/github/web-sdk-PP-Tracking --format json --out <报告路径>` 均退出 0;[修改前](../../sdk-standard/pp-tracking-2026-09-19-release-before.json)、[修改后](../../sdk-standard/pp-tracking-2026-09-19-release-after.json):required 17 pass / 0 fail / 7 skip,recommended 3 pass,仅 locally-compliant。 + +浏览器复用 production preview:`ASTRO_PREVIEW_BACKGROUND=0 node node_modules/astro/bin/astro.mjs preview --host 127.0.0.1 --port 4321`,并使用已安装的 Playwright Chromium(`PLAYWRIGHT_BROWSERS_PATH=F:/git/00_chenmohan/github/web-sdk-PP-Detection/.tmp/dependencies-compatible-browsers`)。没有为本任务修改 dev 服务或依赖。 + +已目检以下截图,内容非空、布局无交叠;390px 只代表桌面窄视口: + +- [1280px 目录](tracking-directory-1280.png) +- [390px 目录](tracking-directory-390.png) +- [1280px 详情](tracking-detail-1280.png) +- [390px 详情](tracking-detail-390.png) + +Pages workflow 的 pages/id-token 写权限移至 deploy job,部署串行、不取消正在进行的部署,保留原构建命令。两份路线已同步至候选及真实 MOT17 训练集证据,Workflow 继续暂缓。 diff --git a/reports/tracking/2026-09-19-release-candidate/tracking-detail-1280.png b/reports/tracking/2026-09-19-release-candidate/tracking-detail-1280.png new file mode 100644 index 0000000000000000000000000000000000000000..2cf31cb251b758a6a89596f606235835690e4db5 GIT binary patch literal 125449 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 literal 0 HcmV?d00001 diff --git a/reports/tracking/2026-09-19-release-candidate/tracking-detail-390.png b/reports/tracking/2026-09-19-release-candidate/tracking-detail-390.png new file mode 100644 index 0000000000000000000000000000000000000000..0f3a2aa05e5dc8fde186dc25434af18648aa33b0 GIT binary patch literal 113463 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�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 literal 0 HcmV?d00001 diff --git a/reports/tracking/2026-09-19-release-candidate/tracking-directory-1280.png b/reports/tracking/2026-09-19-release-candidate/tracking-directory-1280.png new file mode 100644 index 0000000000000000000000000000000000000000..d09c1b777c2118dff158c5beace3e47d75452abc GIT binary patch literal 53085 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 CZ`UFK literal 0 HcmV?d00001 diff --git a/src/components/model/BackendTable.astro b/src/components/model/BackendTable.astro index 9c94911..5e2711d 100644 --- a/src/components/model/BackendTable.astro +++ b/src/components/model/BackendTable.astro @@ -1,6 +1,7 @@ --- import type { ModelData } from '../../lib/registry/types'; +import { backendDisplayNames } from '../../lib/registry/labels'; const { runtime } = Astro.props as Pick; --- -{runtime.backends.map((backend) => )}
后端状态
{backend.name}{backend.status}
+{runtime.backends.map((backend) => )}
后端状态
{backendDisplayNames[backend.name]}{backend.status}
{runtime.verifiedEnvironments.length === 0 &&

当前没有公开设备验证记录;后端状态不代表所有环境兼容。

} diff --git a/src/components/registry/ModelDirectory.test.tsx b/src/components/registry/ModelDirectory.test.tsx index 4728ba9..89e39dd 100644 --- a/src/components/registry/ModelDirectory.test.tsx +++ b/src/components/registry/ModelDirectory.test.tsx @@ -3,6 +3,9 @@ import { fireEvent, render, screen } from '@testing-library/react'; import { describe, expect, it } from 'vitest'; import ModelDirectory from './ModelDirectory'; import type { ModelData } from '../../lib/registry/types'; +import { readFileSync } from 'node:fs'; +import { parse } from 'yaml'; +import { modelSchema } from '../../lib/registry/schema'; const common = { brand: 'baidu', status: 'available', repository: 'https://example.com', license: 'Apache-2.0', package: { name: 'example', version: '1.0.0' }, demo: { url: 'https://example.com/demo', localProcessing: true }, runtime: { backends: [{ name: 'webgpu', status: 'stable' }], capabilities: [], verifiedEnvironments: [] }, io: { input: ['Blob'], output: ['JSON'] }, assets: [{ id: 'fp16', precision: 'fp16', bytes: 1, url: 'https://example.com/model', sha256: 'a'.repeat(64) }], limitations: [] } as const; const models = [ @@ -14,6 +17,16 @@ const models = [ ] as unknown as ModelData[]; describe('ModelDirectory', () => { + it('CPU 过滤显示独立算法及无需模型权重', () => { + const tracking = modelSchema.parse(parse(readFileSync('src/content/models/pp-tracking.yaml', 'utf8'))); + render(); + fireEvent.change(screen.getByRole('combobox', { name: '后端' }), { target: { value: 'cpu' } }); + fireEvent.change(screen.getByRole('combobox', { name: '任务' }), { target: { value: 'multi-object-tracking' } }); + expect(screen.getByText('1 个条目')).toBeVisible(); + expect(screen.getByText('纯算法 · 无需模型权重')).toBeVisible(); + expect(screen.getByRole('link', { name: 'PP-Tracking' })).toHaveAttribute('href', '/models/pp-tracking/'); + expect(screen.queryByText('PP-DocLayoutV3')).not.toBeInTheDocument(); + }); it('旋转框检测分类只展示独立 SDK 并提供详情入口', () => { render(); expect(screen.getByRole('option', { name: '旋转框检测' })).toHaveValue('rotated-detection'); @@ -53,6 +66,6 @@ describe('ModelDirectory', () => { it('shows an honest empty state', () => { render(); fireEvent.change(screen.getByRole('searchbox'), { target: { value: 'not-a-model' } }); - expect(screen.getByText('没有符合条件的模型')).toBeVisible(); + expect(screen.getByText('没有符合条件的 SDK')).toBeVisible(); }); }); diff --git a/src/components/registry/ModelDirectory.tsx b/src/components/registry/ModelDirectory.tsx index 31c9952..b5066fd 100644 --- a/src/components/registry/ModelDirectory.tsx +++ b/src/components/registry/ModelDirectory.tsx @@ -1,7 +1,7 @@ import { useMemo, useState } from 'react'; import { ArrowUpRight, Search } from 'lucide-react'; import { filterModels, type ModelFilters } from '../../lib/registry/query'; -import { taskDisplayNames } from '../../lib/registry/labels'; +import { backendDisplayNames, taskDisplayNames } from '../../lib/registry/labels'; import type { ModelData } from '../../lib/registry/types'; import './model-directory.css'; @@ -15,22 +15,22 @@ export default function ModelDirectory({ models }: { models: ModelData[] }) { const visible = useMemo(() => filterModels(models, filters), [models, filters]); const set = (key: K, value: ModelFilters[K]) => setFilters((current) => ({ ...current, [key]: value })); - return
+ return
- + - +

{visible.length} 个条目

- {visible.length === 0 ?
没有符合条件的模型
:
; diff --git a/src/content/models/pp-tracking.yaml b/src/content/models/pp-tracking.yaml new file mode 100644 index 0000000..097536c --- /dev/null +++ b/src/content/models/pp-tracking.yaml @@ -0,0 +1,39 @@ +id: pp-tracking +kind: algorithm +name: PP-Tracking +brand: self-developed +task: multi-object-tracking +status: beta +summary: 独立实现 ByteTrack 高低分两阶段关联思想的 CPU 主线程多目标跟踪算法,消费检测框并输出有状态轨迹;0.1.0 为待发布候选。 +repository: https://github.com/chenmohan123/web-sdk-PP-Tracking +license: Apache-2.0 +package: { name: web-sdk-pp-tracking, version: 0.1.0 } +demo: { url: 'https://chenmohan123.github.io/web-sdk-PP-Tracking/', localProcessing: true } +runtime: + backends: [{ name: cpu, status: stable }] + capabilities: [main, stateful, reset, detection-input] + verifiedEnvironments: + - browser: Chromium 153.0.8010.12 / Playwright 1.63.0 + os: Windows 11 专业版 10.0.26200 + device: Intel Core i5-10400F @2.90GHz + testedAt: '2026-09-19' +io: + input: [TrackingFrame(时间戳、图像尺寸、原图检测框与分数)] + output: [TrackingResult(轨迹 ID、状态、位置、实际后端与五项耗时)] +assets: [] +algorithm: + id: pp-tracking + version: 0.1.0 + family: ByteTrack 机制多目标跟踪 + source: '独立实现;ByteTrack 论文 https://arxiv.org/abs/2110.06864;不是百度或 ByteTrack 官方移植,来源与实现差异见独立仓库 NOTICE 和算法指南。' + license: Apache-2.0 + input: TrackingFrame + output: TrackingResult + stateful: true +limitations: + - 本地发布候选,npm、GitHub Release 与在线 Demo 待远程核验;门户生产合并须等待目标链接可用。 + - 无需模型权重;只消费已有检测,不含检测模型、视频解码、摄像头、ReID 或相机运动补偿。轨迹 ID 不是个人身份。 + - 2026-09-19 固定 MOT17 七段 FRCNN 训练序列 5316 帧,默认 IDF1 48.2922%、IDSW 1101、MOTA 44.4010%、FP 4169、FN 57166;不是测试集成绩或官方 ByteTrack 排名。 + - 低分消融 IDF1 48.3465%、IDSW 1066;默认低分续接减少漏检但增加误检和切 ID,不承诺普遍精度提升或可靠跨遮挡身份。 + - 状态保存在实例内,seek 或图像尺寸改变前须 reset 并顺序重放;dispose 后不可继续更新。 + - 仅验证 Windows 桌面 CPU/main;390px 为桌面窄视口,手机、Safari、Firefox、微信、Worker、WASM、WebGPU 与 NPU 尚未验证或未实现。 diff --git a/src/content/models/registry.test.ts b/src/content/models/registry.test.ts index 7773c42..ba9308a 100644 --- a/src/content/models/registry.test.ts +++ b/src/content/models/registry.test.ts @@ -8,7 +8,7 @@ describe("model registry", () => { const models = readdirSync("src/content/models") .filter((file) => file.endsWith(".yaml")) .map((file) => modelSchema.parse(parse(readFileSync(`src/content/models/${file}`, "utf8")))); - expect(models).toHaveLength(7); + expect(models).toHaveLength(8); const model = models.find((entry) => entry.id === "pp-tinypose"); expect(model).toBeDefined(); expect(model?.task).toBe("pose-estimation"); diff --git a/src/lib/registry/labels.ts b/src/lib/registry/labels.ts index 8458c01..b66663e 100644 --- a/src/lib/registry/labels.ts +++ b/src/lib/registry/labels.ts @@ -4,4 +4,7 @@ export const taskDisplayNames: Partial> = { 'rotated-detection': '旋转框检测', 'pose-estimation': '人体姿态', 'instance-segmentation': '实例分割', + 'multi-object-tracking': '多目标跟踪', }; + +export const backendDisplayNames = { cpu: 'CPU / JavaScript', wasm: 'wasm', webgpu: 'webgpu', webnn: 'webnn' }; diff --git a/src/lib/registry/query.test.ts b/src/lib/registry/query.test.ts index 42600e9..a8a83a4 100644 --- a/src/lib/registry/query.test.ts +++ b/src/lib/registry/query.test.ts @@ -1,6 +1,9 @@ import { describe, expect, it } from 'vitest'; import { filterModels, groupCounts } from './query'; import type { ModelData } from './types'; +import { readFileSync } from 'node:fs'; +import { parse } from 'yaml'; +import { modelSchema } from './schema'; const common = { brand: 'baidu', status: 'available', repository: 'https://example.com', license: 'Apache-2.0', @@ -18,6 +21,10 @@ const models = [ ] as unknown as ModelData[]; describe('filterModels', () => { + it('按 CPU 和算法家族过滤跟踪 SDK', () => { + const tracking = modelSchema.parse(parse(readFileSync('src/content/models/pp-tracking.yaml', 'utf8'))); + expect(filterModels([...models, tracking], { query: 'ByteTrack', task: 'multi-object-tracking', status: 'all', backend: 'cpu', brand: 'self-developed' }).map(item => item.id)).toEqual(['pp-tracking']); + }); it('combines text, task, status, and backend filters', () => { expect(filterModels(models, { query: 'layout', task: 'document-layout', status: 'available', backend: 'webgpu', brand: 'all' }).map((model) => model.id)).toEqual(['layout']); }); diff --git a/src/lib/registry/query.ts b/src/lib/registry/query.ts index ef27f97..5e34917 100644 --- a/src/lib/registry/query.ts +++ b/src/lib/registry/query.ts @@ -11,7 +11,7 @@ export interface ModelFilters { export function filterModels(models: ModelData[], filters: ModelFilters): ModelData[] { const query = filters.query.trim().toLocaleLowerCase(); return models.filter((model) => { - const haystack = `${model.name} ${model.summary} ${model.package.name}`.toLocaleLowerCase(); + const haystack = `${model.name} ${model.summary} ${model.package.name} ${model.algorithm?.family ?? ''}`.toLocaleLowerCase(); return (!query || haystack.includes(query)) && (filters.brand === 'all' || model.brand === filters.brand) && (filters.task === 'all' || model.task === filters.task) diff --git a/src/lib/registry/schema.test.ts b/src/lib/registry/schema.test.ts new file mode 100644 index 0000000..a52cf98 --- /dev/null +++ b/src/lib/registry/schema.test.ts @@ -0,0 +1,28 @@ +import { readFileSync, readdirSync } from 'node:fs'; +import { parse } from 'yaml'; +import { describe, expect, it } from 'vitest'; +import { modelSchema } from './schema'; + +const model = parse(readFileSync('src/content/models/pp-doclayoutv3.yaml', 'utf8')); +const tracking = { ...model, kind: 'algorithm', task: 'multi-object-tracking', assets: [], + runtime: { backends: [{ name: 'cpu', status: 'stable' }] }, + algorithm: { id: 'pp-tracking', version: '0.1.0', family: 'ByteTrack 机制', source: '独立实现;https://arxiv.org/abs/2110.06864', license: 'Apache-2.0', input: 'TrackingFrame', output: 'TrackingResult', stateful: true } }; + +describe('模型与算法互斥契约', () => { + it('继续接受原有七款模型', () => { + const records = readdirSync('src/content/models').filter(file => file.endsWith('.yaml') && file !== 'pp-tracking.yaml'); + expect(records).toHaveLength(7); + for (const file of records) expect(modelSchema.safeParse(parse(readFileSync(`src/content/models/${file}`, 'utf8'))).success).toBe(true); + }); + it('接受完整无权重 CPU 算法', () => expect(modelSchema.safeParse(tracking).success).toBe(true)); + it('拒绝缺来源、缺元数据及携带模型权重的算法', () => { + expect(modelSchema.safeParse({ ...tracking, algorithm: { ...tracking.algorithm, source: '' } }).success).toBe(false); + expect(modelSchema.safeParse({ ...tracking, algorithm: undefined }).success).toBe(false); + expect(modelSchema.safeParse({ ...tracking, assets: [model.assets[0]] }).success).toBe(false); + }); + it('拒绝模型缺资产、模型携带算法及未知类型', () => { + expect(modelSchema.safeParse({ ...model, assets: [] }).success).toBe(false); + expect(modelSchema.safeParse({ ...model, algorithm: tracking.algorithm }).success).toBe(false); + expect(modelSchema.safeParse({ ...tracking, kind: 'unknown' }).success).toBe(false); + }); +}); diff --git a/src/lib/registry/schema.ts b/src/lib/registry/schema.ts index be9f93a..3395974 100644 --- a/src/lib/registry/schema.ts +++ b/src/lib/registry/schema.ts @@ -3,10 +3,11 @@ import { z } from 'zod'; export const backendStatusSchema = z.enum(['stable', 'fallback', 'experimental']); export const modelSchema = z.object({ + kind: z.enum(['model', 'algorithm']).default('model'), id: z.string().regex(/^[a-z0-9-]+$/), name: z.string().min(1), brand: z.string().regex(/^[a-z0-9-]+$/), - task: z.enum(['ocr', 'document-layout', 'detection', 'rotated-detection', 'pose-estimation', 'instance-segmentation', 'asr', 'tts', 'image-correction', 'vision-language']), + task: z.enum(['ocr', 'document-layout', 'detection', 'rotated-detection', 'pose-estimation', 'instance-segmentation', 'multi-object-tracking', 'asr', 'tts', 'image-correction', 'vision-language']), status: z.enum(['available', 'beta', 'in-development', 'research', 'not-applicable']), summary: z.string().min(20), repository: z.string().url(), @@ -14,11 +15,20 @@ export const modelSchema = z.object({ package: z.object({ name: z.string().min(1), version: z.string().regex(/^\d+\.\d+\.\d+$/) }), demo: z.object({ url: z.string().url(), localProcessing: z.boolean() }), runtime: z.object({ - backends: z.array(z.object({ name: z.enum(['webgpu', 'wasm', 'webnn']), status: backendStatusSchema })).min(1), + backends: z.array(z.object({ name: z.enum(['webgpu', 'wasm', 'webnn', 'cpu']), status: backendStatusSchema })).min(1), capabilities: z.array(z.string()).default([]), verifiedEnvironments: z.array(z.object({ browser: z.string(), os: z.string(), device: z.string(), testedAt: z.string().regex(/^\d{4}-\d{2}-\d{2}$/) })).default([]), }), io: z.object({ input: z.array(z.string()).min(1), output: z.array(z.string()).min(1) }), - assets: z.array(z.object({ id: z.string(), precision: z.string(), bytes: z.number().int().positive(), url: z.string().url(), sha256: z.string().regex(/^[a-f0-9]{64}$/) })).min(1), + assets: z.array(z.object({ id: z.string(), precision: z.string(), bytes: z.number().int().positive(), url: z.string().url(), sha256: z.string().regex(/^[a-f0-9]{64}$/) })), + algorithm: z.object({ id: z.string().trim().min(1), version: z.string().trim().min(1), family: z.string().trim().min(1), source: z.string().trim().min(1), license: z.string().trim().min(1), input: z.string().trim().min(1), output: z.string().trim().min(1), stateful: z.boolean() }).strict().optional(), limitations: z.array(z.string()).default([]), +}).superRefine((entry, context) => { + if (entry.kind === 'algorithm') { + if (!entry.algorithm) context.addIssue({ code: 'custom', path: ['algorithm'], message: '算法必须声明来源及完整元数据' }); + if (entry.assets.length !== 0) context.addIssue({ code: 'custom', path: ['assets'], message: '算法不得声明模型权重' }); + } else { + if (entry.algorithm) context.addIssue({ code: 'custom', path: ['algorithm'], message: '模型不得携带算法元数据' }); + if (entry.assets.length === 0) context.addIssue({ code: 'custom', path: ['assets'], message: '模型必须声明非空资产' }); + } }); diff --git a/src/lib/registry/types.ts b/src/lib/registry/types.ts index 01b2c05..ac116fe 100644 --- a/src/lib/registry/types.ts +++ b/src/lib/registry/types.ts @@ -4,3 +4,4 @@ export type ModelEntry = CollectionEntry<'models'>; export type ModelData = ModelEntry['data']; export type BackendName = ModelData['runtime']['backends'][number]['name']; export type ModelStatus = ModelData['status']; +export type SdkKind = ModelData['kind']; diff --git a/src/pages/index.astro b/src/pages/index.astro index 723bec1..a75685f 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -6,12 +6,12 @@ const models = (await getCollection('models')).map((entry) => entry.data); ---
-

浏览器端模型 SDK 目录

-

从本地优先的角度整理可在浏览器中使用的模型 SDK,帮助开发者快速了解能力、运行约束与集成入口。

+

浏览器端模型与算法 SDK 目录

+

从本地优先的角度整理可在浏览器中使用的模型与算法 SDK,帮助开发者快速了解能力、运行约束与集成入口。

diff --git a/src/pages/models/[slug].astro b/src/pages/models/[slug].astro index d38b99e..a12109c 100644 --- a/src/pages/models/[slug].astro +++ b/src/pages/models/[slug].astro @@ -19,5 +19,12 @@ const npmUrl = `https://www.npmjs.com/package/${data.package.name}`;

{data.brand} · {taskDisplayNames[data.task] ?? data.task}

{data.name}

{data.summary}

{model.id === 'pp-detection' &&

模型选型与对比 →

} {model.id === 'pp-tinypose' &&

规格与精度对比 →

} -

运行能力

模型资源

    {data.assets.map((asset) =>
  • {asset.id} · {mib(asset.bytes)} · SHA-256 已记录
  • )}

限制

    {data.limitations.map((item) =>
  • {item}
  • )}
+

运行能力

+ {data.kind === 'algorithm' && data.algorithm ?

算法信息

无需模型权重

+
算法与版本
{data.algorithm.id} · {data.algorithm.version}
+
家族
{data.algorithm.family}
来源
{data.algorithm.source}
+
许可
{data.algorithm.license}
状态
{data.algorithm.stateful ? '有状态;实例生命周期与复位规则见限制和独立 SDK 文档' : '无状态'}
+
输入
{data.algorithm.input}
输出
{data.algorithm.output}
+
:

模型资源

    {data.assets.map((asset) =>
  • {asset.id} · {mib(asset.bytes)} · SHA-256 已记录
  • )}
} +

限制

    {data.limitations.map((item) =>
  • {item}
  • )}
diff --git a/src/pages/tasks/[task].astro b/src/pages/tasks/[task].astro index 40ca0cf..743a8f1 100644 --- a/src/pages/tasks/[task].astro +++ b/src/pages/tasks/[task].astro @@ -10,5 +10,6 @@ export async function getStaticPaths() { const { task, models } = Astro.props as { task: CollectionEntry<'models'>['data']['task']; models: CollectionEntry<'models'>[] }; const taskLabel = taskDisplayNames[task] ?? task; +const kindLabel = models.every(model => model.data.kind === 'algorithm') ? '算法' : '模型'; --- -

{taskLabel}

+

{taskLabel}

diff --git a/standards/v1/portal-contract.md b/standards/v1/portal-contract.md index 4f3665f..ed73a92 100644 --- a/standards/v1/portal-contract.md +++ b/standards/v1/portal-contract.md @@ -17,3 +17,17 @@ Demo links. Until then, add registry records and recipes rather than nodes. Portal UI may be denser than a Demo, but both consume the same status labels, model/runtime fields, brand bar semantics, links, and [UI tokens](ui-tokens.json). + +## 纯算法目录记录(1.2.0) + +门户记录的 `kind` 缺省为 `model`,兼容已有模型;模型必须保留非空 +`assets`,禁止 `algorithm`。`kind: algorithm` 必须使用空 `assets: []`, +并声明 `algorithm.id/version/family/source/license/input/output/stateful`, +前七项为非空字符串,stateful 为布尔值。不得为算法伪造权重、大小或校验和。 +算法来源必须说明论文或代码来源及独立实现关系,不把自研实现标为官方移植。 + +算法可使用 `cpu` 后端、多目标跟踪使用 `multi-object-tracking` 分类。 +目录和详情展示 CPU / JavaScript、无需模型权重、算法来源和状态语义, +筛选结果及分类页继续链接独立 SDK。门户不得导入跟踪 runtime。 +带日期验证仍只覆盖明确环境;待发布记录必须标明 npm/Release/Demo 待远程核验, +门户生产合并须等待目标链接实际可用。 diff --git a/tests/portal.spec.ts b/tests/portal.spec.ts index a6d90cb..b992ea2 100644 --- a/tests/portal.spec.ts +++ b/tests/portal.spec.ts @@ -1,11 +1,38 @@ import { test, expect } from "@playwright/test"; for (const width of [1280, 390]) { + test(`纯算法在 ${width}px 支持 CPU 分类和完整详情`, async ({ page }) => { + await page.setViewportSize({ width, height: 844 }); + await page.goto('/'); + await expect(page.locator('astro-island[component-url*="ModelDirectory"]')).not.toHaveAttribute('ssr', ''); + await page.getByRole('combobox', { name: '后端', exact: true }).selectOption('cpu'); + await page.getByRole('combobox', { name: '任务', exact: true }).selectOption('multi-object-tracking'); + await expect(page.getByText('1 个条目', { exact: true })).toBeVisible(); + await expect(page.getByText('纯算法 · 无需模型权重')).toBeVisible(); + expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true); + await page.screenshot({ path: `test-results/tracking-directory-${width}.png`, fullPage: true }); + await page.getByRole('link', { name: 'PP-Tracking', exact: true }).click(); + await expect(page.getByRole('heading', { level: 1 })).toHaveText('PP-Tracking'); + await expect(page.getByText('CPU / JavaScript', { exact: true })).toBeVisible(); + await expect(page.getByRole('heading', { name: '模型资源', exact: true })).toHaveCount(0); + await expect(page.getByText('无需模型权重', { exact: true })).toBeVisible(); + await expect(page.getByText(/不是百度或 ByteTrack 官方移植/)).toBeVisible(); + await expect(page.getByText(/有状态;实例生命周期/)).toBeVisible(); + await expect(page.getByText(/默认 IDF1 48.2922%/)).toBeVisible(); + await expect(page.getByRole('link', { name: 'GitHub 仓库', exact: true })).toHaveAttribute('href', 'https://github.com/chenmohan123/web-sdk-PP-Tracking'); + await expect(page.getByRole('link', { name: 'npm 包', exact: true })).toHaveAttribute('href', 'https://www.npmjs.com/package/web-sdk-pp-tracking'); + await expect(page.getByRole('link', { name: '打开在线 Demo', exact: true })).toHaveAttribute('href', 'https://chenmohan123.github.io/web-sdk-PP-Tracking/'); + expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true); + await page.screenshot({ path: `test-results/tracking-detail-${width}.png`, fullPage: true }); + await page.goto('/tasks/multi-object-tracking/'); + await expect(page.getByRole('heading', { level: 1 })).toHaveText('多目标跟踪'); + await expect(page.getByRole('link', { name: 'PP-Tracking' })).toHaveAttribute('href', '/models/pp-tracking/'); + }); test(`旋转框检测在 ${width}px 可筛选、搜索并打开完整发布详情`, async ({ page }) => { await page.setViewportSize({ width, height: 844 }); await page.goto('/'); await expect(page.locator('astro-island[component-url*="ModelDirectory"]')).not.toHaveAttribute('ssr', ''); - await expect(page.getByText('7 个条目', { exact: true })).toBeVisible(); + await expect(page.getByText('8 个条目', { exact: true })).toBeVisible(); await page.getByRole('combobox', { name: '任务', exact: true }).selectOption({ label: '旋转框检测' }); await page.getByRole('searchbox').fill('web-sdk-pp-rotated-detection'); await expect(page.getByText('1 个条目', { exact: true })).toBeVisible(); @@ -33,7 +60,7 @@ for (const width of [1280, 390]) { await page.setViewportSize({ width, height: 844 }); await page.goto('/'); await expect(page.locator('astro-island[component-url*="ModelDirectory"]')).not.toHaveAttribute('ssr', ''); - await expect(page.getByText('7 个条目', { exact: true })).toBeVisible(); + await expect(page.getByText('8 个条目', { exact: true })).toBeVisible(); await page.getByRole('combobox', { name: '任务', exact: true }).selectOption({ label: '实例分割' }); await page.getByRole('searchbox').fill('web-sdk-pp-segmentation'); await expect(page.getByText('1 个条目', { exact: true })).toBeVisible(); @@ -61,7 +88,7 @@ for (const width of [1280, 390]) { await page.setViewportSize({ width, height: 844 }); await page.goto("/"); await expect(page.locator('astro-island[component-url*="ModelDirectory"]')).not.toHaveAttribute("ssr", ""); - await expect(page.getByText("7 个条目", { exact: true })).toBeVisible(); + await expect(page.getByText("8 个条目", { exact: true })).toBeVisible(); await page.getByRole("combobox", { name: "任务", exact: true }).selectOption({ label: "人体姿态" }); await expect(page.getByText("1 个条目", { exact: true })).toBeVisible(); await expect(page.getByRole("link", { name: "PP-DocLayoutV3", exact: true })).toHaveCount(0); @@ -105,7 +132,7 @@ test("homepage exposes the model SDK directory and Models navigation", async ({ await page.goto("/"); await expect(page.getByRole("heading", { level: 1 })).toHaveText( - "浏览器端模型 SDK 目录", + "浏览器端模型与算法 SDK 目录", ); await expect(page.getByRole("navigation")).toContainText("Models"); }); From a307bfd92430729827bf8d1ed24d98104d4f8859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E9=BB=98=E6=B6=B5?= <21739308@qq.com> Date: Sat, 19 Sep 2026 14:00:49 +0800 Subject: [PATCH 11/11] =?UTF-8?q?=E7=99=BB=E8=AE=B0PP-Tracking=E9=A6=96?= =?UTF-8?q?=E7=89=88=E5=8F=91=E5=B8=83=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...2026-08-17-web-model-sdk-portal-roadmap.md | 4 +- ...-09-13-pp-detection-multi-model-roadmap.md | 2 +- .../2026-09-19-pp-tracking-first-release.md | 28 +++++------ reports/tracking/2026-09-19-release/README.md | 19 ++++++++ .../2026-09-19-release/sdk-delivery.json | 47 +++++++++++++++++++ src/content/models/pp-tracking.yaml | 3 +- 6 files changed, 84 insertions(+), 19 deletions(-) create mode 100644 reports/tracking/2026-09-19-release/README.md create mode 100644 reports/tracking/2026-09-19-release/sdk-delivery.json diff --git a/docs/superpowers/plans/2026-08-17-web-model-sdk-portal-roadmap.md b/docs/superpowers/plans/2026-08-17-web-model-sdk-portal-roadmap.md index 24d3f1b..7ec12cc 100644 --- a/docs/superpowers/plans/2026-08-17-web-model-sdk-portal-roadmap.md +++ b/docs/superpowers/plans/2026-08-17-web-model-sdk-portal-roadmap.md @@ -14,7 +14,7 @@ readiness, not by the number of models mentioned in the catalog. TinyPose 0.3.0、PP-Segmentation 0.1.0 与 PP-RotatedDetection 0.1.0 已正式发布。分割以 PP-YOLOE_seg_s 640 FP32 提供单帧图片、原图框与二值 ROI 掩码;旋转框以 PP-YOLOE-R-s 1024 FP32 提供 DOTA 15 类和原图四点框。两者均完成双源、npm、Release 和 HTTPS Demo 回读;旋转框固定发布证据见[独立 SDK 回执](https://github.com/chenmohan123/web-sdk-PP-RotatedDetection/tree/b54ae15ca124fd111cac6e683409fdbb88a14e13/reports/2026-09-18-release)。门户已登记第七个 SDK,并保留各自桌面验证边界。 -旋转框首版已按可行性结论发布,当前不扩展切片、媒体、手机或 NPU 承诺。多目标跟踪已进入独立 `web-sdk-pp-tracking@0.1.0` 发布候选:纯算法标准 1.2.0、ByteTrack 高低分思想的数学独立实现、Apache-2.0、实例状态、CPU/main API、双语 Demo 及 Vanilla/React 示例保持原边界。[前期参考评估](../../../reports/tracking/2026-09-18-feasibility/README.md)中的许可疑点保留为历史,本产品不复制或分发旧参考代码;[原本地验收和七 SDK 标准回归](../../../reports/tracking/2026-09-19-foundation/README.md)也不改写。2026-09-19 固定 MOT17 七段 FRCNN 训练序列 5316 帧已评测,默认 IDF1 48.2922%、IDSW 1101、MOTA 44.4010%,消融 IDF1 48.3465%、IDSW 1066;低分续接减少漏检但增加误检和切 ID,不是测试集排行榜或官方复现。门户本地准备第八个算法条目、CPU 分类和无权重详情,生产合并等待 npm/Release/HTTPS Demo 链接可用及远程治理核验。尚不声明已线上发布、手机或跨设备兼容。分割精度变体与媒体扩展保留后续,Workflow 继续暂缓。 +旋转框首版已按可行性结论发布,当前不扩展切片、媒体、手机或 NPU 承诺。独立 `web-sdk-pp-tracking@0.1.0` 已完成 npm、GitHub Release、HTTPS Demo 和远程治理核验:采用纯算法标准 1.2.0、ByteTrack 高低分思想的数学独立实现、Apache-2.0、实例状态与 CPU/main API,双语 Demo 及 Vanilla/React 示例保持原边界。[前期参考评估](../../../reports/tracking/2026-09-18-feasibility/README.md)中的许可疑点保留为历史,本产品不复制或分发旧参考代码;[原本地验收和七 SDK 标准回归](../../../reports/tracking/2026-09-19-foundation/README.md)也不改写。2026-09-19 固定 MOT17 七段 FRCNN 训练序列 5316 帧已评测,默认 IDF1 48.2922%、IDSW 1101、MOTA 44.4010%,消融 IDF1 48.3465%、IDSW 1066;低分续接减少漏检但增加误检和切 ID,不是测试集排行榜或官方复现。门户第八个算法条目、CPU 分类和无权重详情已准备登记;当前生产站尚未包含第八条,须在门户 PR、合并及生产回读完成后关闭。手机与跨设备兼容仍未声明。分割精度变体与媒体扩展保留后续,Workflow 继续暂缓。 ## Current Baseline @@ -155,4 +155,4 @@ APIs remain separate Labs work and are not prerequisites for the MVP. ## Next Concrete Task -PP-Segmentation 与 PP-RotatedDetection 门户登记均已上线。跟踪现为 0.1.0 发布候选,已补充固定 MOT17 训练序列评测及真实身份连续性限制;门户第八条算法记录在本地就绪,生产合并等待 npm/Release/HTTPS Demo 和远程治理核验。390px 仅为桌面布局证据。上方各 Phase 保留为历史总体路线;Workflow 实施仍暂缓。 +PP-Segmentation 与 PP-RotatedDetection 门户登记均已上线。PP-Tracking 0.1.0 的 npm、GitHub Release、HTTPS Demo 和远程治理已核验,固定 MOT17 训练序列评测及真实身份连续性限制保持不变;门户第八条算法记录已准备登记,当前生产站尚未包含该条目,后续完成门户 PR、合并与生产回读。390px 仅为桌面布局证据。上方各 Phase 保留为历史总体路线;Workflow 实施仍暂缓。 diff --git a/docs/superpowers/plans/2026-09-13-pp-detection-multi-model-roadmap.md b/docs/superpowers/plans/2026-09-13-pp-detection-multi-model-roadmap.md index ce1cd97..e150bc6 100644 --- a/docs/superpowers/plans/2026-09-13-pp-detection-multi-model-roadmap.md +++ b/docs/superpowers/plans/2026-09-13-pp-detection-multi-model-roadmap.md @@ -18,7 +18,7 @@ - **TinyPose:0.3.0 已发布。** 三个稳定变体,单帧/人体框 API,独立 Demo 支持图片、本地视频和摄像头。物理摄像头、手机及 NPU 的验证边界保持不变。 - **PP-Segmentation:0.1.0 已发布。** PP-YOLOE_seg_s 640 FP32,单帧 Blob/RGBA、原图框和独立二值 ROI 掩码。64图四模式严格质量验收通过,最小掩码IoU为0.9987084870848708;双源八组合、npm、GitHub Release和HTTPS Demo均已核验。原始官方裁边口径失败记录保留,最终采用原图整数尺寸独立参考。[固定发布记录](https://github.com/chenmohan123/web-sdk-PP-Segmentation/tree/89b350d30305ecbc275780e455d1c115a250570f/reports/2026-09-18-release-readiness)包含许可、双源、质量和交付回执。Trusted Publishing已配置,实际OIDC发布待下一次新版本验证。门户登记为第六个SDK。 - **PP-RotatedDetection:0.1.0 已发布。** PP-YOLOE-R-s 1024 FP32,单帧 Blob/RGBA、DOTA 15 类和原图四点框。双源 × WASM/WebGPU × 主线程/Worker、npm、GitHub Release 与 HTTPS Demo 均已核验;[固定发布记录](https://github.com/chenmohan123/web-sdk-PP-RotatedDetection/tree/b54ae15ca124fd111cac6e683409fdbb88a14e13/reports/2026-09-18-release)保留模型身份、数值和交付回执。门户登记为第七个 SDK;大图切片、媒体、手机与 NPU 不在当前验证范围。 -- **多目标跟踪:0.1.0 发布候选,等待远程交付核验。** 纯算法标准 1.2.0、ByteTrack 高低分思想的独立数学实现、CPU/main、有状态 API、双语 Demo 及 Vanilla/React 示例已完成,代码 Apache-2.0,不复制或分发旧参考实现。[前期评估](../../../reports/tracking/2026-09-18-feasibility/README.md)和[原本地验收](../../../reports/tracking/2026-09-19-foundation/README.md)保持历史原貌。新增固定 MOT17 七段 FRCNN 训练序列 5316 帧:默认 IDF1 48.2922%、IDSW 1101、MOTA 44.4010%、FP 4169、FN 57166;低分消融 IDF1 48.3465%、IDSW 1066,说明低分关联不是普遍精度提升。这不是测试集、官方 ByteTrack 排名或视频端到端速度。门户本地准备第八个纯算法条目,远程合并等待 npm、Release、HTTPS Demo 和治理证据;尚不声称线上发布。移动设备及其他后端仍未验证,Workflow 继续暂缓。 +- **多目标跟踪:0.1.0 已发布,门户第八条准备登记。** npm、GitHub Release、HTTPS Demo 与远程治理已完成核验;SDK 采用纯算法标准 1.2.0、ByteTrack 高低分思想的独立数学实现、CPU/main、有状态 API、双语 Demo 及 Vanilla/React 示例,代码 Apache-2.0,不复制或分发旧参考实现。[前期评估](../../../reports/tracking/2026-09-18-feasibility/README.md)和[原本地验收](../../../reports/tracking/2026-09-19-foundation/README.md)保持历史原貌。固定 MOT17 七段 FRCNN 训练序列 5316 帧:默认 IDF1 48.2922%、IDSW 1101、MOTA 44.4010%、FP 4169、FN 57166;低分消融 IDF1 48.3465%、IDSW 1066,说明低分关联不是普遍精度提升。这不是测试集、官方 ByteTrack 排名或视频端到端速度。门户第八个纯算法条目已准备登记,当前生产站尚未包含该条目,须待门户 PR、合并和生产回读完成后关闭。移动设备及其他后端仍未验证,Workflow 继续暂缓。 ### 已完成:旋转框独立 SDK 可行性 diff --git a/docs/superpowers/plans/2026-09-19-pp-tracking-first-release.md b/docs/superpowers/plans/2026-09-19-pp-tracking-first-release.md index a56761b..f61c0e3 100644 --- a/docs/superpowers/plans/2026-09-19-pp-tracking-first-release.md +++ b/docs/superpowers/plans/2026-09-19-pp-tracking-first-release.md @@ -22,31 +22,31 @@ **Files:** SDK `scripts/evaluation/mot17/*`、`tests/mot17-adapter.test.*`、`reports/2026-09-19-mot17/*`、`docs/zh-CN/performance.md`、`docs/en/performance.md`、compatibility双语、NOTICE评测来源说明。 **Interfaces:** 现有createTracker的TrackingFrame与TrackingResult;输入官方MOT17Labels.zip,七段FRCNN默认与低分消融;输出summary.json和双语解读,输入/输出/评分器完整摘要。 -- [ ] 固定数据包身份、官方训练用途和引用依据、TrackEval MIT源码commit与依赖;保留来源快照于.tmp及JSON摘要,拒绝错误hash。不得读旧Kalman源码。 -- [ ] 用小型原创输入验证适配:MOT帧号/fps到timestamp、越界框裁剪和空框剔除、低分保留、observed+tracked导出、GT不进入SDK。示例断言:`expect(frame.timestampMs).toBe(1000 / fps); expect(result.detections[0].box.x).toBe(0)`;无效文件不得静默截断。 -- [ ] 实现下载/适配/运行/评分的可复跑CLI,默认输出新.tmp目录,固定归档禁止覆盖;用官方MotChallenge2DBox预处理+Identity/CLEAR计算标准指标。评分器用原创完美匹配/换ID/漏检小fixture核对定义。 -- [ ] 跑全部5316帧默认与low=high消融,记录每段及合计IDF1/IDSW/MOTA/FP/FN、运行耗时、容量、原始输出hash;确定性复核同输入同配置非timing输出相同。 -- [ ] Chromium至少一完整真实序列非耗时输出对齐,记录具体版本/CPU/main/日期;不展示未经许可的媒体。 -- [ ] 写真实失败模式和发布建议,运行新增相关测试+必要类型/构建以及前后checker;中文提交,给主代理报告。不做远程发布。 +- [x] 固定数据包身份、官方训练用途和引用依据、TrackEval MIT源码commit与依赖;保留来源快照于.tmp及JSON摘要,拒绝错误hash。不得读旧Kalman源码。 +- [x] 用小型原创输入验证适配:MOT帧号/fps到timestamp、越界框裁剪和空框剔除、低分保留、observed+tracked导出、GT不进入SDK。示例断言:`expect(frame.timestampMs).toBe(1000 / fps); expect(result.detections[0].box.x).toBe(0)`;无效文件不得静默截断。 +- [x] 实现下载/适配/运行/评分的可复跑CLI,默认输出新.tmp目录,固定归档禁止覆盖;用官方MotChallenge2DBox预处理+Identity/CLEAR计算标准指标。评分器用原创完美匹配/换ID/漏检小fixture核对定义。 +- [x] 跑全部5316帧默认与low=high消融,记录每段及合计IDF1/IDSW/MOTA/FP/FN、运行耗时、容量、原始输出hash;确定性复核同输入同配置非timing输出相同。 +- [x] Chromium至少一完整真实序列非耗时输出对齐,记录具体版本/CPU/main/日期;不展示未经许可的媒体。 +- [x] 写真实失败模式和发布建议,运行新增相关测试+必要类型/构建以及前后checker;中文提交,给主代理报告。不做远程发布。 ### Task 2: 发布候选与纯算法门户支持 **Files:** 门户 `standards/v1/portal-contract.md`、`src/lib/registry/{schema,types,labels,query}.ts`及相关测试、`src/pages/models/[slug].astro`、目录筛选、`src/content/models/pp-tracking.yaml`、`.github/workflows/deploy.yml`;SDK `AGENTS.md`、README双语、docs双语发布/兼容/性能指南、`sdk-manifest.yaml`、CHANGELOG、`.github/workflows/*`、Demo公开链接文案。 **Interfaces:** 消费Task1已审查summary;门户kind互斥模型与算法,CPU后端、multi-object-tracking分类、algorithm元数据;PP-Tracking条目仅本地待发布验证,远程合并由主代理控制。 -- [ ] 先扩标准:模型继续要求assets非空;算法assets=[]且algorithm必填,不接收假模型权重。schema测试覆盖旧7模型、正确算法、缺来源/算法带权重拒绝、模型缺资产拒绝。示例:`expect(modelSchema.safeParse({...tracking, assets:[modelAsset]}).success).toBe(false)`。 -- [ ] 实现目录/详情/筛选算法展示,CPU/JavaScript清楚,保留现有视觉令牌。单SDK详情不导入任何runtime。支持390px布局。 -- [ ] 更新发布候选双语文档与Demo远程链接、真实评测限制、版本0.1.0;旧本地验收报告保持历史原样。AGENTS远程规则改成明确授权下可执行,不给未来无限授权。 -- [ ] 发布工作流统一npm环境,先验证tag版本。支持首次手工npm已存在相同版本时安全核对并跳过重复发布,不能吞掉不一致或真正发布错误;未来版本走OIDC。Pages和CI最小权限,按实际线上/离线边界写说明。门户deploy.yml的pages/id-token写权限移至deploy job,concurrency.cancel-in-progress=false,以满足现行发布契约并保持原构建命令。 -- [ ] 最终候选完整SDK verify一次;门户checker/相关tests/build与浏览器目录/详情/CPU过滤/390px,保存报告;更新两份路线至待发布候选。两仓分别提交,独立审查后交主代理。 +- [x] 先扩标准:模型继续要求assets非空;算法assets=[]且algorithm必填,不接收假模型权重。schema测试覆盖旧7模型、正确算法、缺来源/算法带权重拒绝、模型缺资产拒绝。示例:`expect(modelSchema.safeParse({...tracking, assets:[modelAsset]}).success).toBe(false)`。 +- [x] 实现目录/详情/筛选算法展示,CPU/JavaScript清楚,保留现有视觉令牌。单SDK详情不导入任何runtime。支持390px布局。 +- [x] 更新发布候选双语文档与Demo远程链接、真实评测限制、版本0.1.0;旧本地验收报告保持历史原样。AGENTS远程规则改成明确授权下可执行,不给未来无限授权。 +- [x] 发布工作流统一npm环境,先验证tag版本。支持首次手工npm已存在相同版本时安全核对并跳过重复发布,不能吞掉不一致或真正发布错误;未来版本走OIDC。Pages和CI最小权限,按实际线上/离线边界写说明。门户deploy.yml的pages/id-token写权限移至deploy job,concurrency.cancel-in-progress=false,以满足现行发布契约并保持原构建命令。 +- [x] 最终候选完整SDK verify一次;门户checker/相关tests/build与浏览器目录/详情/CPU过滤/390px,保存报告;更新两份路线至待发布候选。两仓分别提交,独立审查后交主代理。 ### Task 3: 首次发布、线上核验与门户合并 **Files:** SDK `reports/2026-09-19-release/*`、双语发布清单;门户路线、发布证据与最终SDK条目。 **Interfaces:** 已通过审查的SDK候选,GitHub仓库/main/Rulesets/v0.1.0、npm web-sdk-pp-tracking@0.1.0、Pages URL、Trusted Publisher仓库/release.yml/npm。 -- [ ] 主代理核验远程不存在冲突,创建SDK仓库,建立main基线并通过PR/CI引入审查候选;绑定并API回读默认分支与标签规则、npm/github-pages环境、Pages。 -- [ ] 首次npm发布使用宿主登录;遇服务端要求本人安全验证时立即给当前有效链接并继续独立准备工作。不输出token/OTP,不重复已完成的登录。 -- [ ] 配置Trusted Publishing并回读;发布不可变v0.1.0与GitHub Release,验证Actions、HTTPS Demo、npm dist/tarball实际API消费,保存不含凭据的回执。 +- [x] 主代理核验远程不存在冲突,创建SDK仓库,建立main基线并通过PR/CI引入审查候选;绑定并API回读默认分支与标签规则、npm/github-pages环境、Pages。 +- [x] 首次npm发布使用宿主登录;遇服务端要求本人安全验证时立即给当前有效链接并继续独立准备工作。不输出token/OTP,不重复已完成的登录。 +- [x] 配置 Trusted Publishing 并逐项核对 HTTP 201 保存响应(未独立 GET);发布不可变 v0.1.0 与 GitHub Release,验证 Actions、HTTPS Demo、npm dist/tarball 实际 API 消费,保存不含凭据的回执。 - [ ] 仅公开链接及部署回读通过后推送门户PR,包含此前未合并算法标准基础和本轮算法目录,核对main差异/CI后合并;attach_artifact挂载全部PR。 - [ ] 生产门户第八条、筛选/详情、Demo回放与中英文、远程治理通过;报告真实评测指标与范围,关闭计划并保留工作树/历史分支。 diff --git a/reports/tracking/2026-09-19-release/README.md b/reports/tracking/2026-09-19-release/README.md new file mode 100644 index 0000000..ce53383 --- /dev/null +++ b/reports/tracking/2026-09-19-release/README.md @@ -0,0 +1,19 @@ +# PP-Tracking 0.1.0 发布与门户登记候选 + +日期:2026-09-19。独立 SDK `web-sdk-pp-tracking@0.1.0` 的 npm、GitHub Release、HTTPS Demo、仓库治理与 Pages 部署已完成远程核验;门户第八个算法条目已具备登记条件。本目录只记录门户合并前的链接核验与候选状态,不能作为门户生产站已包含第八条的证据。 + +## 独立 SDK 链接核验 + +- 仓库与不可变来源提交:[`chenmohan123/web-sdk-PP-Tracking`](https://github.com/chenmohan123/web-sdk-PP-Tracking),`c2ee347884426aa1a03962b76ac2915959397611`。 +- npm:[`web-sdk-pp-tracking@0.1.0`](https://www.npmjs.com/package/web-sdk-pp-tracking/v/0.1.0)。公开 tarball 为 16541 字节,sha512 integrity 与本地唯一候选一致,并已完成 ESM/CJS 与状态 API 实际消费。 +- GitHub Release:[`v0.1.0`](https://github.com/chenmohan123/web-sdk-PP-Tracking/releases/tag/v0.1.0)。资产 SHA256 为 `4b532782a008a5ef411d6db00fb58900884fef0c1759c15c9441d30a3093b2bb`。 +- HTTPS Demo:[`https://chenmohan123.github.io/web-sdk-PP-Tracking/`](https://chenmohan123.github.io/web-sdk-PP-Tracking/)。线上交互证据仍限 Windows 桌面 Chromium 153、CPU/main 与 390px 桌面窄视口,不扩展手机或跨浏览器声明。 +- SDK 完整发布证据:[独立仓库交付目录](https://github.com/chenmohan123/web-sdk-PP-Tracking/tree/main/reports/2026-09-19-release)。门户侧摘要见 [sdk-delivery.json](sdk-delivery.json)。 + +首版由本机认证发布,npm registry 返回 `provenance: null`。Trusted Publisher 已通过 HTTP 201 保存为仓库 `chenmohan123/web-sdk-PP-Tracking`、工作流 `release.yml`、环境 `npm`;配置回执没有独立 GET,Release 工作流只核对已存在的同完整性版本并跳过重复发布。因此 0.1.0 不声明经过 OIDC 发布;未来新版本须以实际 npm 回执为准。 + +## 门户候选状态 + +`src/content/models/pp-tracking.yaml` 保持 `status: beta`、CPU/JavaScript 主线程、空资产与无需模型权重语义,并保留固定 MOT17 训练序列、状态生命周期、设备和浏览器限制。beta 表示算法质量与兼容性边界,不表示 SDK 链接仍待发布。 + +当前分支只准备门户第八条记录和部署内容。门户 PR、合并、生产站第八条、筛选/详情及线上回放尚待主代理完成并回读;完成前不得把本报告称为生产验收回执。 diff --git a/reports/tracking/2026-09-19-release/sdk-delivery.json b/reports/tracking/2026-09-19-release/sdk-delivery.json new file mode 100644 index 0000000..b6888ce --- /dev/null +++ b/reports/tracking/2026-09-19-release/sdk-delivery.json @@ -0,0 +1,47 @@ +{ + "verifiedAt": "2026-09-19T05:52:51.7386765Z", + "sdk": { + "repository": "https://github.com/chenmohan123/web-sdk-PP-Tracking", + "sourceCommit": "c2ee347884426aa1a03962b76ac2915959397611", + "package": { + "name": "web-sdk-pp-tracking", + "version": "0.1.0", + "url": "https://www.npmjs.com/package/web-sdk-pp-tracking/v/0.1.0", + "tarball": "https://registry.npmjs.org/web-sdk-pp-tracking/-/web-sdk-pp-tracking-0.1.0.tgz", + "bytes": 16541, + "integrity": "sha512-0ujJfrcXoDcynzIUYQRI7pcETWT1x2t4LkEqB1D2cGQrnT8JCfk9LNrEZwAIDnBh1TLILcPmOaUbCDxsjE+tTQ==", + "provenance": null, + "consumptionVerified": true + }, + "release": { + "url": "https://github.com/chenmohan123/web-sdk-PP-Tracking/releases/tag/v0.1.0", + "id": 391930716, + "assetSha256": "4b532782a008a5ef411d6db00fb58900884fef0c1759c15c9441d30a3093b2bb", + "workflow": "https://github.com/chenmohan123/web-sdk-PP-Tracking/actions/runs/35424969266", + "workflowMode": "verified-existing", + "workflowConclusion": "success" + }, + "demo": { + "url": "https://chenmohan123.github.io/web-sdk-PP-Tracking/", + "verified": true, + "boundary": "Windows 桌面 Chromium 153、CPU/main;390px 为桌面窄视口。" + }, + "trustedPublishing": { + "status": "configured", + "configurationId": "da2f97a7-680c-4041-b902-26c74f639a3c", + "repository": "chenmohan123/web-sdk-PP-Tracking", + "workflow": "release.yml", + "environment": "npm", + "httpStatus": 201, + "independentGetPerformed": false, + "oidcPublicationVerified": false + } + }, + "portal": { + "record": "src/content/models/pp-tracking.yaml", + "recordStatus": "beta", + "deliveryState": "本地候选,待门户 PR、合并与生产回读", + "productionVerified": false + }, + "boundary": "独立 SDK 已发布;当前证据不声明门户生产站已包含第八条,也不声明 0.1.0 经 OIDC 发布。" +} diff --git a/src/content/models/pp-tracking.yaml b/src/content/models/pp-tracking.yaml index 097536c..4d524b9 100644 --- a/src/content/models/pp-tracking.yaml +++ b/src/content/models/pp-tracking.yaml @@ -4,7 +4,7 @@ name: PP-Tracking brand: self-developed task: multi-object-tracking status: beta -summary: 独立实现 ByteTrack 高低分两阶段关联思想的 CPU 主线程多目标跟踪算法,消费检测框并输出有状态轨迹;0.1.0 为待发布候选。 +summary: 独立实现 ByteTrack 高低分两阶段关联思想的 CPU 主线程多目标跟踪算法,消费检测框并输出有状态轨迹;0.1.0 已发布至 npm、GitHub Release 与 HTTPS Demo。 repository: https://github.com/chenmohan123/web-sdk-PP-Tracking license: Apache-2.0 package: { name: web-sdk-pp-tracking, version: 0.1.0 } @@ -31,7 +31,6 @@ algorithm: output: TrackingResult stateful: true limitations: - - 本地发布候选,npm、GitHub Release 与在线 Demo 待远程核验;门户生产合并须等待目标链接可用。 - 无需模型权重;只消费已有检测,不含检测模型、视频解码、摄像头、ReID 或相机运动补偿。轨迹 ID 不是个人身份。 - 2026-09-19 固定 MOT17 七段 FRCNN 训练序列 5316 帧,默认 IDF1 48.2922%、IDSW 1101、MOTA 44.4010%、FP 4169、FN 57166;不是测试集成绩或官方 ByteTrack 排名。 - 低分消融 IDF1 48.3465%、IDSW 1066;默认低分续接减少漏检但增加误检和切 ID,不承诺普遍精度提升或可靠跨遮挡身份。