2026-09-24 13:07:21 -04:00
|
|
|
# CLAUDE.md
|
|
|
|
|
|
2026-09-24 13:46:33 -04:00
|
|
|
## Every session: stay in sync with Gitea
|
|
|
|
|
|
|
|
|
|
This repo is backed up to `http://10.3.8.90:3000/chris/AV-Watch` (private, branch
|
|
|
|
|
`main`). The Git login is in Windows Credential Manager, not in this repo.
|
|
|
|
|
|
2026-09-24 14:58:08 -04:00
|
|
|
For sessions opened in `C:\Users\Live Stream\Desktop\Claude`, hooks do the syncing
|
|
|
|
|
(`.claude\settings.json` and `.claude\hooks\avwatch-sync.ps1` in that folder,
|
|
|
|
|
outside this repo):
|
2026-09-24 13:46:33 -04:00
|
|
|
|
2026-09-24 14:58:08 -04:00
|
|
|
- **At session start** a hook runs `git pull --ff-only` and adds the result to your
|
|
|
|
|
context as "AVWatch SessionStart hook: ...". Don't pull again. If it says the
|
|
|
|
|
pull was skipped, mention that in your first reply, and don't stash, reset,
|
|
|
|
|
rebase or merge to get past it unless asked.
|
|
|
|
|
- **After each reply** a hook shows the user a one-line note while anything is
|
|
|
|
|
uncommitted or unpushed, so don't add a reminder of your own. Only commit or
|
|
|
|
|
push when asked.
|
2026-09-24 13:46:33 -04:00
|
|
|
|
2026-09-24 14:58:08 -04:00
|
|
|
In a session opened anywhere else the hooks don't run, so do both by hand: pull
|
|
|
|
|
before the first request, and end any reply that leaves work uncommitted or
|
|
|
|
|
unpushed with a one-line reminder and an offer to commit and push.
|
2026-09-24 13:46:33 -04:00
|
|
|
|
2026-09-24 14:58:08 -04:00
|
|
|
```powershell
|
|
|
|
|
# 'never' makes a bad token fail fast instead of opening a login window
|
|
|
|
|
$env:GCM_INTERACTIVE = 'never'; git pull --ff-only
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
If `git` isn't found, use `C:\Program Files\Git\cmd\git.exe`.
|
2026-09-24 13:46:33 -04:00
|
|
|
|
2026-09-24 13:07:21 -04:00
|
|
|
## What this is
|
|
|
|
|
|
|
|
|
|
**AVWatch** is a Python CLI that monitors firmware/software updates across the
|
|
|
|
|
Waters Church live-production AV fleet. For each item it answers *"a new version
|
|
|
|
|
exists, is it safe to install or should we wait?"* and makes a **Go / Wait / Avoid**
|
|
|
|
|
call. It does this by finding the current release, searching Reddit, GitHub and
|
|
|
|
|
the web for people reporting breakage, and applying a conservative soak policy.
|
|
|
|
|
|
|
|
|
|
The design principle: **a dead device on Sunday morning costs far more than
|
|
|
|
|
running one release behind.** The update has to prove it is safe, and waiting is
|
|
|
|
|
the default. It also never guesses a version it can't verify. Anything it can't
|
|
|
|
|
verify goes to a "check by hand" list rather than getting an invented answer.
|
|
|
|
|
|
|
|
|
|
README.md is the full user guide. This file is orientation for working on the code.
|
|
|
|
|
|
|
|
|
|
## Layout
|
|
|
|
|
|
|
|
|
|
| Path | Purpose |
|
|
|
|
|
|---|---|
|
|
|
|
|
| `avwatch/cli.py` | Entry point. Commands: `doctor`, `list`, `scan`, `digest`, `report`, `set-installed`, `mark`, `history`, `test-email`, `worksheet` |
|
|
|
|
|
| `avwatch/config.py` | Loads `config.toml`. All secrets are read from env vars here, **never** from the file |
|
|
|
|
|
| `avwatch/inventory.py`, `models.py` | Loads/merges `inventory*.yaml`; item model |
|
|
|
|
|
| `avwatch/coupling.py` | Version-coupling graph (`must_match`, `must_be_at_least`, `host_of`, …); mirrors edges, detects live mismatches |
|
|
|
|
|
| `avwatch/scanner.py`, `versions.py` | Orchestrates lookups; version comparison |
|
|
|
|
|
| `avwatch/sources/` | Version sources: `blackmagic` (support JSON), `github_releases`, `generic` (HTML regex), `websearch` (Claude + web search, must cite a source) |
|
|
|
|
|
| `avwatch/risk/` | `collectors` (Reddit/GitHub/DDG evidence), `heuristics` (rule-based score), `llm` (Claude verdict), `classify` |
|
|
|
|
|
| `avwatch/report/` | HTML dashboard + email digest (`templates/*.jinja`) |
|
|
|
|
|
| `avwatch/store.py` | SQLite state in `data/avwatch.sqlite3` (versions seen, verdicts, `mark` decision history) |
|
|
|
|
|
| `avwatch/http.py` | Cached HTTP client (`data/cache/`) |
|
|
|
|
|
| `scripts/` | `install_tasks.ps1` (Task Scheduler: scans Tue/Thu/Sat 06:00, digest Mon 07:00), `run_avwatch.ps1`, `set_api_key.ps1` |
|
|
|
|
|
| `tests/` | `test_core.py`, `test_coupling.py` (unittest) |
|
|
|
|
|
|
|
|
|
|
## Commands
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
python -m pip install -r requirements.txt
|
|
|
|
|
python -m unittest discover -s tests # 65 tests, all passing as of 2026-09-24
|
|
|
|
|
python -m avwatch doctor --network
|
|
|
|
|
python -m avwatch scan --no-llm # no API spend
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Secrets and config
|
|
|
|
|
|
|
|
|
|
- Credentials come **only** from env vars: `ANTHROPIC_API_KEY`, `AVWATCH_SMTP_PASSWORD`,
|
|
|
|
|
`AVWATCH_GITHUB_TOKEN`, `AVWATCH_REDDIT_CLIENT_ID`, `AVWATCH_REDDIT_CLIENT_SECRET`.
|
|
|
|
|
Keep it that way. Don't add a secret field that gets read from `config.toml`.
|
|
|
|
|
- `config.toml` is gitignored because it can hold recipient addresses and usernames.
|
|
|
|
|
`config.example.toml` is the committed template.
|
|
|
|
|
- `data/`, `reports/` and `logs/` are runtime output and gitignored. This means
|
|
|
|
|
**the SQLite decision history is not in git.** Back it up separately if it matters.
|
|
|
|
|
- Community post text is untrusted input to the LLM. Keep the prompt-injection
|
|
|
|
|
guarding in `risk/llm.py` intact.
|
|
|
|
|
|
|
|
|
|
## Where it stands (2026-09-24)
|
|
|
|
|
|
|
|
|
|
- v1.0.0. Core pipeline, coupling checks, dashboard, email digest and scheduled
|
|
|
|
|
tasks are built. Reports from 2026-09-08 and 2026-09-09 show real runs.
|
|
|
|
|
- `inventory.yaml` has **88 items, but only 3 have `installed:` filled in.** Until
|
|
|
|
|
more do, AVWatch can report "a newer version exists" but not "you are behind",
|
|
|
|
|
and coupling mismatches can't be checked.
|
|
|
|
|
- The README describes `inventory-expansion.yaml` (116 items) and
|
|
|
|
|
`inventory-optional.yaml` (57 items), but **neither file exists in this folder.**
|
|
|
|
|
Either they were deleted (the README says that's supported) or they were never
|
|
|
|
|
saved. The README counts (259 total, 130 unconfirmed, cost estimates) are out of
|
|
|
|
|
date until this is sorted out.
|
|
|
|
|
- The five `VERIFY` entries the README lists (DQ Rack, Quantum 225, DBOX+, AJA,
|
|
|
|
|
Netgear Dante switch) no longer carry a `VERIFY` marker in `inventory.yaml`.
|
|
|
|
|
Check whether they were resolved or just lost the marker.
|
|
|
|
|
|
|
|
|
|
## Next steps
|
|
|
|
|
|
|
|
|
|
1. Record installed versions with `python -m avwatch worksheet` and
|
|
|
|
|
`set-installed`, starting with the coupled clusters (ATEM trio, Shure RF,
|
|
|
|
|
DiGiCo pair, Netgear Dante switch).
|
|
|
|
|
2. Resolve the missing expansion/optional inventory files, then update the README counts.
|
|
|
|
|
3. Set `AVWATCH_REDDIT_CLIENT_ID`/`SECRET` (the best evidence source) and
|
|
|
|
|
`AVWATCH_GITHUB_TOKEN`.
|
|
|
|
|
4. Confirm the scheduled tasks are registered and the Monday digest mails correctly
|
|
|
|
|
(`python -m avwatch digest --dry-run`).
|
|
|
|
|
5. Decide whether `data/avwatch.sqlite3` needs a backup.
|