# SyncTide v1.8.2 — Update Log

**Release date:** 2026-04-20
**Schema version:** 19 (no migrations required since v1.8.0)
**Previous version:** v1.8.0

A ship-readiness release — polishing everything that was already built
and closing the remaining items from the pre-release audit. No schema
changes, no breaking API changes.

---

## What's new

### Installer auto-upgrade (was planned)
- Stable `AppId={F3B3C4D2-2E8F-4A19-9C5B-3A7D4E2F1B01}` — **never change
  this GUID**, it's how Inno Setup recognises an existing installation.
- `IsUpgradeInstall()` detects an upgrade via `{app}\.env` +
  `{app}\python\python.exe` presence on the target machine.
- `ShouldSkipPage()` hides PostgreSQL config, data dirs, admin account
  and license wizard pages when upgrading — no risk of wizard defaults
  stomping on real customer credentials.
- `PrepareToInstall()` stops SyncTide services (`sc stop Backend/
  Ingestion/UI` + 2s sleep) before file copy.
- `RunPostInstall()` skips `SetupPostgres()`, pgAdmin, LibreOffice,
  admin creation and license copy on upgrade runs.
- VM-verified: fresh install + upgrade flow both pass end-to-end.

### Hi-res logo + wizard BMPs
- New transparent hi-res `logo.png` replaces the 23 KB placeholder across
  the app root, `website/assets/img/`, and the installer ICO.
- Source files under `assets/` — `logo.png`, `logo_white_bg.png`,
  `logo_black_bg.png`.
- New helper `tools/build_wizard_bmps.py` regenerates Inno Setup wizard
  BMPs (164×314 banner + 55×58 small) from the PNG with one command.

### Virtual tags test suite (was flagged "resilience tests required")
- **40 unit tests** in `tests/test_virtual_tags.py` — parser, validator
  (including `${   }` empty-ref guard + `asteval` sandbox-escape
  attempts), cycle detector (self-ref, direct, indirect, diamond),
  evaluate_series across `all_new` / `any_new` / `time` trigger modes
  with 60s interval clamp, evaluate_scalar, and
  `collect_transitive_dependencies` across flat/chained/multi-root/cycle
  scenarios.
- **4 end-to-end tests** in `tests/test_virtual_tags_integration.py` —
  exercise `/measurements/query` and `/measurements/trend` virtual-tag
  expansion, dep row filtering, and the explicit-dep-request path.
  Skip cleanly when `SYNCTIDE_TEST_API_URL` / `_ADMIN_USER` /
  `_ADMIN_PASSWORD` env vars are unset. All 4 pass against dev Postgres.

### Messaging module rename (`sms` → `messaging`)
- License module string `sms` is now `messaging` — covers SMS, WhatsApp,
  Telegram and email under one name. Legacy `sms` string accepted via
  `_MODULE_ALIASES` in `license_manager.py` and mirrored in
  `api_client.check_license_or_stop`, so existing `.lic` files keep
  working.
- Messaging Center page now checks for `module="messaging"` — previously
  it still asked for `"sms"` and spuriously showed the lock icon even
  on licences that included the module.

### License management UI
- **Sidebar every page**: version caption extended with a licence summary
  — `SyncTide v1.8.2 · Perpetual licence` / `Licensed until 2026-05-10`
  / `Subscription expired` / `No licence`.
- **Banner on pages** (`render_license_banner`): silent when healthy,
  fires red / amber / blue depending on severity (red when `ingestion_
  allowed` is False; amber ≤14 days; blue ≤30 days).
- **Configurations → License Management tab**: now shows `License type`,
  and on subscription licences additionally `Subscription expiry` +
  `Days until subscription expiry`. Top banner picks subscription vs
  maintenance mode based on licence type.

### H-5 ACL lockdown verification
- New `tools/verify_h5_acl.ps1` — run as admin on a VM after install.
  Checks: icacls inspection (SYSTEM:F + Administrators:F, Users removed,
  inheritance off), non-admin read-denial probe, backend service health
  (Running + `/version` 200). Exits 0 on pass, 1+ on fail.
- H-5 is now **runtime-verified** on a clean VM (previously
  code-complete only).

### Build tooling
- `build_release.py` caches vendor installers (Postgres, pgAdmin,
  LibreOffice, Python) in `installer/_cache/` — ~760 MB saved per
  build.
- `installer/_cache/` and `installer/Output/` added to `.gitignore`.

### Hero typography fix (public website)
- `background-clip: text` on the hero `<h1>` was being defeated by
  `animations.js` wrapping words in `<span class="word"><span
  class="inner">…</span></span>` with `display: inline-block` —
  invisible text until the animation completed. CSS inheritance chain
  fixed: `[data-split]` + `.word` + `.inner` all inherit the gradient
  and re-apply the clip.

---

## Pending (not blocked by this release)

- Virtual tags **manual UI smoke checklist** (create / edit / delete,
  click-to-insert grid, error surfacing).
- Rollback path for installer upgrade — currently relies on Inno's own
  restore; no `backups/pre-upgrade-{version}/` snapshot yet.

---

## Files changed

| File | Change | Recompile |
|------|--------|-----------|
| `installer/synctide.iss` | Stable AppId, upgrade detection, page-skip logic | No (Inno) |
| `installer/scripts/post_install.pas` | Skip PG/pgAdmin/LibreOffice/admin/license on upgrade | No (Pascal) |
| `installer/scripts/setup_services.pas` | Stop services before file copy | No (Pascal) |
| `license_manager.py` | `_MODULE_ALIASES` (sms→messaging) | Yes |
| `api_client.py` | Canonicalise module names in `check_license_or_stop` | Yes |
| `app_core.py` | `render_license_banner`, sidebar licence summary | Yes |
| `pages/3_SMS_Center.py` | `module="messaging"` | No |
| `pages/6_Configurations.py` | License type + subscription fields | No |
| `main.py` | `/license/status` exposes `license_type`, gates | Yes |
| `virtual_tags.py` | `pd.concat(sort=False)` | Yes |
| `tests/test_virtual_tags.py` | New — 40 unit tests | No (tests) |
| `tests/test_virtual_tags_integration.py` | New — 4 integration tests | No (tests) |
| `assets/logo.png`, `logo_white_bg.png`, `logo_black_bg.png` | New source logos | No |
| `tools/build_wizard_bmps.py` | New — wizard BMP regen | No |
| `tools/verify_h5_acl.ps1` | New — VM audit script | No |
| `build_release.py` | `installer/_cache/` caching | No |

---

## Deployment

Standard: stop services → copy updated `.pyd` files → update
`integrity_manifest.json` with new SHA-256 hashes → start services →
verify `/version` returns `1.8.2`.

Or use the installer: run `SyncTideSetup-1.8.2.exe` on an existing
install — the auto-upgrade path detects the previous version, stops
services, updates files, and preserves DB / admin / license without
re-prompting.
