gh-glance 0.13.0 → 0.13.1

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 (3) hide show
  1. package/CHANGELOG.md +24 -1
  2. package/index.mjs +38 -0
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -7,6 +7,28 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.13.1] - 2026-09-07
11
+
12
+ ### Fixed
13
+
14
+ - **Upgrading from 0.12.0 no longer leaves every pane unable to coordinate.**
15
+ 0.13.0 moved the coordination protocol from version 3 to 5 but could migrate
16
+ only version 1, and unlike earlier protocol changes it kept the state file in
17
+ the same place. A pane upgrading therefore read its own file, could neither
18
+ validate nor migrate it, and reported unusable coordination state on every
19
+ launch from then on -- the read happens before any write, so nothing ever
20
+ replaced the file. Versions 2, 3 and 4 now upgrade in place, keeping the
21
+ unsettled charges and observed budgets they hold. A file from a *newer*
22
+ protocol is still refused, which is the recoverable direction: the newer build
23
+ is still there to rewrite it.
24
+
25
+ - **Diagnostics and manual actions no longer pace as though they spent their
26
+ worst case.** Work is admitted against the most it could cost, and the
27
+ difference is returned once the real cost is known. That return was wired
28
+ into only one of the two settlement paths, so every `--doctor` probe and
29
+ manual operation kept paying for capacity it had not used, delaying later
30
+ requests for no reason.
31
+
10
32
  ## [0.13.0] - 2026-09-06
11
33
 
12
34
  ### Changed
@@ -904,7 +926,8 @@ engineering, security, QA and UX. What follows is what changed as a result.
904
926
  - The `main` field from `package.json`. It advertised the file as importable,
905
927
  but importing it took over the terminal or exited the host process.
906
928
 
907
- [Unreleased]: https://github.com/juan294/gh-glance/compare/v0.13.0...HEAD
929
+ [Unreleased]: https://github.com/juan294/gh-glance/compare/v0.13.1...HEAD
930
+ [0.13.1]: https://github.com/juan294/gh-glance/compare/v0.13.0...v0.13.1
908
931
  [0.13.0]: https://github.com/juan294/gh-glance/compare/v0.12.0...v0.13.0
909
932
  [0.12.0]: https://github.com/juan294/gh-glance/compare/v0.11.2...v0.12.0
910
933
  [0.11.2]: https://github.com/juan294/gh-glance/compare/v0.11.1...v0.11.2
package/index.mjs CHANGED
@@ -3510,7 +3510,36 @@ function serializeGovernorState(state) {
3510
3510
  return `${JSON.stringify(state)}\n`;
3511
3511
  }
3512
3512
 
3513
+ // Failing closed on a version this build does not write is right in one
3514
+ // direction only. An *older* binary meeting a newer file must refuse it, because
3515
+ // it cannot know what the new fields mean. A *newer* binary meeting an older
3516
+ // file at its own canonical path has to migrate it: the read happens before any
3517
+ // write, so refusing leaves the file in place and the pane never recovers. It is
3518
+ // not a transient failure, it is a permanent one, and every later launch repeats
3519
+ // it.
3520
+ //
3521
+ // That asymmetry was missed when the observer split moved the protocol from 3 to
3522
+ // 5. Versions 1 and 2 escaped it only because the file moved path at the same
3523
+ // time, so a new build simply found nothing and started clean; 3 onwards share a
3524
+ // path with 5 and collided.
3513
3525
  function migrateGovernorState(raw, nowMs) {
3526
+ if (!isRecord(raw) || !Number.isSafeInteger(raw.version) || raw.version < 1) return null;
3527
+ if (raw.version >= GOVERNOR_STATE_VERSION) return null;
3528
+ if (raw.version === 1) return migrateV1GovernorState(raw, nowMs);
3529
+ // Everything from 2 onwards is this build's shape or one adaptation away from
3530
+ // it, and what it holds is worth keeping: the uncertain reservations are debts
3531
+ // that must not be forgiven by an upgrade, and the budgets were written by the
3532
+ // same observers this build trusts.
3533
+ const shaped = raw.version >= GOVERNOR_OBSERVER_SPLIT_VERSION
3534
+ ? structuredClone(raw)
3535
+ : adaptPreSplitGovernorShape(raw);
3536
+ if (!shaped) return null;
3537
+ if (!isRecord(shaped.fairness)) shaped.fairness = { manualStreak: 0 };
3538
+ shaped.version = GOVERNOR_STATE_VERSION;
3539
+ return normalizeGovernorState(shaped, nowMs);
3540
+ }
3541
+
3542
+ function migrateV1GovernorState(raw, nowMs) {
3514
3543
  if (!exactKeys(raw, [
3515
3544
  "version", "epochs", "budgets", "probeClaim", "probeOutcome", "leases", // v1 shape
3516
3545
  "intents", "reservations", "manualProbe",
@@ -4478,6 +4507,15 @@ function completeReservation(scope, reservationId, completion, nowMs) {
4478
4507
  reservation.outcome = completion.outcome;
4479
4508
  reservation.actualCosts = { ...measured };
4480
4509
  reservation.accountedCosts = { core: 0, graphql: 0 };
4510
+ // The same rule as the observing settlement path: this one narrows the
4511
+ // charge in exactly the same way, so it releases the pacing in exactly the
4512
+ // same way. Wiring it into only one of the two settlement paths left every
4513
+ // doctor probe and manual operation paying for capacity it did not use.
4514
+ if (completion.outcome === "measured-success") {
4515
+ for (const resource of RATE_RESOURCES) {
4516
+ returnPacingCredit(state, resource, reservation.costs[resource] - measured[resource], at);
4517
+ }
4518
+ }
4481
4519
  return { value: { status: "completed", actualCosts: reservation.actualCosts } };
4482
4520
  });
4483
4521
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gh-glance",
3
- "version": "0.13.0",
3
+ "version": "0.13.1",
4
4
  "description": "A live-refreshing GitHub dashboard for your terminal: Actions, Issues, Pull Requests, and Security alerts in one narrow pane",
5
5
  "type": "module",
6
6
  "bin": {