前端测试
- 作者仓库星标 43
- 许可证 GPL-3.0
- 作者更新于 实时读取
- 作者仓库 flightjar
- 领域
- 文档
- 兼容 Agent
-
- Claude Code
- Cursor
- Cline
- Codex
- Windsurf
- Gemini CLI
- +20
- 信任分
- 94 / 100 · 已通过审计
- 作者 / 版本 / 许可
- @MrSuttonmann · GPL-3.0
- Token 消耗评级
- 低消耗
- 接入复杂程度
- 需简单配置
- 是否需要外部 API Key
- 不需要
- 兼容的系统
- macOS · Linux · Windows
- 底层运行要求
- Node.js · Python
- 文件与系统权限
-
- 只读
- 允许写入 / 修改
- 读取环境变量
- 网络行为
- 允许外网请求
- 安装命令数
- 26 条
档案由构建时根据 SKILL.md 与安装命令自动衍生,可能与作者实际意图存在差异。
需要注意: 未限定 allowed-tools,默认拥有全部工具权限。
---
name: bds-port
description: Port a Mode S Comm-B BDS register decoder from pyModeS into FlightJar.Decoder.ModeS.CommB, wire…
category: 文档
runtime: Node.js / Python
---
# bds-port 输出预览
## PART A: 任务判断
- 适用问题:PRD、RFC、README、项目说明或知识库整理。
- 输入要求:目标材料、限制条件、期望输出和验收方式。
- 证据边界:围绕“Touchpoints / Step 1 — Fetch the pyModeS reference / Step 2 — Port the validator + decoder”读取原文规则,不把推断写成作者承诺。
## PART B: 执行结果
- **01** 任务判断:确认你的需求是否属于PRD、RFC、README、项目说明或知识库整理,并标出输入、限制和预期结果。
- **02** 执行计划:优先按“Touchpoints / Step 1 — Fetch the pyModeS reference / Step 2 — Port the validator + decoder”拆成步骤,说明每一步会读取什么、修改什么、产出什么。
- **03** 交付结果:给出可复制的命令、文件改动、检查清单或内容草稿,并说明如何继续迭代。
- **04** 风险边界:结合 读取文件、写入/修改文件、读取环境变量、会按任务需要访问外部网络、通常不需要额外 API Key 给出执行前确认项。
## Running Rules
- 读取文件、写入/修改文件、读取环境变量;会按任务需要访问外部网络;通常不需要额外 API Key。
- 先小样例验证,再放大到真实任务。
- 交付时同时给结果、检查口径和下一步迭代建议。 原文出现了 `/tmp` 这类斜杠命令;如果你的 Agent 支持命令触发,优先用命令开场,再补充目标和边界。
告诉 Agent 目标文件或材料、期望结果、不可改范围、是否允许联网或执行命令。本 Skill 的权限画像是:读取文件、写入/修改文件、读取环境变量。
先用一个小任务确认它会围绕“Touchpoints / Step 1 — Fetch the pyModeS reference / Step 2 — Port the validator + decoder”工作;涉及文件或命令时,先看 diff、日志、预览或测试结果。
检查最终产物是否包含明确结果、必要证据和下一步动作;如果输出泛泛而谈,就补充输入、边界和验收标准后重跑。
---
name: bds-port
description: Port a Mode S Comm-B BDS register decoder from pyModeS into FlightJar.Decoder.ModeS.CommB, wire…
category: 文档
source: MrSuttonmann/flightjar
---
# bds-port
## 什么时候使用
- 把项目文档方向的常用动作沉淀成 Agent 可调用的技能 适合处理README、PRD、RFC、教程和知识库文档,核心价值是把输入、判断、执行、验证和交付边界固定下来,避免 Agent 泛泛回答。 把任务拆成可执行、可检查、可继续迭代的…
- 面向PRD、RFC、README、项目说明或知识库整理,优先处理能明确输入、步骤和验收标准的工作。
## 需要提供什么
- 目标材料、目录范围、期望结果和不可改动内容。
- 是否允许联网、执行命令、读写文件或调用外部服务。
## 执行规则
- 围绕「Touchpoints / Step 1 — Fetch the pyModeS reference / Step 2 — Port the validator + decoder」组织步骤,不把推断写成作者事实。
- 读取文件、写入/修改文件、读取环境变量;会按任务需要访问外部网络;通常不需要额外 API Key。
- 先跑小样例,确认结果可检查后再扩大任务范围。
## 输出要求
- 给出最终产物、关键证据、验证方式和下一步动作。
- 信息不足时标记 unknown,不编造命令、平台或依赖。 作者原文负责流程事实;仓库文件负责来源和命令;流狐只补充适用场景、限制和质量判断。
skill "bds-port" {
输入层 -> 用户目标 + 目标文件 + 禁止范围 + 验收标准
上下文层 -> Touchpoints / Step 1 — Fetch the pyModeS reference / Step 2 — Port the validator + decoder
规则层 -> SKILL.md 触发条件 / 执行顺序 / 输出格式
运行层 -> Node.js / Python | 读取文件、写入/修改文件、读取环境变量 | 会按任务需要访问外部网络
安全层 -> 通常不需要额外 API Key + 小任务验证 + diff / 日志复核
输出层 -> 可复制结果 + 检查清单 + 下一步迭代
} Porting a pyModeS BDS register to FlightJar
FlightJar's Comm-B decoder matches pyModeS 3.x byte-for-byte. When adding another register, keep the wire behaviour identical so we can cross-check golden vectors from pyModeS's own test corpus. Every deviation (even a "cleanup") is a source of silent drift later.
Touchpoints
A full port spans six files and one frontend + one docs update:
dotnet/src/FlightJar.Decoder/ModeS/CommB.cs— addIsBdsXX(payload)validator +DecodeBdsXX(payload)decoder +BdsXXDatarecord. Extend theCandidatesrecord andInfer()to include the new register.dotnet/src/FlightJar.Decoder/ModeS/DecodedMessage.cs— add one field per value the register exposes.dotnet/src/FlightJar.Decoder/ModeS/MessageDecoder.cs— add a branch inInferCommBthat builds aDecodedMessagefor the new register; extendMerge()to copy the new fields.dotnet/src/FlightJar.Core/State/Aircraft.cs— add per-field state + oneBdsXXAttimestamp.dotnet/src/FlightJar.Core/State/AircraftRegistry.cs— add acase "X,Y":branch inApplyCommBthat writes the fields + stamps the timestamp. ExtendBuildCommBSnapshotto gate the fields on freshness and feed theSnapshotCommBrecord.dotnet/src/FlightJar.Core/State/RegistrySnapshot.cs— add the fields toSnapshotCommB(nullable, snake-case on the wire).app/static/detail_panel.js— add metric tiles to the.panel-met-gridplaceholder markup inbuildPopupContentand aset('.pop-met-xxx', …)call per field inrenderCommBSection.dotnet/tests/FlightJar.Decoder.Tests/ModeS/CommBTests.cs— validator accept/reject tests + golden-vector decoder tests using hex captures from pyModeS'stests/test_bds_commb.py.README.md— add the new fields to the "Enhanced Mode S air data" bullet and flag any register-specific caveats.CLAUDE.md— update the decoder list + any behavioural nuances.
Step 1 — Fetch the pyModeS reference
pyModeS lives at junzis/pyModeS on GitHub (default branch main).
BDS decoders live under src/pyModeS/decoder/bds/bdsXX.py, helpers
under _helpers.py. Fetch into /tmp/pymodes/ via the GitHub API
(the raw CDN sometimes 404s; the contents API is reliable):
# List available registers.
curl -sL "https://api.github.com/repos/junzis/pyModeS/contents/src/pyModeS/decoder/bds?ref=main" \
| grep '"download_url"'
# Fetch a specific register + the shared helpers.
for f in bds45 _helpers _infer; do
curl -sL "https://raw.githubusercontent.com/junzis/pyModeS/main/src/pyModeS/decoder/bds/$f.py" \
> /tmp/pymodes/$f.py
done
# And the golden-vector test corpus.
curl -sL "https://raw.githubusercontent.com/junzis/pyModeS/main/tests/test_bds_commb.py" \
> /tmp/pymodes/test_bds_commb.py
Step 2 — Port the validator + decoder
pyModeS operates on a 56-bit payload as a Python int with (payload >> (55 - i)) & mask
indexing (MSB-first from payload bit 0). The C# port keeps identical
bit indexing so the ported arithmetic reads one-to-one next to the Python.
Add your new methods inside public static class CommB in CommB.cs.
Use the existing helpers that are already in CommB.cs:
WrongStatus(payload, statusBit, valueStart, valueWidth)— mirrors pyModeS_helpers.wrong_status(status-bit / value-field consistency).Signed(value, width, sign)— sign-magnitude to signed int (NOT two's complement; Mode S splits sign + magnitude bits).NormaliseAngle(deg)— wrap into[0, 360).
Every range gate in IsBdsXX must match pyModeS's validator. If pyModeS
rejects > 600 kt but you port it as >= 600, you will silently accept
values pyModeS would reject.
Step 3 — Wire into inference
CommB.Infer(payload) returns a Candidates record with one bool per
heuristic register. MessageDecoder.InferCommB(msg) returns a decoded
message only when exactly one candidate validates. This single-match
discipline is intentional: multi-match payloads are ambiguous and
dropped rather than risk polluting aircraft state with fields decoded
against the wrong register. Do not relax it without a replacement
disambiguation strategy (e.g. pyModeS Phase 3 known-state scoring).
BDS 4,5 (meteorological hazard) is opt-in in pyModeS because it
false-positives on non-meteorological payloads. When porting, keep
the validator strict; if ambiguity becomes a problem, add a
Candidates.Bds45 branch behind a config flag rather than unconditionally
accepting it.
Step 4 — Extend state + snapshot
For every decoded field:
- Add a nullable property to
Aircraft(e.g.public int? WindshearLevel { get; set; }). - Add an identically-named property to
SnapshotCommB. - In
ApplyCommB, add acase "X,Y":that assigns from theDecodedMessage; do not touch fields from other registers (each register owns its own slice of state). - In
BuildCommBSnapshot, compute abdsXXFreshflag usingCommBMaxAge(120 s) and use it to gate every field from the register. Include the register'sBdsXXAttimestamp on the snapshot so the frontend can age values out independently.
Naming convention on the wire: snake_case via the global serializer
config in FlightJar.Api.Configuration. MagneticHeadingDeg becomes
magnetic_heading_deg without any extra attributes.
Step 5 — Extend the frontend panel
The Enhanced Mode S panel is driven entirely by a.comm_b in the
snapshot. In detail_panel.js:
- Add placeholder tile markup inside
.panel-met-gridinbuildPopupContent— same shape as existing tiles (<div class="metric pop-met-xxx" hidden><div class="label">…</div><div class="val"></div></div>). - Add a
set('.pop-met-xxx', value)call inrenderCommBSectionthat computes the formatted string or returnsnullto hide the tile.
Use the existing uconv('alt' | 'spd' | 'vrt' | 'dst', value) helper
for unit-system-aware formatting. Raw-unit values (Mach, temperature,
degrees, percent) format inline.
Step 6 — Tests
CommBTests.cs follows two patterns; use both for the new register:
- Validator acceptance + rejection: pick a golden-vector hex
from pyModeS's
test_bds_commb.py, assertIsBdsXXaccepts it, then construct synthetic payloads that should be rejected (all zeros, out-of-range values, status-bit / value-bit inconsistency) and assert they're rejected. - Decode golden vector: decode the pyModeS golden hex and
assert each field matches the pyModeS oracle value to within the
same
abs=tolerance the pyModeS test uses.
Also add an end-to-end test in MessageDecoder_RoutesUnambiguousBdsXXOnDf20
to prove the InferCommB branch wires through.
Step 7 — Add an integration test
dotnet/tests/FlightJar.Core.Tests/State/AircraftRegistryTests.cs +
FakeDecoder.cs use fake hex keys (BD44 → pre-built DecodedMessage)
to exercise ApplyCommB + BuildCommBSnapshot without real wire
bytes. Add a BDxx fixture and a test proving the new field lands
in the snapshot and ages out correctly past CommBMaxAge.
Step 8 — Playwright smoke (optional)
The detail panel renders Enhanced Mode S section when comm_b is present
test in tests/e2e/layout.spec.js injects a fake snapshot with a full
comm_b block. If the new register adds a user-visible tile, extend the
fixture and assert the new label appears.
Step 9 — Verify
cd dotnet && dotnet format FlightJar.slnx --verify-no-changes
dotnet build FlightJar.slnx
dotnet test FlightJar.slnx
cd ..
node --test tests/js/
npx playwright test
All five must pass before the port is done. Formatter drift and Playwright regressions are the two most common surprises.
Do not
- Do not rename pyModeS's field names in the decoded record unless
you have a codebase-wide reason.
static_air_temperaturein pyModeS isStaticAirTemperatureCinCommB.cs— theCsuffix is the only concession, and it's there because our wire convention suffixes every physical-unit field. - Do not persist Comm-B state in
state.json.gz. The 120 s freshness window is far shorter than the 30 s persist cadence + 10 minPersistMaxAge, so restored Comm-B values would be stale on load. - Do not bypass
CommB.Infer's single-match gate by eagerly decoding every register. Multi-match payloads mean one of the decodes is wrong; you cannot tell which without an external reference signal, so dropping ambiguous payloads is the safe default. - Do not add register-specific env vars for opt-in registers without
updating
README.md's configuration reference table.
先判断是否适合
作者设计意图
作者的方法与取舍
边界和复核