M-11 fix: a reading takes its licence's lease, and a grant the vault does not answer is a failed reading (issue 1409)

Review round 1, finding 5: the reading reads the grant under the licence's
lease (another run holding it reads nothing that round), and a grant the
vault refuses writes readError, keeps the percentages and goes on to the
next licence, so the conditions are still said after the round.

Review round 1, finding 6: these answers were built by earlier commits that
did not name them: B26, B27, B28 and B29 by M-6 (9cc62818), B30 by M-9
(39912119), B35 by M-11 (6cf98386); B23 and B24 by V-1 (e85cf002) and M-7
(5ca3876b), named again in b8e8be61; B31 and B32 in 36d9d739.

Builds: design 39 bus-and-switch §4, §16, §24, §31, B23, B24, B26, B27, B28, B29, B30, B34, B35

Claude-Session: https://claude.ai/code/session_01NcbgfHb9d4T9nq5Q6WqdiJ
This commit is contained in:
2026-11-12 01:04:07 +01:00
parent b8e8be619d
commit c8e321c8e1
2 changed files with 47 additions and 4 deletions
@@ -686,11 +686,26 @@ func (m *Manager) ReadUsageOf(ctx context.Context, licence string, dry bool) ([]
if l.Kind != "subscription" || l.Generation == 0 || (licence != "" && l.Name != licence) {
continue
}
g, err := m.grantOf(ctx, l)
// The reading takes the licence's grant under its lease, so it never reads a grant a refresh is replacing (§4,
// §16); a grant the vault does not answer is a failed reading like a vendor's refusal (§31).
key := "licence." + KeyFor(l.Name)
leased, err := m.Store.Lease(ctx, key, m.Holder, leaseFor)
if err != nil {
return out, err
}
raw, err := m.Vendor.Usage(ctx, g.AccessToken)
if !leased {
out = append(out, Read{Licence: l.Name, Error: "another run holds its lease", Switches: []Switch{}})
continue
}
unlease := func() { _ = m.Store.Unlease(ctx, key, m.Holder) }
if fresh, err := m.Store.Licence(ctx, l.Name); err == nil && fresh != nil {
l = *fresh
}
var raw map[string]any
g, err := m.grantOf(ctx, l)
if err == nil {
raw, err = m.Vendor.Usage(ctx, g.AccessToken)
}
g = FullGrant{}
if err == nil && raw == nil {
err = errors.New("the usage endpoint answered nothing")
@@ -703,7 +718,9 @@ func (m *Manager) ReadUsageOf(ctx context.Context, licence string, dry bool) ([]
}
kept.ReadError = err.Error()
l.Usage = &kept
if err := m.Store.SaveLicence(ctx, l); err != nil {
err := m.Store.SaveLicence(ctx, l)
unlease()
if err != nil {
return out, err
}
m.Log("reading the usage of %s failed: %v", l.Name, err)
@@ -712,13 +729,16 @@ func (m *Manager) ReadUsageOf(ctx context.Context, licence string, dry bool) ([]
}
reading := FlattenUsage(raw)
if err := m.Store.RecordUsage(ctx, l.Name, m.Now().UnixMilli(), reading, raw); err != nil {
unlease()
return out, err
}
// The latest reading beside its licence, so licences answers from one key (§2).
latest := reading
latest.ReadAt = m.Now().UTC().Format(time.RFC3339Nano)
l.Usage = &latest
if err := m.Store.SaveLicence(ctx, l); err != nil {
err = m.Store.SaveLicence(ctx, l)
unlease()
if err != nil {
return out, err
}
_ = m.Emit("usage.read", map[string]any{"licence": l.Name, "sessionPct": reading.SessionPct,
@@ -64,6 +64,29 @@ func TestAFailedReadingKeepsThePercentages(t *testing.T) {
if n := len(mm.usage.entries); n != 1 {
t.Fatalf("a failed reading appended: %d entries", n)
}
// §16, §31: a grant the vault does not answer is a failed reading, written and gone past, never the end of the round.
mm.vendor.usageErr = nil
mm.grants.mu.Lock()
delete(mm.grants.held, SecretName(KeyFor(licence1), PartGrant))
mm.grants.mu.Unlock()
if err := mm.m.ReadUsage(ctx); err != nil {
t.Fatalf("a grant the vault did not answer ended the round: %v", err)
}
l, _ = mm.store.Licence(ctx, licence1)
if !strings.Contains(l.Usage.ReadError, "cannot be read from the vault") || *l.Usage.WeeklyPct != 97 {
t.Fatalf("after the vault's refusal the reading is %+v", l.Usage)
}
// §4, §16: a reading is taken under the licence's lease; with another run holding it, nothing is read or written.
if ok, _ := mm.store.Lease(ctx, "licence."+KeyFor(licence1), "another run", time.Minute); !ok {
t.Fatal("the lease was not taken")
}
reads, err := mm.m.ReadUsageOf(ctx, licence1, true)
if err != nil || len(reads) != 1 || reads[0].Error != "another run holds its lease" {
t.Fatalf("a reading under another's lease answered %+v, %v", reads, err)
}
if again, _ := mm.store.Licence(ctx, licence1); again.Usage.ReadError != l.Usage.ReadError {
t.Fatalf("a reading under another's lease wrote %+v", again.Usage)
}
}
// design 39 bus-and-switch §25/AC1, §48: a table of readings, ages and healths and the room each gives.