Initial commit: AVWatch v1.0.0 with .gitignore and CLAUDE.md
This commit is contained in:
@@ -0,0 +1,435 @@
|
||||
# AVWatch
|
||||
|
||||
Update and risk monitoring for a live production AV fleet.
|
||||
|
||||
It answers one question, per device, every week: **a new version exists — is it
|
||||
safe for us to install, or should we hold off?**
|
||||
|
||||
It does that by finding the current release, searching Reddit, GitHub and the
|
||||
wider web for people reporting that the release broke something, and then
|
||||
producing a **Go / Wait / Avoid** call with the evidence attached. It is built
|
||||
around the assumption that in a church production environment, running a
|
||||
release behind costs almost nothing and a dead device on Sunday morning costs
|
||||
a great deal — so the burden of proof is on the update, not on waiting.
|
||||
|
||||
---
|
||||
|
||||
## What you get
|
||||
|
||||
- **A local HTML dashboard** (`reports/latest.html`) — every item, grouped by
|
||||
what needs a decision, with the community evidence one click away.
|
||||
- **An automated Monday email briefing** to whoever you list, with the full
|
||||
dashboard attached.
|
||||
- **Mid-week automated scans** that keep the dashboard current and notice new
|
||||
releases as they land.
|
||||
- **Three layers of verdict**, all shown side by side, because you asked for
|
||||
all three:
|
||||
1. **Claude's reasoning verdict** — reads the release notes and the community
|
||||
posts, filters the irrelevant ones, and explains its call.
|
||||
2. **A rule-based score** — release age against your soak policy, size of the
|
||||
version jump, keyword severity of the reports, criticality weighting. Runs
|
||||
always, needs no API key.
|
||||
3. **The raw cited evidence** — every post it looked at, linked, so you can
|
||||
overrule both engines.
|
||||
|
||||
When the two engines disagree, the report says so explicitly. That
|
||||
disagreement is the signal to go read the evidence yourself.
|
||||
|
||||
---
|
||||
|
||||
## Not sure what to do next?
|
||||
|
||||
```bash
|
||||
python -m avwatch worksheet
|
||||
```
|
||||
|
||||
Prints a walk-the-rack checklist: what to look at, where the version is
|
||||
displayed on that specific box, and the exact command to type. Coupled gear
|
||||
comes first, because a matched set cannot be checked until both halves are
|
||||
known.
|
||||
|
||||
You do not have to finish it. Every line helps, and nothing breaks while lines
|
||||
are missing.
|
||||
|
||||
---
|
||||
|
||||
## Quick start
|
||||
|
||||
```bash
|
||||
cd "C:\Users\Live Stream\Desktop\Claude\avwatch"
|
||||
python -m pip install -r requirements.txt
|
||||
```
|
||||
|
||||
```bash
|
||||
copy config.example.toml config.toml
|
||||
```
|
||||
|
||||
Edit `config.toml` — at minimum the `[email]` block. Then:
|
||||
|
||||
```bash
|
||||
python -m avwatch doctor
|
||||
```
|
||||
|
||||
That tells you exactly what is and is not configured. Then a first real run:
|
||||
|
||||
```bash
|
||||
python -m avwatch scan --no-llm
|
||||
```
|
||||
|
||||
Open `reports\latest.html`.
|
||||
|
||||
---
|
||||
|
||||
## Version coupling — the thing that actually breaks services
|
||||
|
||||
In a live AV fleet the update that ruins a Sunday is rarely a bad release. It
|
||||
is a **mismatch**: the ATEM got flashed but not the 2 M/E panel, the AD4Q
|
||||
receivers but not the AD1 packs, the VX1000 but not the receiving cards. Judging
|
||||
each item on its own merits cannot see any of that.
|
||||
|
||||
So items can declare dependencies on each other:
|
||||
|
||||
```yaml
|
||||
- id: atem-constellation-4k
|
||||
update_group: atem
|
||||
coupled_with:
|
||||
- {id: atem-2me-advanced-panel, relation: must_match,
|
||||
note: "the panel goes offline if the switcher moves without it"}
|
||||
```
|
||||
|
||||
| Relation | Meaning |
|
||||
|---|---|
|
||||
| `must_match` | Versions must be *equal* or they will not talk. Shure TX/RX, Teradek TX/RX, SoundGrid, ATEM + panel. |
|
||||
| `must_be_at_least` | This item must be at or above the other. Config tools over the devices they configure (Wireless Workbench, RedNet Control). |
|
||||
| `carries_firmware_for` | This is the utility that flashes the other. Update the tool first, then re-check the device. |
|
||||
| `host_of` | Platform under dependents. macOS over Pro Tools, Pro Tools over its plugins. |
|
||||
| `content_for` | Show files, libraries, calibration data. Usually forward-only — back up first. |
|
||||
|
||||
The rules AVWatch then enforces:
|
||||
|
||||
- **A coupled item is never reported as plain "go".** It becomes "go, together
|
||||
with X and Y", with the partners named.
|
||||
- **A live mismatch leads the report** in its own *Version mismatch — broken
|
||||
now* section, because that is a present fault, not a future risk.
|
||||
- **Edges are mirrored.** Only one half of a pair needs to declare the
|
||||
relationship; both halves get warned.
|
||||
- **`update_group`** marks items that must be planned and frozen as one set.
|
||||
|
||||
91 dependency edges across 8 groups ship configured: ATEM trio, Companion pair,
|
||||
DiGiCo console + rack, WING + Edit + DP48, Waves SoundGrid, Shure Axient and
|
||||
QLX-D sets, Vista + M1, both operating systems over their dependent stacks.
|
||||
|
||||
Check the graph any time with `avwatch doctor`.
|
||||
|
||||
---
|
||||
|
||||
## The one thing you must do
|
||||
|
||||
`installed:` ships blank on every item, because only you know what is actually
|
||||
running in your racks. Until you fill those in, AVWatch can tell you a newer
|
||||
version exists but not whether you are behind — **and coupling can be described
|
||||
but never checked**, since comparing two versions needs both of them.
|
||||
|
||||
Record them as you go:
|
||||
|
||||
```bash
|
||||
python -m avwatch set-installed atem-constellation-4k 10.4
|
||||
```
|
||||
|
||||
That edits one line and leaves the file's comments intact.
|
||||
|
||||
**Start with the coupled clusters, not alphabetically.** `avwatch doctor` lists
|
||||
them. Getting the ATEM trio, the Shure RF sets and the DiGiCo pair recorded
|
||||
unlocks the mismatch detection that is the most valuable thing here; the long
|
||||
tail of plugins can wait.
|
||||
|
||||
---
|
||||
|
||||
## Credentials
|
||||
|
||||
All optional, all read from environment variables so nothing sensitive lives in
|
||||
a config file. Set them with `setx` so scheduled tasks inherit them, then open
|
||||
a new shell:
|
||||
|
||||
```bash
|
||||
setx ANTHROPIC_API_KEY "sk-ant-..."
|
||||
```
|
||||
|
||||
| Variable | What it buys you |
|
||||
|---|---|
|
||||
| `ANTHROPIC_API_KEY` | The reasoning verdicts, **and** version lookups for the ~60 vendors that cannot be scraped. Without it you get rule-based scores and a much shorter list of tracked versions. |
|
||||
| `AVWATCH_SMTP_PASSWORD` | The email digest. |
|
||||
| `AVWATCH_GITHUB_TOKEN` | Lifts GitHub from 60 to 5,000 requests/hour. A classic token with **no scopes ticked** is enough. Worth setting. |
|
||||
| `AVWATCH_REDDIT_CLIENT_ID` + `AVWATCH_REDDIT_CLIENT_SECRET` | Proper Reddit search. Strongly recommended — see below. |
|
||||
|
||||
### Reddit is worth the five minutes
|
||||
|
||||
Reddit **blocks unauthenticated API access** (verified: plain `.json` endpoints
|
||||
return HTTP 403). Without credentials AVWatch falls back to scraping
|
||||
DuckDuckGo for `site:reddit.com` results, which works but is rate-limited and
|
||||
noisier.
|
||||
|
||||
r/livesound, r/VIDEOENGINEERING and r/churchtech are the highest-signal
|
||||
sources for this exact fleet, so this is the single best upgrade available:
|
||||
|
||||
1. Go to <https://www.reddit.com/prefs/apps>
|
||||
2. **Create another app…** → name it `avwatch`, type **script**,
|
||||
redirect URI `http://localhost:8080`
|
||||
3. The ID is under the app name; the secret is the `secret` field.
|
||||
|
||||
```bash
|
||||
setx AVWATCH_REDDIT_CLIENT_ID "your-id"
|
||||
```
|
||||
|
||||
Free, and gives 100 requests/minute.
|
||||
|
||||
---
|
||||
|
||||
## Email setup
|
||||
|
||||
### Google Workspace
|
||||
|
||||
```toml
|
||||
smtp_host = "smtp.gmail.com"
|
||||
smtp_port = 587
|
||||
use_starttls = true
|
||||
username = "you@waterschurch.org"
|
||||
```
|
||||
|
||||
Requires an **App Password** (Google Account → Security → 2-Step Verification →
|
||||
App passwords). Your normal password will be rejected.
|
||||
|
||||
### Microsoft 365
|
||||
|
||||
```toml
|
||||
smtp_host = "smtp.office365.com"
|
||||
smtp_port = 587
|
||||
use_starttls = true
|
||||
username = "avwatch@waterschurch.org"
|
||||
```
|
||||
|
||||
Two gotchas: with MFA you need an app password, and **SMTP AUTH is disabled by
|
||||
default on new M365 tenants** — enable it in Exchange admin centre → the
|
||||
mailbox → Manage email apps → tick *Authenticated SMTP*.
|
||||
|
||||
Test it without running a whole scan:
|
||||
|
||||
```bash
|
||||
python -m avwatch test-email
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Automation
|
||||
|
||||
```bash
|
||||
powershell -ExecutionPolicy Bypass -File scripts\install_tasks.ps1
|
||||
```
|
||||
|
||||
That registers two per-user scheduled tasks (no admin rights needed):
|
||||
|
||||
| Task | When | What |
|
||||
|---|---|---|
|
||||
| **AVWatch Scan** | Tue / Thu / Sat 06:00 | Checks for new releases, refreshes the dashboard. Only asks Claude about versions it has never seen, so it is cheap. No email. |
|
||||
| **AVWatch Digest** | Monday 07:00 | Full pass, every outstanding update gets a verdict, then the email goes out. |
|
||||
|
||||
Change the schedule:
|
||||
|
||||
```bash
|
||||
powershell -File scripts\install_tasks.ps1 -ScanDays Monday,Wednesday,Friday -DigestTime 08:30
|
||||
```
|
||||
|
||||
Remove them:
|
||||
|
||||
```bash
|
||||
powershell -File scripts\install_tasks.ps1 -Remove
|
||||
```
|
||||
|
||||
Run the digest right now without waiting for Monday:
|
||||
|
||||
```bash
|
||||
python -m avwatch digest --dry-run
|
||||
```
|
||||
|
||||
`--dry-run` builds everything and reports who it *would* mail, without
|
||||
sending. Logs land in `logs\`, last 30 kept.
|
||||
|
||||
---
|
||||
|
||||
## The three inventory files
|
||||
|
||||
| File | Items | What it is |
|
||||
|---|---|---|
|
||||
| `inventory.yaml` | 86 | Everything from your original notes, plus the pieces those items require. Confirmed. |
|
||||
| `inventory-expansion.yaml` | 116 | Categories your notes never mentioned — cameras, intercom, DMX nodes, projection, network core, UPS, click-track playback, storage. Either near-universal at this scale or directly implied by something you confirmed. |
|
||||
| `inventory-optional.yaml` | 57 | Lower-confidence: common but genuinely optional. A checklist to skim. |
|
||||
|
||||
All `inventory*.yaml` files are loaded and merged automatically. **Deleting a
|
||||
whole file is a supported action** — that is why the expansion lives separately
|
||||
from your verified list.
|
||||
|
||||
### `status: unconfirmed`
|
||||
|
||||
130 of the added rows are marked `status: unconfirmed`. For those AVWatch runs
|
||||
**no version lookup and gives no update advice at all**, and the `query` field
|
||||
is deliberately empty.
|
||||
|
||||
That is not laziness, it is the safest available behaviour: a search built from
|
||||
a guessed vendor returns a real, correct-looking version for the *wrong
|
||||
product*, and the team then acts on it. Silence is better. Fill in the make and
|
||||
model, set `status: confirmed`, and the row starts working.
|
||||
|
||||
### Update classes
|
||||
|
||||
Some rows are not a go/no-go decision at all:
|
||||
|
||||
- **`do_not_touch`** — fire alarm / voice-evac interface, rigging motors,
|
||||
building automation. Licensed contractor only; a self-installed update can
|
||||
void the listing that makes them compliant. Tracked for visibility, never
|
||||
for action.
|
||||
- **`vendor_pushed`** — YouTube, Resi's cloud platform, iPad apps, Stream Deck.
|
||||
These update whether or not you approve. The useful signal is advance warning
|
||||
of a breaking change, not a recommendation.
|
||||
- **`expiry`** — CCLI, FCC Part 74, support contracts. These fail on a *date*,
|
||||
not a version.
|
||||
|
||||
---
|
||||
|
||||
## How version detection actually works
|
||||
|
||||
This is the part worth understanding, because it determines what AVWatch can
|
||||
and cannot tell you.
|
||||
|
||||
| Tier | Items | How | Reliability |
|
||||
|---|---|---|---|
|
||||
| `blackmagic` | 12 | Blackmagic publish their whole support catalogue as one JSON document. One fetch resolves ATEM, DeckLink/Desktop Video, HyperDeck, the converters and MultiView, with real release dates and release notes. | **High.** Verified working. |
|
||||
| `github` | 6 | GitHub Releases API for OBS, Companion, Satellite, RustDesk, obs-websocket, DistroAV. Their issue trackers double as the best bug signal available. | **High.** Verified working. |
|
||||
| `html` | 1 | Regex against the ProPresenter download page. | Medium — breaks if they redesign. The report will tell you. |
|
||||
| `websearch` | 97 confirmed | Claude looks the version up with web search and cites the page it came from. | Medium. Guarded: it must return a source, low-confidence answers are downgraded to "check by hand", and answers are cached ~6 days. |
|
||||
| `manual` | 29 | No field-updatable firmware exists — passive and analogue gear. Will never produce an update. | — |
|
||||
| unconfirmed | 130 | No lookup attempted until you name the model. | — |
|
||||
|
||||
**Why so much `websearch`?** Because almost every AV vendor's support site is
|
||||
now a JavaScript-rendered single-page app. Verified on 2026-09-08: Shure,
|
||||
Resolume, Focusrite, Teradek, NovaStar, Line 6 and Rational Acoustics all
|
||||
return HTTP 200 with essentially **no version text in the HTML**. A regex
|
||||
scraper reads nothing from them. Gearspace and most vendor forums sit behind
|
||||
Cloudflare and refuse scripted requests outright.
|
||||
|
||||
The design choice throughout is that **AVWatch never guesses a version it
|
||||
cannot verify.** A confidently wrong version number would produce confidently
|
||||
wrong advice, which is worse than an honest "go look at this page". So
|
||||
anything unverifiable lands in a "check by hand" list with a link, and the
|
||||
community bug scan still runs on it — which is arguably the half you cared
|
||||
about most.
|
||||
|
||||
---
|
||||
|
||||
## What it costs to run
|
||||
|
||||
With an API key, per **weekly digest**. Note this scales with the *confirmed*
|
||||
item count, not the 259 total — unconfirmed placeholders cost nothing:
|
||||
|
||||
- Version lookups: **97 confirmed items** needing the web lookup, cached 6 days,
|
||||
on the cheaper model — roughly **$3–5**.
|
||||
- Reasoning verdicts: only for items you are actually behind on, typically
|
||||
10–25 — roughly **$0.50–2** on Opus.
|
||||
|
||||
So roughly **$4–7/week** at the current inventory size, with mid-week scans
|
||||
nearly free because they reuse the cache and only ask about genuinely new
|
||||
versions.
|
||||
|
||||
`lookup.max_items_per_run` is set to 120 to cover the confirmed set in one
|
||||
pass. Lower it to cap spend — items over the ceiling are deferred to the next
|
||||
run and the report says so explicitly rather than silently skipping them.
|
||||
|
||||
To cut cost further: set `model = "claude-sonnet-5"` under `[llm]`, raise
|
||||
`lookup.ttl_days`, or delete `inventory-optional.yaml`. To spend nothing, run
|
||||
`--no-llm` and you still get rule-based scores, coupling checks and the raw
|
||||
evidence.
|
||||
|
||||
---
|
||||
|
||||
## Tuning the recommendations
|
||||
|
||||
`[policy]` in `config.toml` is where your judgement lives, and it matters more
|
||||
than anything else:
|
||||
|
||||
```toml
|
||||
soak_days_showstopper = 30 # a showstopper release must be 30 days old
|
||||
soak_days_high = 21
|
||||
soak_days_normal = 10
|
||||
```
|
||||
|
||||
Raise these to get more conservative, lower them to move faster. Per item,
|
||||
`criticality` in `inventory.yaml` picks which window applies — so **be honest
|
||||
there**. Marking everything `showstopper` makes the report useless.
|
||||
|
||||
For gear you have deliberately decided not to touch:
|
||||
|
||||
```yaml
|
||||
freeze: true
|
||||
```
|
||||
|
||||
Frozen items are reported in their own section and never recommended. The X32
|
||||
is a good candidate — mature firmware where the risk of touching it usually
|
||||
exceeds the benefit.
|
||||
|
||||
---
|
||||
|
||||
## Commands
|
||||
|
||||
```bash
|
||||
python -m avwatch doctor --network # check setup, test one fetch per source kind
|
||||
python -m avwatch list # the tracked inventory
|
||||
python -m avwatch scan # check for updates, write the dashboard
|
||||
python -m avwatch scan --only obs-studio propresenter
|
||||
python -m avwatch scan --offline # re-run from cache, no network
|
||||
python -m avwatch digest # full pass + email
|
||||
python -m avwatch report --text # rebuild from stored data, no network
|
||||
python -m avwatch set-installed <id> <version>
|
||||
python -m avwatch mark <id> holding --note "waiting for 10.5"
|
||||
python -m avwatch history --item-id atem-constellation-4k
|
||||
```
|
||||
|
||||
`mark` records what you decided, so there is a written history of who held what
|
||||
and why — useful when the same question comes round again in three months.
|
||||
|
||||
---
|
||||
|
||||
## Things to fix in the inventory
|
||||
|
||||
Five entries could not be pinned down from the source list and are marked
|
||||
`VERIFY` in `inventory.yaml`. They will not track properly until you name the
|
||||
actual product:
|
||||
|
||||
- **DQ Rack** — ambiguous; Klark Teknik DN? Midas DL?
|
||||
- **Quantum 225** — ambiguous; DiGiCo console? An amplifier?
|
||||
- **DBOX+** — ambiguous
|
||||
- **AJA devices** — the list said only "aja"; needs the model (KUMO, Ki Pro, HELO…)
|
||||
- **Netgear Dante switch** — needs the model (M4250 / M4300 / …). Worth doing
|
||||
first: it is the highest-consequence update in the fleet, because bad switch
|
||||
firmware takes every Dante device down at once.
|
||||
|
||||
`Decimator` and `Focusrite RedNet` would also benefit from exact models.
|
||||
|
||||
---
|
||||
|
||||
## Limitations, stated plainly
|
||||
|
||||
- **It does not read your devices.** It compares "what you told us is
|
||||
installed" against "what the vendor has published". Nothing is probed over
|
||||
the network. Keeping `installed:` current is manual, and if it drifts, the
|
||||
advice drifts with it.
|
||||
- **Absence of bug reports is not safety.** The report says this out loud
|
||||
rather than treating silence as a green light. Niche gear — NovaStar, RF
|
||||
Venue, Decimator — has almost no community discussion, so verdicts there
|
||||
rest mostly on release age and your policy.
|
||||
- **Community posts are strangers on the internet.** AVWatch ranks and
|
||||
summarises them; it does not vouch for them. Post text is passed to Claude
|
||||
as untrusted data with explicit instructions never to follow instructions
|
||||
found inside it.
|
||||
- **`websearch` versions are web-sourced, not vendor-verified.** They are
|
||||
labelled as such in the report.
|
||||
- **No verdict replaces a rollback plan.** Nothing here knows whether you have
|
||||
a spare, a saved show file, or a way back.
|
||||
Reference in New Issue
Block a user