feat(manifest): add upstream_docs to watch vendor register sources - #48
Conversation
|
Automated maintenance pass: not merging this one, flagging for a maintainer. The change itself reads well-scoped — descriptive metadata only, Two things stop it from going further autonomously:
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 |
|
It is now done. |
|
Automated maintenance pass, follow-up on the earlier flag. What's changed since: CI now runs and is green ( Why I'm still not merging it:
Secondary: the companion RFC (#47) checklist is still unticked, though this PR does appear to settle "agree the surfacing mechanism" by implementing it. Not a blocker, just flagging it's still open. Ask: @HuggeK — could you rebase/resolve the conflict against current Generated by Claude Code |
|
Conflict resolved — the branch now merges cleanly against current What conflicted: exactly one hunk, the Merged rather than rebased so the branch history stays append-only and the two reviewed commits keep their hashes; no force-push. Second commit: a CHANGELOG entry moved, no content changedMerging carried both Re-verified after the merge
The full pytest suite ran in CI rather than locally (no On the standing question — granting a scheduled workflow
RFC #47's checklist is still unticked — agreed that's not a blocker, and this PR does implement the surfacing mechanism it proposes. |
|
Added the missing half of this: the guidance to actually use the field.
Both state the limits rather than overselling it: a document behind a login or a vendor portal can't be fetched and belongs in a driver comment instead, and detection is a hash of the bytes, so a flagged document is a prompt to look rather than proof the registers moved. No code, manifest or generated file changes — three markdown files. Re-verified after the commit: |
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>
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>
Merging main carried both entries along inside the Added group they were written in, next to BLUEPRINT.lua, which now reads as though the field shipped in that batch. [Unreleased] prepends a group per merged change, so they belong at the top - the same move 6f8eab7 made for the catalog. Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com> Signed-off-by: Hugo Karlsson <48095810+HuggeK@users.noreply.github.com>
upstream_docs was specified in spec/manifest-v2.md and nowhere else, which is where you read for field syntax after you have decided to use a field - not where you find out you should. Neither AGENTS.md nor WRITING-A-DRIVER.md mentioned it, so nothing in the repository ever asks an author for the one thing the watcher needs. A field nobody is told to populate stays at two manifests out of eighty, and a driver with no entry is one nobody will be warned about. WRITING-A-DRIVER.md already says the valuable part of a driver is the knowledge rather than the code, and to write down what you learn about the registers as you find it. Where that knowledge came from is the half it never asked for. The reasoning and the YAML now sit beside the manifest step that writes it; AGENTS.md carries the one-line rule with the source rules. Both state the limits rather than overselling it: a document behind a login cannot be fetched and belongs in a driver comment, and detection is a hash of the bytes, so a flagged document is a prompt to look rather than proof the registers moved. No code, manifest or generated file changes. Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com> Signed-off-by: Hugo Karlsson <48095810+HuggeK@users.noreply.github.com>
Rebasing onto main put main's own group above this batch's, so [Unreleased] read as though upstream_docs shipped before the nibe_local fix that main merged after it. A group per merged change is prepended, and this change merges next, so its group leads again -- the same move 892a599 made when main was merged in, and 6f8eab7 before it for the catalog. Ordering only: no entry text changed. Signed-off-by: Hugo Karlsson <48095810+HuggeK@users.noreply.github.com> Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
08d1acd to
ede9210
Compare
srcfl#57 landed the `signed channel accepts this tree` check on main, and both branches wrote a CHANGELOG entry under the same `### Added` heading. The resolution keeps both, `upstream_docs` first, and touches nothing else. The check main gained is one this branch has to answer: it adds `upstream_docs` to two manifests, and a manifest edit can move the signed artifact's bytes. This field does not -- `_load_channel` copies `ders`, `protocol` and the tested-device make and model into the artifact metadata and leaves the rest behind -- so no driver version has to move. The check says so rather than the branch claiming it. Signed-off-by: HuggeK <48095810+HuggeK@users.noreply.github.com> Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
This branch was cut before srcfl#48, srcfl#57, srcfl#62 and srcfl#63 landed. Five files conflicted; all five were additions on both sides and keep both: CHANGELOG.md both wrote under the same ### Added heading spec/manifest-v2.md Connectivity/Setup beside main's DER Types, and the numbered validation list renumbered so the upstream_docs rule survives manifests/myuplink.yaml connectivity/setup from here, ders [heatpump] and 1.1.1 from srcfl#63 -- the empty ders this branch was written against no longer exists tools/generate_site.py Reach labels beside the vendor-document labels index.yaml regenerated rather than hand-merged Also records V2.4 in the migration ledger. The branch adds two manifest fields and updated the field table and validation rules, but never recorded the version the way V2.3 did for upstream_docs. The claim that neither field reaches the signed artifact is checked rather than trusted: with all 80 manifests carrying connectivity, stripping both fields back out moves no artifact digest, so no driver needs a version bump. Signed-off-by: HuggeK <48095810+HuggeK@users.noreply.github.com> Co-authored-by: HuggeK <48095810+HuggeK@users.noreply.github.com>
What
Two halves of one feature for keeping drivers honest against the vendor
documents they decode a device by:
upstream_docs— an optional manifest field recording the vendorreference documents (register maps, parameter changelogs) a driver was built
against, at a semi-persistent URL, plus how durable that URL is.
watch-upstream-docs— a scheduled watcher that fetches each URL, noticeswhen 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_localmanifest author to its real provenance.The author-facing guides ask for it, so the field is something a driver author is
told to fill in rather than something they have to find in the spec first.
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_docsis an optional list, modelled ontested_devices:urlhttp(s)URL to watchtitlekindchangelog,register_map,manual,api_docs,firmware_notes,otherurl_stabilitycommitted,stable,volatile,unknown(default)url_stabilityrecords 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: astableonegoing missing is a real signal the document moved; a
volatileone breaking canbe routine.
The watcher
tools/check_upstream_docs.py(stdlib only): fetches each distinct URLonce, 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 changeit opens an issue labelled
upstream-doc-changedlisting old/new hash and areview 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
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.
that crosses the 3-run failure threshold, not every run after; recovery
resets the count.
changing (rebuild timestamps). So a flagged change is a prompt to look, not
proof the registers moved — the issue says exactly that.
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
generate_index.pycopies a fixed field set, soupstream_docsnever reachesindex.yamland never affects how a driverinstalls or runs.
devices.yamlandsupport-status.*are unaffected —regenerated and confirmed byte-identical.
sha256/size_bytesareunchanged and no driver version moves. Both drivers stay byte-identical to
their
baselines/ftwsources.validate_manifest.pyrequires anhttp(s)urland knownkind/url_stabilityvalues, and rejects unknown entry fields.contents: write/issues: writeare scoped to that workflow.Changes
tools/manifest_parser.py—parse_upstream_docs()tools/validate_manifest.py— validates url / kind / url_stability / unknown fieldstools/check_upstream_docs.py— the watcher (fetch, diff, render issues).github/workflows/watch-upstream-docs.yml— weekly jobupstream-docs-state.json— seeded baselinespec/manifest-v2.md— documents the field (V2.3)docs/WRITING-A-DRIVER.md,AGENTS.md— tell a driver author to record the documents they decoded the device from, and what the watcher cannot domanifests/nibe_local.yaml,manifests/myuplink.yaml— entries;nibe_localauthor correctedMakefile—make watch-upstream-docs(local dry-run)tests/test_upstream_docs.py,tests/test_upstream_docs_watch.pyCHANGELOG.md—[Unreleased]Testing
python tools/validate_manifest.py→ 80 manifests, 0 errorspython tools/sync_manifests.py --check→ no driftgenerate_index.py/generate_devices.py/generate_support_status.py→ no content changepytest tests/test_upstream_docs.py tests/test_upstream_docs_watch.py→ 23 passedpython tools/check_upstream_docs.py --dry-run→ fetches both NIBE URLs live, 0 failuresPR evidence
nibe_local) and MyUplink heat pumps(
myuplink); watched document is NIBE's public myUplink register changelog(
nibe-n.pdf).registers, emits, control paths or driver bytes are modified.
branch protection blocks the Actions bot, either allow
github-actions[bot]for
upstream-docs-state.jsonor switch that step to open a PR — issues arestill filed correctly either way, and the open-issue dedup prevents duplicates.
RFC / discussion: #47