gh-glance 0.15.2 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/CHANGELOG.md +52 -1
  2. package/README.md +76 -25
  3. package/index.mjs +2237 -500
  4. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,56 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.16.0] - 2026-10-01
11
+
12
+ ### Changed
13
+
14
+ - Default to `--background on-demand`: automatically update active Actions,
15
+ check secondary tabs when opened or refreshed, and retain cached results with
16
+ visible source age. Keep `all` and `off` for continuous polling.
17
+
18
+ ### Added
19
+
20
+ - Add a strict 24-hour freshness monitor with a fixed candidate hash, declared
21
+ pane cohort and cadence, source-clock checks, process-liveness checks, and
22
+ independently captured provider-hold evidence. Earlier monitor manifests
23
+ remain available for diagnostics but cannot qualify a run.
24
+ - Add a deterministic 72-hour request and governor oracle with repeated quota
25
+ resets, persistent pane cohorts, interrupted requests, and a completion-write
26
+ retry that reconciles charges without repeating HTTP requests.
27
+
28
+ ### Fixed
29
+
30
+ - Recover automatically from full or aged quota receipt stores and acquisition
31
+ uncertainty while preserving retained charges and cached data.
32
+ - Allow up to three admitted HTTP requests per host while retaining FIFO starts,
33
+ shared pacing, quota reserves and provider backoff. Renew queued lease ownership
34
+ within the original request deadline.
35
+ - Keep active Actions progressing while a slower secondary response is in flight.
36
+ - Read shared acquisition snapshots consistently during concurrent publication.
37
+ - Retain requested collector refreshes through connection setup and welcome.
38
+ - Advance the displayed cached source age while secondary views are idle,
39
+ without requesting another observation.
40
+ - Preserve diagnostic recovery notices until a matching guarded write proves
41
+ recovery, and tolerate event-clock rollback without discarding journal evidence.
42
+ - Preserve bounded Core and GraphQL recovery causes across observer handoffs and
43
+ show the next action and truthful cached source age in normal, narrow,
44
+ no-color, and screen-reader output.
45
+ - Let plain `--doctor` inspect a unique cached quota scope without GitHub calls
46
+ or state writes. Report bounded receipt/debt and recovery diagnostics while
47
+ withholding credential and quota fingerprints.
48
+ - Keep legacy and interrupted request charges until causal observer evidence
49
+ can retire them; the diagnostic journal does not grant or refund quota.
50
+
51
+ ### Migration
52
+
53
+ - Restart every pane sharing coordination state with the upgraded binary;
54
+ mixed-version coordination is unsupported.
55
+ An older live transport request must drain before registry migration, and older
56
+ writers are fenced afterward. Retained quota charges are preserved.
57
+ - Rolling back to v0.15.2 cannot restore operation against the newer ledger.
58
+ Use a compatible repaired version and retain the current accounting state.
59
+
10
60
  ## [0.15.2] - 2026-09-24
11
61
 
12
62
  ### Fixed
@@ -1166,7 +1216,8 @@ engineering, security, QA and UX. What follows is what changed as a result.
1166
1216
  - The `main` field from `package.json`. It advertised the file as importable,
1167
1217
  but importing it took over the terminal or exited the host process.
1168
1218
 
1169
- [Unreleased]: https://github.com/juan294/gh-glance/compare/v0.15.2...HEAD
1219
+ [Unreleased]: https://github.com/juan294/gh-glance/compare/v0.16.0...HEAD
1220
+ [0.16.0]: https://github.com/juan294/gh-glance/compare/v0.15.2...v0.16.0
1170
1221
  [0.15.2]: https://github.com/juan294/gh-glance/compare/v0.15.1...v0.15.2
1171
1222
  [0.15.1]: https://github.com/juan294/gh-glance/compare/v0.15.0...v0.15.1
1172
1223
  [0.15.0]: https://github.com/juan294/gh-glance/compare/v0.14.1...v0.15.0
package/README.md CHANGED
@@ -84,10 +84,11 @@ current.
84
84
  - `lazygit`-style panel frame: the tab name sits in the top border, the
85
85
  visible-of-total row count in the bottom
86
86
  - A semantic footer for the active tab: **Watching** is settled or scheduled;
87
+ **Cached** shows a secondary view's source age until its next requested refresh;
87
88
  **Checking** means admitted work is live; **Paused** protects a held or
88
89
  unavailable budget; **Failed** names a normal fetch error; and **Limited**
89
90
  means Security visibility is incomplete. Startup and manual Checking can
90
- animate. Adapted automatic checks, Watching, Paused, Failed, and Limited are
91
+ animate. Adapted automatic checks, Watching, Cached, Paused, Failed, and Limited are
91
92
  static. A pure shared-lane wait adds `sharing N` without moving the key hints
92
93
  - Row state icons are real GitHub Octicons (via the Nerd Font glyph set), not emoji
93
94
  -- with a plain-ASCII fallback for terminals without one
@@ -319,9 +320,9 @@ pane definition. Flags are there when you want them:
319
320
  | Flag | Effect |
320
321
  |---|---|
321
322
  | `-R`, `--repo [host/]owner/name` | Watch a specific repository instead of the current directory's. Works from anywhere -- you do not need a local clone. The optional host targets a GitHub Enterprise or EMU data-residency tenant, e.g. `tenant.ghe.com/acme/widget`. |
322
- | `--refresh <seconds>` | Minimum active-tab poll interval, 2-3600, default 5. A floor, never a ceiling: a tab whose content is unchanged twice in a row slows to 30 seconds (60 for Security), and Actions with a running or queued job is checked every 5. Safe shared grants can make any check later. |
323
+ | `--refresh <seconds>` | Minimum automatic poll interval, 2-3600, default 5. A floor, never a ceiling: a tab whose content is unchanged twice in a row slows to 30 seconds (60 for Security), and Actions with a running or queued job is checked every 5. Safe shared grants can make any check later. |
323
324
  | `--tab <name>` | Start on `actions`, `issues`, `prs` or `security`. |
324
- | `--background <mode>` | `all` (default) or `off`. `off` never requests data for a tab you are not looking at. Its count keeps the last known value and ages visibly, and switching to the tab fetches it. |
325
+ | `--background <mode>` | `on-demand` (default): active Actions updates automatically; Issues, PRs and Security use cached data and check when opened or refreshed with `r`. Unopened tabs make no data requests. `all` continuously polls active and inactive tabs; `off` continuously polls only the selected tab. |
325
326
  | `--verbose` | Log one line per dashboard `gh` call to stderr, with timing and outcome. Credential lookup and the account-identity proof are deliberately excluded, so that they cannot log anything derived from a token. stderr must be redirected: `gh-glance --verbose 2>gh-glance.log`. |
326
327
  | `--doctor` | Print a diagnostic report and exit. See [Diagnostics](#diagnostics). |
327
328
  | `--doctor --probe` | Add bounded, admitted GitHub capability probes to the local diagnostic report. |
@@ -721,6 +722,13 @@ secondary limits, disconnected acquisition, shared producer waits, and local
721
722
  coordination. Doctor checks acquisition metadata, referenced snapshot artifacts,
722
723
  and the lock path separately. A readable metadata file does not make an aged
723
724
  orphan lock or invalid snapshot healthy; plain doctor never repairs either.
725
+ When one cached identity names the selected host, plain doctor also reads its
726
+ quota ledger without changing it or contacting GitHub. It reports receipt and
727
+ debt totals, observer age, the current recovery cause, and an actual retry time
728
+ when one is known. If several cached identities match, it says the scope is
729
+ unavailable instead of choosing an account. For a corrupt or unreadable quota
730
+ file, it names the coordination directory and a `quota-[redacted].json` file
731
+ pattern with the cause. The private quota fingerprint stays out of reports.
724
732
 
725
733
  Use `gh-glance --doctor --probe` when live capability evidence is needed. Probe
726
734
  mode uses an ephemeral governor lease and admits every quota-consuming endpoint
@@ -730,8 +738,9 @@ classification (`unavailable`, `rate-limited`, `auth-problem` or `other`). A
730
738
  probe whose safe slot is later, whose resource is held, or whose budget is
731
739
  unavailable is reported as `SKIPPED`; diagnostics do not bypass the reserve.
732
740
  The `API governor` section then reports its measured health and resource state.
733
- Neither mode prints scope hashes, account identity keys, state paths, lock
734
- owners, reservation IDs, response bodies, or credentials.
741
+ Neither mode prints scope hashes, account identity keys, quota fingerprints,
742
+ lock owners, reservation IDs, response bodies, or credentials. The redacted
743
+ state-file location above is shown only when local recovery needs it.
735
744
 
736
745
  The `Repository access` probe shows whether the target resolves for the active
737
746
  `gh` credentials. A failed GitHub resolution response cannot distinguish a
@@ -761,7 +770,15 @@ The default five seconds is a healthy single-pane floor, not an unconditional
761
770
  request frequency. Each check first needs an atomic grant from the private
762
771
  governor shared by local panes using the same effective host and account
763
772
  namespace. Beyond that floor, the cadence follows what the repository is
764
- actually doing:
773
+ actually doing. By default only active Actions polls automatically. Secondary
774
+ views show Cached with source age after their requested observation; open the tab
775
+ or press `r` to check again. Opening can reuse a fresh shared snapshot. An
776
+ outstanding request continues through admission delays and transient failures.
777
+ Cached tab counts are last known values, not live counts. `--background all`
778
+ restores continuous polling of all tabs; `--background off` continuously polls
779
+ only the selected tab.
780
+
781
+ The following cadence table applies to automatically polled tabs:
765
782
 
766
783
  | State | Earliest normal check |
767
784
  |---|---|
@@ -844,6 +861,14 @@ rate-limiting, it loads the saved rows for that same target immediately. Their
844
861
  age remains explicit through the `stale` label, and the rate-limit banner still
845
862
  describes the current failed request; cached data never turns a failure into a
846
863
  false success.
864
+ After a sustained hold, the notice names the Core or GraphQL cause, the next
865
+ automatic retry or required action, and the age of the last complete source
866
+ observation. A successful budget check, cache load, or heartbeat cannot make
867
+ old rows fresh. A validated unchanged `304` response does count as a new source
868
+ observation. Unknown older spend remains charged until owner/child quiescence
869
+ and a later matching observer establish safe retirement. A verified new system
870
+ boot can establish that boundary for otherwise unidentifiable older children;
871
+ the app does not assume a restart or reset alone refunded the charge.
847
872
 
848
873
  The enforceable boundary is admission from fresh, conservatively debited
849
874
  evidence. Another program can spend after the probe, and standalone panes in a
@@ -886,29 +911,51 @@ fixture performance is not presented as live GitHub performance.
886
911
  ### Live freshness monitor
887
912
 
888
913
  The repository includes a read-only monitor for a declared set of live panes.
889
- Create its manifest independently of the acquisition store, after each pane has
890
- installed its subscription. Each entry fixes the pane ID, process ID, expected
891
- repository and tab, start time in Unix milliseconds, and effective cadence:
892
-
893
- ```json
894
- {"schema":1,"panes":[{"id":"my-pane","pid":12345,"repository":"owner/repo","tab":"actions","startedAt":1800000000000,"cadenceMs":5000}]}
895
- ```
914
+ For a qualifying run, create a schema 2 manifest independently of the
915
+ acquisition store before sampling. Fix `candidateHash` to the SHA-256 of the
916
+ exact executable, `requestedDurationMs` to the full observation window, and
917
+ `sampleIntervalMs` to `5000`. Each expected pane needs its ID, live PID,
918
+ repository, repository ID, tab, host, access-key fingerprint, start time in
919
+ Unix milliseconds, and declared `cadenceMs`. The monitor checks the live PID
920
+ and subscription against that fixed identity on every sample. It derives the
921
+ source deadline from the declared cadence, not from the app's `nextDueAt`.
896
922
 
897
- Use the same private config root as the panes and write a new report path:
923
+ Use the panes' private config root and a new report path:
898
924
 
899
925
  ```sh
900
- node scripts/freshness-monitor.mjs --manifest panes.json \
926
+ node scripts/freshness-monitor.mjs --manifest "$GH_GLANCE_QUALIFICATION_MANIFEST" \
901
927
  --store "$XDG_CONFIG_HOME/gh-glance/coordination-v2/acquisition.json" \
902
- --report freshness.jsonl --interval-ms 5000 --duration-ms 86400000
928
+ --quota "$GH_GLANCE_MONITORED_QUOTA" \
929
+ --candidate "$(command -v gh-glance)" --report freshness.jsonl \
930
+ --interval-ms 5000 --duration-ms 86400000
903
931
  ```
904
932
 
905
- The JSONL contains hashed pane/target labels, validated source-success times,
906
- maximum eligible gaps, overdue time, lock status, and declared hold intervals.
907
- Missing or expired subscriptions and corrupt stores fail the run, even when no
908
- subscriber remains in the store. To close an expectation after an intentional
909
- pane exit, add its `exitedAt` timestamp to the manifest. Only a `holds` interval
910
- declared in that pane entry and confirmed by the runtime excludes time from
911
- the cadence check. No GitHub request is made by the monitor.
933
+ Set `GH_GLANCE_MONITORED_QUOTA` to the quota file selected by the cached
934
+ registry entry matching every declared host and access-key fingerprint; the
935
+ monitor rejects an unrelated or ambiguous scope. Resolve the installed
936
+ executable with `command -v gh-glance` only after confirming it is the exact
937
+ candidate under observation. The JSONL records requested and actual
938
+ elapsed time, sample gaps, source clocks, maximum eligible gaps, overdue time,
939
+ observer/debt counts, and separate wall, external-outage, and eligible windows.
940
+ Its `windowClass` is derived from requested duration: `short` below 30
941
+ minutes, `initial` from 30 minutes to under 24 hours, and `full` from 24 hours.
942
+ A successful short or initial report does not complete 24-hour acceptance;
943
+ that requires a separate full window and its declared live transitions.
944
+ Missing or expired subscriptions, a disappeared PID, corrupt stores, an early
945
+ SIGINT, or a sample gap over 15 seconds makes the report nonqualifying. A sleep
946
+ gap requires a new uninterrupted window. A schema 1 manifest supports a
947
+ diagnostic `--once` read only; it cannot qualify a run.
948
+
949
+ Provider-limit exclusions require `--external-evidence` pointing to an
950
+ append-only JSON trace with `schema: 1` and an `events` array. Each `raw-http`
951
+ event records the independently captured issue and finish timestamps, host,
952
+ access-key fingerprint, Core or GraphQL resource, and raw HTTP status line and
953
+ bounded headers. The monitor derives the interval from a matching 429 or
954
+ rate-exhausted 403 response with `Retry-After`. It reloads the trace on every
955
+ sample and rejects changed past records or future responses. App hold labels
956
+ and synthetic request-oracle records grant no exclusion. Local coordination
957
+ holds fail qualification even when brief. Keep this trace and the manifest private. The
958
+ monitor makes no GitHub request or coordination write.
912
959
 
913
960
  ## Limitations
914
961
 
@@ -1022,9 +1069,13 @@ GH_GLANCE_ICONS=ascii gh-glance
1022
1069
  | It exits immediately when piped | Intentional. It is a full-screen dashboard, not a reporting command. |
1023
1070
  | `Restart required: close older gh-glance panes` | A pane from an older release still holds a live lease in the previous coordination format, which this version cannot join. Quit those panes; this one resumes on its own. Nothing is killed for you, and no state needs deleting. |
1024
1071
  | `Upgrade waiting for legacy quota reset` | The older panes are gone, but left spend that cannot be proven settled. It waits for the affected rate-limit window to reset rather than assume the quota is free. This one has a deadline and clears itself. |
1025
- | `Upgrade blocked: older spend cannot be settled or waited out` | The same unsettled spend, but the older file records no reset to wait for, so there is no deadline and it will not clear on its own. Quit every pane, then delete the `rate-governor-v1-*.json` files in the configuration directory -- they are the leftover evidence, and with no reset recorded there is nothing further to learn from them. Coordination starts clean on the next launch. |
1072
+ | `Upgrade blocked: older spend cannot be settled or waited out` | Older spend has no reliable reset or child evidence. Verify old children ended and use `gh-glance --doctor` to inspect reserved units. An unidentifiable old child may require a verified system restart before a later observer can retire its charge. Keep the quota files as accounting evidence. |
1026
1073
  | `Coordination state unavailable; evidence preserved` | Coordination state could not be read and was deliberately not overwritten, so nothing is lost. Run `gh-glance --doctor` and check the config directory's permissions. |
1027
- | `Shared HTTP request or cooldown in progress` | Panes share one outbound request slot and one cooldown. Another pane holds it, or a server-sent `Retry-After` is still running. Normal pacing, not an error. |
1074
+ | `Local coordination` corruption, overflow, full storage, or unsafe permissions | Run `gh-glance --doctor`; its read-only report gives the exact cause and redacted local file location when known. Repair storage or permissions, then let the app retry. Preserve corrupt and overflow evidence for diagnosis. |
1075
+ | `Saving request result` or `Recovering interrupted request` | The original request may have consumed quota. The app keeps its charge, retries settlement without repeating HTTP, and waits for safe observer evidence before retiring uncertain debt. |
1076
+ | `GitHub request limit` | A shared secondary hold preserves GitHub's retry deadline. Wait for the shown time; if the repeated-limit circuit explicitly asks, press `r` once to retry. Repeated refresh cannot bypass a hold. |
1077
+ | `Collector connection unavailable` or `GitHub App authorization unavailable` | Restore the selected collector connection or App authorization. The mode does not switch to independent requests. Cached rows remain labeled with their source age. |
1078
+ | `Shared HTTP request or cooldown in progress` | Panes share up to three admitted requests per host, FIFO starts with at least 250 ms spacing, and one cooldown. The pool is full, a queued request is ahead, or a server-sent `Retry-After` is still running. Normal pacing, not an error. |
1028
1079
  | `Verified identity unavailable; waiting to retry` | The account behind your credential has not been confirmed yet -- not signed in, a denied or malformed proof, or a slow credential helper. Check `gh auth status`. Retries are rate-limited, so this clears on its own rather than immediately. |
1029
1080
  | It stopped updating and you cannot tell why | Run `gh-glance --verbose 2>gh-glance.log`, reproduce, then read the log: one line per dashboard `gh` call with its duration and outcome. Credential lookup and the identity proof are excluded by design. It is redacted the same way `--doctor` is, so it is safe to attach to a bug report. `--doctor --verbose` logs the probes too. |
1030
1081
  | `--verbose` refuses to start | stderr is still your terminal, where the log would draw over the dashboard. Redirect it to a file. |