Dec 14, 6-7 AM (0)
Dec 14, 7-8 AM (0)
Dec 14, 8-9 AM (8)
Dec 14, 9-10 AM (5)
Dec 14, 10-11 AM (33)
Dec 14, 11-12 PM (9)
Dec 14, 12-1 PM (13)
Dec 14, 1-2 PM (4)
Dec 14, 2-3 PM (5)
Dec 14, 3-4 PM (9)
Dec 14, 4-5 PM (6)
Dec 14, 5-6 PM (10)
Dec 14, 6-7 PM (13)
Dec 14, 7-8 PM (23)
Dec 14, 8-9 PM (12)
Dec 14, 9-10 PM (32)
Dec 14, 10-11 PM (36)
Dec 14, 11-12 AM (27)
Dec 15, 12-1 AM (16)
Dec 15, 1-2 AM (16)
Dec 15, 2-3 AM (15)
Dec 15, 3-4 AM (9)
Dec 15, 4-5 AM (5)
Dec 15, 5-6 AM (5)
Dec 15, 6-7 AM (2)
Dec 15, 7-8 AM (75)
Dec 15, 8-9 AM (36)
Dec 15, 9-10 AM (43)
Dec 15, 10-11 AM (29)
Dec 15, 11-12 PM (44)
Dec 15, 12-1 PM (49)
Dec 15, 1-2 PM (54)
Dec 15, 2-3 PM (41)
Dec 15, 3-4 PM (44)
Dec 15, 4-5 PM (31)
Dec 15, 5-6 PM (22)
Dec 15, 6-7 PM (55)
Dec 15, 7-8 PM (13)
Dec 15, 8-9 PM (33)
Dec 15, 9-10 PM (14)
Dec 15, 10-11 PM (26)
Dec 15, 11-12 AM (38)
Dec 16, 12-1 AM (17)
Dec 16, 1-2 AM (2)
Dec 16, 2-3 AM (6)
Dec 16, 3-4 AM (4)
Dec 16, 4-5 AM (2)
Dec 16, 5-6 AM (9)
Dec 16, 6-7 AM (13)
Dec 16, 7-8 AM (25)
Dec 16, 8-9 AM (36)
Dec 16, 9-10 AM (49)
Dec 16, 10-11 AM (53)
Dec 16, 11-12 PM (46)
Dec 16, 12-1 PM (71)
Dec 16, 1-2 PM (54)
Dec 16, 2-3 PM (40)
Dec 16, 3-4 PM (25)
Dec 16, 4-5 PM (33)
Dec 16, 5-6 PM (30)
Dec 16, 6-7 PM (20)
Dec 16, 7-8 PM (20)
Dec 16, 8-9 PM (41)
Dec 16, 9-10 PM (18)
Dec 16, 10-11 PM (65)
Dec 16, 11-12 AM (26)
Dec 17, 12-1 AM (10)
Dec 17, 1-2 AM (5)
Dec 17, 2-3 AM (6)
Dec 17, 3-4 AM (1)
Dec 17, 4-5 AM (4)
Dec 17, 5-6 AM (5)
Dec 17, 6-7 AM (13)
Dec 17, 7-8 AM (14)
Dec 17, 8-9 AM (26)
Dec 17, 9-10 AM (22)
Dec 17, 10-11 AM (56)
Dec 17, 11-12 PM (38)
Dec 17, 12-1 PM (39)
Dec 17, 1-2 PM (49)
Dec 17, 2-3 PM (35)
Dec 17, 3-4 PM (27)
Dec 17, 4-5 PM (21)
Dec 17, 5-6 PM (26)
Dec 17, 6-7 PM (9)
Dec 17, 7-8 PM (26)
Dec 17, 8-9 PM (6)
Dec 17, 9-10 PM (11)
Dec 17, 10-11 PM (26)
Dec 17, 11-12 AM (20)
Dec 18, 12-1 AM (44)
Dec 18, 1-2 AM (11)
Dec 18, 2-3 AM (10)
Dec 18, 3-4 AM (14)
Dec 18, 4-5 AM (4)
Dec 18, 5-6 AM (8)
Dec 18, 6-7 AM (7)
Dec 18, 7-8 AM (10)
Dec 18, 8-9 AM (21)
Dec 18, 9-10 AM (27)
Dec 18, 10-11 AM (40)
Dec 18, 11-12 PM (29)
Dec 18, 12-1 PM (49)
Dec 18, 1-2 PM (38)
Dec 18, 2-3 PM (47)
Dec 18, 3-4 PM (61)
Dec 18, 4-5 PM (31)
Dec 18, 5-6 PM (43)
Dec 18, 6-7 PM (28)
Dec 18, 7-8 PM (11)
Dec 18, 8-9 PM (17)
Dec 18, 9-10 PM (13)
Dec 18, 10-11 PM (40)
Dec 18, 11-12 AM (31)
Dec 19, 12-1 AM (2)
Dec 19, 1-2 AM (6)
Dec 19, 2-3 AM (4)
Dec 19, 3-4 AM (19)
Dec 19, 4-5 AM (5)
Dec 19, 5-6 AM (6)
Dec 19, 6-7 AM (4)
Dec 19, 7-8 AM (22)
Dec 19, 8-9 AM (51)
Dec 19, 9-10 AM (41)
Dec 19, 10-11 AM (20)
Dec 19, 11-12 PM (68)
Dec 19, 12-1 PM (34)
Dec 19, 1-2 PM (34)
Dec 19, 2-3 PM (49)
Dec 19, 3-4 PM (37)
Dec 19, 4-5 PM (52)
Dec 19, 5-6 PM (33)
Dec 19, 6-7 PM (32)
Dec 19, 7-8 PM (14)
Dec 19, 8-9 PM (12)
Dec 19, 9-10 PM (4)
Dec 19, 10-11 PM (26)
Dec 19, 11-12 AM (15)
Dec 20, 12-1 AM (0)
Dec 20, 1-2 AM (1)
Dec 20, 2-3 AM (3)
Dec 20, 3-4 AM (3)
Dec 20, 4-5 AM (1)
Dec 20, 5-6 AM (0)
Dec 20, 6-7 AM (0)
Dec 20, 7-8 AM (1)
Dec 20, 8-9 AM (2)
Dec 20, 9-10 AM (4)
Dec 20, 10-11 AM (12)
Dec 20, 11-12 PM (2)
Dec 20, 12-1 PM (4)
Dec 20, 1-2 PM (0)
Dec 20, 2-3 PM (2)
Dec 20, 3-4 PM (10)
Dec 20, 4-5 PM (5)
Dec 20, 5-6 PM (2)
Dec 20, 6-7 PM (15)
Dec 20, 7-8 PM (17)
Dec 20, 8-9 PM (18)
Dec 20, 9-10 PM (0)
Dec 20, 10-11 PM (21)
Dec 20, 11-12 AM (32)
Dec 21, 12-1 AM (0)
Dec 21, 1-2 AM (0)
Dec 21, 2-3 AM (3)
Dec 21, 3-4 AM (4)
Dec 21, 4-5 AM (5)
Dec 21, 5-6 AM (2)
Dec 21, 6-7 AM (0)
3,553 commits this week Dec 14, 2025 - Dec 21, 2025
Add StreamSource and TCP connection support
New features:
- StreamSource: receives snapshots from async streams (TCP, etc.)
- CLI --connect flag: connect to TCP endpoint for live snapshots
  Example: caryatid-doctor --connect localhost:9090

StreamSource provides two ways to receive data:
1. spawn(reader, desc): reads newline-delimited JSON from AsyncRead
2. from_bytes_channel(rx, desc): receives JSON bytes via mpsc channel

This enables integration with:
- Direct TCP connections to a monitor server
- Message bus subscriptions (bridge via channel)
- Any async byte stream

Usage:
  caryatid-doctor --file monitor.json     # File-based (default)
  caryatid-doctor --connect host:port     # TCP stream
Fix rate calculation by tracking file modification time
FileSource now tracks the file's modification time and only returns
new data when the file has actually been updated. This prevents
recording duplicate snapshots into history when the TUI polls faster
than caryatid's Monitor writes (e.g., TUI polls every 1s but Monitor
writes every 5s).

Previously, the rate column would show 0 because we recorded the same
total_read value multiple times, resulting in delta=0. Now the rate
correctly shows messages/second based on actual changes.
Restructure as caryatid-doctor library crate
- Rename package to caryatid-doctor
- Add lib.rs with public API exports and documentation
- Separate library and binary targets
- Export all types needed for library consumers:
  - App, DataSource trait, FileSource, ChannelSource
  - MonitorSnapshot and serialization types
  - MonitorData, Thresholds, HealthStatus, etc.
- Fix ChannelSource to return initial value on first poll
- Update Cargo.toml with publishing metadata

The crate can now be used as:
1. CLI tool: caryatid-doctor --file monitor.json
2. Library with FileSource for file-based monitoring
3. Library with ChannelSource for message bus integration
Refactor App to use DataSource trait
- App now accepts Box<dyn DataSource> instead of PathBuf
- MonitorData gains from_snapshot() for direct snapshot conversion
- main.rs creates FileSource and passes to App
- Removes tight coupling between App and file-based loading

This enables future support for channel-based data sources
(e.g., message bus subscriptions) without changing App logic.
Add DataSource abstraction for monitor snapshots
Introduces a trait-based abstraction for receiving monitor data from
various sources:

- DataSource trait: common interface for polling snapshots
- FileSource: reads from JSON file (existing behavior)
- ChannelSource: receives via tokio watch channel (for message bus)
- MonitorSnapshot types: shared serialization format matching caryatid

This prepares monitor_cli for publishing as caryatid-doctor crate
with support for both file-based and message bus data sources.
chore(deps-dev): bump @vitejs/plugin-react from 5.1.1 to 5.1.2
Bumps [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react) from 5.1.1 to 5.1.2.
- [Release notes](https://github.com/vitejs/vite-plugin-react/releases)
- [Changelog](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite-plugin-react/commits/[email protected]/packages/plugin-react)

---
updated-dependencies:
- dependency-name: "@vitejs/plugin-react"
  dependency-version: 5.1.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
chore(deps-dev): bump eslint-plugin-react-refresh from 0.4.24 to 0.4.25
Bumps [eslint-plugin-react-refresh](https://github.com/ArnaudBarre/eslint-plugin-react-refresh) from 0.4.24 to 0.4.25.
- [Release notes](https://github.com/ArnaudBarre/eslint-plugin-react-refresh/releases)
- [Changelog](https://github.com/ArnaudBarre/eslint-plugin-react-refresh/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ArnaudBarre/eslint-plugin-react-refresh/compare/v0.4.24...v0.4.25)

---
updated-dependencies:
- dependency-name: eslint-plugin-react-refresh
  dependency-version: 0.4.25
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
build(deps): bump golang.org/x/net from 0.47.0 to 0.48.0 (#511)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.47.0 to 0.48.0.
- [Commits](https://github.com/golang/net/compare/v0.47.0...v0.48.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-version: 0.48.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
chore(deps-dev): bump eslint from 9.39.1 to 9.39.2 (#317)
Bumps [eslint](https://github.com/eslint/eslint) from 9.39.1 to 9.39.2.
- [Release notes](https://github.com/eslint/eslint/releases)
- [Commits](https://github.com/eslint/eslint/compare/v9.39.1...v9.39.2)

---
updated-dependencies:
- dependency-name: eslint
  dependency-version: 9.39.2
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
build(deps): bump github.com/blinklabs-io/gouroboros (#479)
Bumps [github.com/blinklabs-io/gouroboros](https://github.com/blinklabs-io/gouroboros) from 0.143.0 to 0.145.0.
- [Release notes](https://github.com/blinklabs-io/gouroboros/releases)
- [Commits](https://github.com/blinklabs-io/gouroboros/compare/v0.143.0...v0.145.0)

---
updated-dependencies:
- dependency-name: github.com/blinklabs-io/gouroboros
  dependency-version: 0.145.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
chore(deps): Bump github.com/blinklabs-io/gouroboros (#279)
Bumps [github.com/blinklabs-io/gouroboros](https://github.com/blinklabs-io/gouroboros) from 0.144.0 to 0.145.0.
- [Release notes](https://github.com/blinklabs-io/gouroboros/releases)
- [Commits](https://github.com/blinklabs-io/gouroboros/compare/v0.144.0...v0.145.0)

---
updated-dependencies:
- dependency-name: github.com/blinklabs-io/gouroboros
  dependency-version: 0.145.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
chore(deps): bump github.com/blinklabs-io/gouroboros (#405)
Bumps [github.com/blinklabs-io/gouroboros](https://github.com/blinklabs-io/gouroboros) from 0.143.0 to 0.145.0.
- [Release notes](https://github.com/blinklabs-io/gouroboros/releases)
- [Commits](https://github.com/blinklabs-io/gouroboros/compare/v0.143.0...v0.145.0)

---
updated-dependencies:
- dependency-name: github.com/blinklabs-io/gouroboros
  dependency-version: 0.145.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
flake.lock: Update
Flake lock file updates:

• Updated input 'flake-utils':
    'github:numtide/flake-utils/c1dfcf0' (2024-09-17)
  → 'github:numtide/flake-utils/11707dc' (2024-11-13)
• Updated input 'haskell-nix':
    'github:input-output-hk/haskell.nix/a2f75fc' (2024-10-19)
  → 'github:input-output-hk/haskell.nix/238583f' (2025-12-20)
• Removed input 'haskell-nix/ghc-8.6.5-iohk'
• Updated input 'haskell-nix/hackage':
    'github:input-output-hk/hackage.nix/4988994' (2024-10-19)
  → 'github:input-output-hk/hackage.nix/c87a0fb' (2025-12-20)
• Added input 'haskell-nix/hackage-for-stackage':
    'github:input-output-hk/hackage.nix/401b72d' (2025-12-20)
• Added input 'haskell-nix/hackage-internal':
    'github:input-output-hk/hackage.nix/f7867ba' (2025-06-19)
• Added input 'haskell-nix/hls':
    'github:haskell/haskell-language-server/682d689' (2025-03-10)
• Added input 'haskell-nix/hls-2.10':
    'github:haskell/haskell-language-server/2318c61' (2025-03-27)
• Added input 'haskell-nix/hls-2.11':
    'github:haskell/haskell-language-server/46ef452' (2025-05-15)
• Updated input 'haskell-nix/hls-2.9':
    'github:haskell/haskell-language-server/0c1817c' (2024-07-03)
  → 'github:haskell/haskell-language-server/90319a7' (2024-07-03)
• Removed input 'haskell-nix/hydra'
• Removed input 'haskell-nix/hydra/nix'
• Removed input 'haskell-nix/hydra/nix/lowdown-src'
• Removed input 'haskell-nix/hydra/nix/nixpkgs'
• Removed input 'haskell-nix/hydra/nix/nixpkgs-regression'
• Removed input 'haskell-nix/hydra/nixpkgs'
• Updated input 'haskell-nix/iserv-proxy':
    'github:stable-haskell/iserv-proxy/2ed3400' (2024-06-04)
  → 'github:stable-haskell/iserv-proxy/1506051' (2025-08-15)
• Removed input 'haskell-nix/nixpkgs-2003'
• Removed input 'haskell-nix/nixpkgs-2105'
• Removed input 'haskell-nix/nixpkgs-2111'
• Removed input 'haskell-nix/nixpkgs-2205'
• Removed input 'haskell-nix/nixpkgs-2211'
• Updated input 'haskell-nix/nixpkgs-2405':
    'github:NixOS/nixpkgs/086b448' (2024-09-16)
  → 'github:NixOS/nixpkgs/1e7a8f3' (2024-12-30)
• Added input 'haskell-nix/nixpkgs-2411':
    'github:NixOS/nixpkgs/5ab036a' (2025-06-30)
• Added input 'haskell-nix/nixpkgs-2505':
    'github:NixOS/nixpkgs/6c8f0cc' (2025-12-01)
• Added input 'haskell-nix/nixpkgs-2511':
    'github:NixOS/nixpkgs/b0924ea' (2025-12-01)
• Updated input 'haskell-nix/nixpkgs-unstable':
    'github:NixOS/nixpkgs/658e722' (2024-09-17)
  → 'github:NixOS/nixpkgs/c1cb7d0' (2025-12-01)
• Updated input 'haskell-nix/stackage':
    'github:input-output-hk/stackage.nix/df1d8f0' (2024-10-16)
  → 'github:input-output-hk/stackage.nix/f92d00a' (2025-12-20)
flake.lock: Update
Flake lock file updates:

• Updated input 'cabal':
    'github:stable-haskell/cabal/c0d52b2' (2025-03-30)
  → 'github:stable-haskell/cabal/0a74ea1' (2025-07-28)
• Updated input 'cabal-experimental':
    'github:stable-haskell/cabal/7e50837' (2025-04-07)
  → 'github:stable-haskell/cabal/7ea381f' (2025-05-22)
• Updated input 'haskellNix':
    'github:input-output-hk/haskell.nix/835d1d8' (2025-04-04)
  → 'github:input-output-hk/haskell.nix/238583f' (2025-12-20)
• Removed input 'haskellNix/ghc-8.6.5-iohk'
• Updated input 'haskellNix/hackage':
    'github:input-output-hk/hackage.nix/9f3c25c' (2025-04-04)
  → 'github:input-output-hk/hackage.nix/c87a0fb' (2025-12-20)
• Updated input 'haskellNix/hackage-for-stackage':
    'github:input-output-hk/hackage.nix/fdd3d2b' (2025-04-04)
  → 'github:input-output-hk/hackage.nix/401b72d' (2025-12-20)
• Added input 'haskellNix/hackage-internal':
    'github:input-output-hk/hackage.nix/f7867ba' (2025-06-19)
• Added input 'haskellNix/hls-2.11':
    'github:haskell/haskell-language-server/46ef452' (2025-05-15)
• Updated input 'haskellNix/iserv-proxy':
    'github:stable-haskell/iserv-proxy/e9dc86e' (2025-03-16)
  → 'github:stable-haskell/iserv-proxy/1506051' (2025-08-15)
• Updated input 'haskellNix/nixpkgs-2411':
    'github:NixOS/nixpkgs/94792ab' (2025-02-10)
  → 'github:NixOS/nixpkgs/5ab036a' (2025-06-30)
• Added input 'haskellNix/nixpkgs-2505':
    'github:NixOS/nixpkgs/6c8f0cc' (2025-12-01)
• Added input 'haskellNix/nixpkgs-2511':
    'github:NixOS/nixpkgs/b0924ea' (2025-12-01)
• Updated input 'haskellNix/nixpkgs-unstable':
    'github:NixOS/nixpkgs/041c867' (2025-01-17)
  → 'github:NixOS/nixpkgs/c1cb7d0' (2025-12-01)
• Updated input 'haskellNix/stackage':
    'github:input-output-hk/stackage.nix/b7d2fd6' (2025-04-04)
  → 'github:input-output-hk/stackage.nix/f92d00a' (2025-12-20)
• Updated input 'iohk-nix':
    'github:input-output-hk/iohk-nix/3acad18' (2025-04-03)
  → 'github:input-output-hk/iohk-nix/a704b93' (2025-11-12)
• Updated input 'iohk-nix/nixpkgs':
    'github:nixos/nixpkgs/55af203' (2023-05-15)
  → 'github:nixos/nixpkgs/a47938d' (2025-06-28)