Skip to content

feat(manifest): add upstream_docs to watch vendor register sources - #48

Open
HuggeK wants to merge 2 commits into
srcfl:mainfrom
HuggeK:feat/manifest-upstream-docs
Open

feat(manifest): add upstream_docs to watch vendor register sources#48
HuggeK wants to merge 2 commits into
srcfl:mainfrom
HuggeK:feat/manifest-upstream-docs

Conversation

@HuggeK

@HuggeK HuggeK commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

What

Two halves of one feature for keeping drivers honest against the vendor
documents they decode a device by:

  1. upstream_docs — an optional manifest field recording the vendor
    reference documents (register maps, parameter changelogs) a driver was built
    against, at a semi-persistent URL, plus how durable that URL is.
  2. watch-upstream-docs — a scheduled watcher that fetches each URL, notices
    when a document changes or disappears, and opens a tracking issue so a
    maintainer reviews whether the driver's registers are affected.

Populated for the two NIBE drivers (both watching NIBE's myUplink register
changelog), and corrects the nibe_local manifest author to its real provenance.

upstream_docs:
  - url: "https://www.nibe.eu/webdav/files/myuplink_changelog/nibe-n.pdf"
    title: "NIBE S-series myUplink register/parameter changelog (nibe-n.pdf)"
    kind: changelog
    url_stability: stable

Why

A driver decodes a device by following the vendor's own reference material. When
that material changes upstream — a register renumbered, a parameter added — the
driver silently falls behind, and it's usually a human noticing a wrong value on
a real site months later. This makes that visible: record the document, watch it,
and raise a reviewable issue the moment it moves.

Implements the pilot proposed in #47.

The field

upstream_docs is an optional list, modelled on tested_devices:

Field Required Notes
url yes semi-persistent http(s) URL to watch
title no human-readable label
kind no changelog, register_map, manual, api_docs, firmware_notes, other
url_stability no committed, stable, volatile, unknown (default)

url_stability records whether the manufacturer keeps the URL put — committed
(vendor promises it's permanent), stable (stable in practice), volatile
(rotates), unknown. The watcher uses it to weigh a broken link: a stable one
going missing is a real signal the document moved; a volatile one breaking can
be routine.

The watcher

  • tools/check_upstream_docs.py (stdlib only): fetches each distinct URL
    once, SHA-256s it, and diffs against a committed baseline
    (upstream-docs-state.json, seeded here for the two NIBE entries).
  • .github/workflows/watch-upstream-docs.yml: weekly (+ manual). On a change
    it opens an issue labelled upstream-doc-changed listing old/new hash and a
    review checklist; on a document that has been unreachable for 3 runs it opens
    one labelled upstream-doc-unreachable. Then it commits the updated baseline.
Notify-once, and the raw-hash caveat
  • A changed file is reported once. Detecting a change updates the baseline,
    so the next run sees the new hash as current and does not re-fire. The issue
    title carries the new hash's short prefix, and the workflow skips a title that
    already has an open issue — so even a failed baseline-commit can't double-post.
  • A broken link is reported once. The unreachable alert fires only on the run
    that crosses the 3-run failure threshold, not every run after; recovery
    resets the count.
  • Detection is a raw-byte SHA-256. A PDF's bytes can churn without its content
    changing (rebuild timestamps). So a flagged change is a prompt to look, not
    proof the registers moved — the issue says exactly that.
  • Impact review (auto-diffing which registers changed) is deliberately left
    as a follow-up in RFC: watch manifest upstream_docs for changes and flag affected drivers #47; v1 surfaces the change and links the driver source.
Why it's safe to add and what it does not touch
  • Descriptive metadata only. generate_index.py copies a fixed field set, so
    upstream_docs never reaches index.yaml and never affects how a driver
    installs or runs. devices.yaml and support-status.* are unaffected —
    regenerated and confirmed byte-identical.
  • No version bump. The Lua source is untouched, so sha256/size_bytes are
    unchanged and no driver version moves. Both drivers stay byte-identical to
    their baselines/ftw sources.
  • Enforced. validate_manifest.py requires an http(s) url and known
    kind/url_stability values, and rejects unknown entry fields.
  • The watcher's contents: write / issues: write are scoped to that workflow.

Changes

  • tools/manifest_parser.pyparse_upstream_docs()
  • tools/validate_manifest.py — validates url / kind / url_stability / unknown fields
  • tools/check_upstream_docs.py — the watcher (fetch, diff, render issues)
  • .github/workflows/watch-upstream-docs.yml — weekly job
  • upstream-docs-state.json — seeded baseline
  • spec/manifest-v2.md — documents the field (V2.3)
  • manifests/nibe_local.yaml, manifests/myuplink.yaml — entries; nibe_local author corrected
  • Makefilemake watch-upstream-docs (local dry-run)
  • tests/test_upstream_docs.py, tests/test_upstream_docs_watch.py
  • CHANGELOG.md[Unreleased]

Testing

  • python tools/validate_manifest.py → 80 manifests, 0 errors
  • python tools/sync_manifests.py --check → no drift
  • generate_index.py / generate_devices.py / generate_support_status.py → no content change
  • pytest tests/test_upstream_docs.py tests/test_upstream_docs_watch.py → 23 passed
  • python tools/check_upstream_docs.py --dry-run → fetches both NIBE URLs live, 0 failures

PR evidence

  • Device / source: NIBE S-series (nibe_local) and MyUplink heat pumps
    (myuplink); watched document is NIBE's public myUplink register changelog
    (nibe-n.pdf).
  • No hardware behaviour changes — manifest metadata plus tooling; no
    registers, emits, control paths or driver bytes are modified.
  • Operational note: the baseline-commit step pushes to the default branch. If
    branch protection blocks the Actions bot, either allow github-actions[bot]
    for upstream-docs-state.json or switch that step to open a PR — issues are
    still filed correctly either way, and the open-issue dedup prevents duplicates.

RFC / discussion: #47

A driver decodes a device by following the vendor's own reference material
- a register map, a parameter changelog PDF. When that material changes
upstream, the driver can silently fall behind. upstream_docs records those
documents at a semi-persistent URL so a watcher can poll them and flag the
driver for review, instead of a human noticing months later.

- new optional manifest field: list of {url, title?, kind?} entries
- parse_upstream_docs() in manifest_parser.py, mirroring parse_tested_devices
- validate_manifest.py enforces an http(s) url and a known kind
- nibe_local + myuplink both declare NIBE's myUplink register changelog
- nibe_local manifest author corrected to its real provenance
- descriptive metadata only: never copied into index.yaml
- documented in spec/manifest-v2.md (V2.3); tests in test_upstream_docs.py

Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
Signed-off-by: Hugo Karlsson <48095810+HuggeK@users.noreply.github.com>
@HuggeK
HuggeK marked this pull request as ready for review July 29, 2026 14:56
Adds the automation half of upstream_docs and a stability signal.

Watcher:
- tools/check_upstream_docs.py fetches each watched URL, hashes it, and
  diffs against a committed baseline (upstream-docs-state.json).
- .github/workflows/watch-upstream-docs.yml runs weekly, opens a tracking
  issue when a document changes (review the registers) or goes missing
  (link rotted), and commits the updated baseline.
- Notifies once per event: a change updates the baseline so it cannot
  re-fire; an outage alerts only on crossing a 3-run failure threshold;
  the workflow skips a title that already has an open issue.
- Shared URLs are fetched once; detection is a raw-byte hash (a prompt to
  look, not proof registers moved) - the issue says so.

url_stability field:
- new optional upstream_docs field: committed | stable | volatile | unknown
- records whether the manufacturer keeps the URL put; weights how loudly a
  broken link is reported.
- both NIBE entries set to 'stable'; validated in validate_manifest.py.

Baseline seeded for the two NIBE entries. Pure diff + rendering logic is
covered by tests/test_upstream_docs_watch.py; make watch-upstream-docs runs
a local dry-run.

Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
Signed-off-by: Hugo Karlsson <48095810+HuggeK@users.noreply.github.com>
@HuggeK
HuggeK marked this pull request as draft July 29, 2026 15:15
@HuggeK
HuggeK marked this pull request as ready for review July 29, 2026 15:23

frahlg commented Jul 29, 2026

Copy link
Copy Markdown
Member

Automated maintenance pass: not merging this one, flagging for a maintainer.

The change itself reads well-scoped — descriptive metadata only, generate_index.py doesn't copy upstream_docs through, no driver bytes/version touched, validate_manifest.py enforces the schema, and the PR reports 80/80 manifests valid, no sync drift, and 23 passing tests for the new tooling. On the merits of the diff I'd call this close to ready.

Two things stop it from going further autonomously:

  1. It's from a non-collaborator contributor via a fork, and GitHub has held every CI run action_required (both pushes to this branch) rather than running it — this repo's CI has not actually validated this PR yet, and approving that requires a maintainer with the standing to review what external code is about to run in Actions.
  2. It adds a new scheduled workflow (watch-upstream-docs.yml) with contents: write and issues: write permissions, authored externally. Granting an untrusted contribution write access to the repo and issue tracker via a recurring Action is exactly the kind of change that should get a human security read before its CI is even allowed to run, separate from whether the Python/Lua logic is correct.

Secondary note: the companion RFC (#47) still has open checklist items ("agree the surfacing mechanism," decisions to make "before the watcher is wired up") — worth confirming those are settled the way this PR settled them before merging.

Flagging for a maintainer to approve the workflow run (or review the Action file directly) and confirm the RFC is considered resolved.


Generated by Claude Code

@HuggeK

HuggeK commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator Author

It is now done.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants