gh-glance 0.13.0 → 0.13.2
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.
- package/CHANGELOG.md +39 -1
- package/index.mjs +51 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,42 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.13.2] - 2026-09-07
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **"Upgrade waiting for legacy quota reset" could never clear.** A pane holding
|
|
15
|
+
a pre-0.12.0 ledger with unsettled GraphQL charges waited on a deadline that
|
|
16
|
+
did not exist, so the message stayed for as long as that file was present --
|
|
17
|
+
weeks, in one case, long after the quota windows it was waiting for had ended.
|
|
18
|
+
Migrating such a ledger deliberately drops its budgets, because their numbers
|
|
19
|
+
came from `/rate_limit` and were never spendable capacity; the reset times
|
|
20
|
+
recorded alongside them are still facts about when those windows closed, and
|
|
21
|
+
are now read as the deadline they always were. Reading a reset is not the same
|
|
22
|
+
as spending against it. Affected panes clear themselves on the next launch.
|
|
23
|
+
|
|
24
|
+
## [0.13.1] - 2026-09-07
|
|
25
|
+
|
|
26
|
+
### Fixed
|
|
27
|
+
|
|
28
|
+
- **Upgrading from 0.12.0 no longer leaves every pane unable to coordinate.**
|
|
29
|
+
0.13.0 moved the coordination protocol from version 3 to 5 but could migrate
|
|
30
|
+
only version 1, and unlike earlier protocol changes it kept the state file in
|
|
31
|
+
the same place. A pane upgrading therefore read its own file, could neither
|
|
32
|
+
validate nor migrate it, and reported unusable coordination state on every
|
|
33
|
+
launch from then on -- the read happens before any write, so nothing ever
|
|
34
|
+
replaced the file. Versions 2, 3 and 4 now upgrade in place, keeping the
|
|
35
|
+
unsettled charges and observed budgets they hold. A file from a *newer*
|
|
36
|
+
protocol is still refused, which is the recoverable direction: the newer build
|
|
37
|
+
is still there to rewrite it.
|
|
38
|
+
|
|
39
|
+
- **Diagnostics and manual actions no longer pace as though they spent their
|
|
40
|
+
worst case.** Work is admitted against the most it could cost, and the
|
|
41
|
+
difference is returned once the real cost is known. That return was wired
|
|
42
|
+
into only one of the two settlement paths, so every `--doctor` probe and
|
|
43
|
+
manual operation kept paying for capacity it had not used, delaying later
|
|
44
|
+
requests for no reason.
|
|
45
|
+
|
|
10
46
|
## [0.13.0] - 2026-09-06
|
|
11
47
|
|
|
12
48
|
### Changed
|
|
@@ -904,7 +940,9 @@ engineering, security, QA and UX. What follows is what changed as a result.
|
|
|
904
940
|
- The `main` field from `package.json`. It advertised the file as importable,
|
|
905
941
|
but importing it took over the terminal or exited the host process.
|
|
906
942
|
|
|
907
|
-
[Unreleased]: https://github.com/juan294/gh-glance/compare/v0.13.
|
|
943
|
+
[Unreleased]: https://github.com/juan294/gh-glance/compare/v0.13.2...HEAD
|
|
944
|
+
[0.13.2]: https://github.com/juan294/gh-glance/compare/v0.13.1...v0.13.2
|
|
945
|
+
[0.13.1]: https://github.com/juan294/gh-glance/compare/v0.13.0...v0.13.1
|
|
908
946
|
[0.13.0]: https://github.com/juan294/gh-glance/compare/v0.12.0...v0.13.0
|
|
909
947
|
[0.12.0]: https://github.com/juan294/gh-glance/compare/v0.11.2...v0.12.0
|
|
910
948
|
[0.11.2]: https://github.com/juan294/gh-glance/compare/v0.11.1...v0.11.2
|
package/index.mjs
CHANGED
|
@@ -2528,7 +2528,19 @@ function inspectLegacyMigration(root, state, now) {
|
|
|
2528
2528
|
const uncertain = Object.values(legacy.reservations).some((reservation) =>
|
|
2529
2529
|
["started", "completed"].includes(reservation.status) && reservationCost(reservation, resource, legacy.leases, now) > 0);
|
|
2530
2530
|
if (uncertain) {
|
|
2531
|
-
|
|
2531
|
+
// The deadline may only be read from the migrated view for resources the
|
|
2532
|
+
// migration kept. A v1 file's budgets are dropped on purpose -- they came
|
|
2533
|
+
// from /rate_limit and are not spendable capacity -- but the reset they
|
|
2534
|
+
// record is still a fact about when that window ended, and using it as a
|
|
2535
|
+
// deadline is not the same as admitting work against it. Core already had
|
|
2536
|
+
// a reconstruction above; GraphQL never did, so a v1 file holding any
|
|
2537
|
+
// uncertain GraphQL charge asked a dropped budget for its reset, found
|
|
2538
|
+
// none, and reported legacy-unresolved -- which has no deadline and so
|
|
2539
|
+
// never cleared. The file it came from had a perfectly good reset in it
|
|
2540
|
+
// the whole time.
|
|
2541
|
+
const reset = Number.isFinite(budget?.resetMs)
|
|
2542
|
+
? budget.resetMs
|
|
2543
|
+
: Number(raw?.budgets?.[resource]?.resetMs);
|
|
2532
2544
|
if (!Number.isFinite(reset)) return { ok: false, reason: "legacy-unresolved" };
|
|
2533
2545
|
holdUntil = Math.max(holdUntil, reset + BUDGET_RESET_GRACE_MS);
|
|
2534
2546
|
}
|
|
@@ -3510,7 +3522,36 @@ function serializeGovernorState(state) {
|
|
|
3510
3522
|
return `${JSON.stringify(state)}\n`;
|
|
3511
3523
|
}
|
|
3512
3524
|
|
|
3525
|
+
// Failing closed on a version this build does not write is right in one
|
|
3526
|
+
// direction only. An *older* binary meeting a newer file must refuse it, because
|
|
3527
|
+
// it cannot know what the new fields mean. A *newer* binary meeting an older
|
|
3528
|
+
// file at its own canonical path has to migrate it: the read happens before any
|
|
3529
|
+
// write, so refusing leaves the file in place and the pane never recovers. It is
|
|
3530
|
+
// not a transient failure, it is a permanent one, and every later launch repeats
|
|
3531
|
+
// it.
|
|
3532
|
+
//
|
|
3533
|
+
// That asymmetry was missed when the observer split moved the protocol from 3 to
|
|
3534
|
+
// 5. Versions 1 and 2 escaped it only because the file moved path at the same
|
|
3535
|
+
// time, so a new build simply found nothing and started clean; 3 onwards share a
|
|
3536
|
+
// path with 5 and collided.
|
|
3513
3537
|
function migrateGovernorState(raw, nowMs) {
|
|
3538
|
+
if (!isRecord(raw) || !Number.isSafeInteger(raw.version) || raw.version < 1) return null;
|
|
3539
|
+
if (raw.version >= GOVERNOR_STATE_VERSION) return null;
|
|
3540
|
+
if (raw.version === 1) return migrateV1GovernorState(raw, nowMs);
|
|
3541
|
+
// Everything from 2 onwards is this build's shape or one adaptation away from
|
|
3542
|
+
// it, and what it holds is worth keeping: the uncertain reservations are debts
|
|
3543
|
+
// that must not be forgiven by an upgrade, and the budgets were written by the
|
|
3544
|
+
// same observers this build trusts.
|
|
3545
|
+
const shaped = raw.version >= GOVERNOR_OBSERVER_SPLIT_VERSION
|
|
3546
|
+
? structuredClone(raw)
|
|
3547
|
+
: adaptPreSplitGovernorShape(raw);
|
|
3548
|
+
if (!shaped) return null;
|
|
3549
|
+
if (!isRecord(shaped.fairness)) shaped.fairness = { manualStreak: 0 };
|
|
3550
|
+
shaped.version = GOVERNOR_STATE_VERSION;
|
|
3551
|
+
return normalizeGovernorState(shaped, nowMs);
|
|
3552
|
+
}
|
|
3553
|
+
|
|
3554
|
+
function migrateV1GovernorState(raw, nowMs) {
|
|
3514
3555
|
if (!exactKeys(raw, [
|
|
3515
3556
|
"version", "epochs", "budgets", "probeClaim", "probeOutcome", "leases", // v1 shape
|
|
3516
3557
|
"intents", "reservations", "manualProbe",
|
|
@@ -4478,6 +4519,15 @@ function completeReservation(scope, reservationId, completion, nowMs) {
|
|
|
4478
4519
|
reservation.outcome = completion.outcome;
|
|
4479
4520
|
reservation.actualCosts = { ...measured };
|
|
4480
4521
|
reservation.accountedCosts = { core: 0, graphql: 0 };
|
|
4522
|
+
// The same rule as the observing settlement path: this one narrows the
|
|
4523
|
+
// charge in exactly the same way, so it releases the pacing in exactly the
|
|
4524
|
+
// same way. Wiring it into only one of the two settlement paths left every
|
|
4525
|
+
// doctor probe and manual operation paying for capacity it did not use.
|
|
4526
|
+
if (completion.outcome === "measured-success") {
|
|
4527
|
+
for (const resource of RATE_RESOURCES) {
|
|
4528
|
+
returnPacingCredit(state, resource, reservation.costs[resource] - measured[resource], at);
|
|
4529
|
+
}
|
|
4530
|
+
}
|
|
4481
4531
|
return { value: { status: "completed", actualCosts: reservation.actualCosts } };
|
|
4482
4532
|
});
|
|
4483
4533
|
}
|
package/package.json
CHANGED