cctally 1.114.0 → 1.115.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.
- package/CHANGELOG.md +25 -0
- package/bin/_cctally_account.py +32 -8
- package/bin/_cctally_cache.py +4394 -279
- package/bin/_cctally_completion.py +1287 -0
- package/bin/_cctally_config.py +37 -0
- package/bin/_cctally_core.py +178 -2
- package/bin/_cctally_dashboard.py +2 -2
- package/bin/_cctally_dashboard_conversation.py +292 -23
- package/bin/_cctally_dashboard_envelope.py +16 -3
- package/bin/_cctally_dashboard_sources.py +18 -9
- package/bin/_cctally_db.py +316 -5
- package/bin/_cctally_doctor.py +436 -27
- package/bin/_cctally_five_hour.py +82 -31
- package/bin/_cctally_forecast.py +81 -9
- package/bin/_cctally_journal.py +3884 -112
- package/bin/_cctally_journal_repair.py +101 -12
- package/bin/_cctally_parser.py +42 -0
- package/bin/_cctally_project.py +8 -2
- package/bin/_cctally_quarantine.py +652 -0
- package/bin/_cctally_quota.py +26 -4
- package/bin/_cctally_quota_model.py +360 -83
- package/bin/_cctally_record.py +677 -149
- package/bin/_cctally_rederive.py +297 -32
- package/bin/_cctally_refresh.py +84 -13
- package/bin/_cctally_source_analytics.py +23 -2
- package/bin/_cctally_statusline.py +6137 -517
- package/bin/_cctally_store.py +287 -48
- package/bin/_cctally_transcript.py +8 -7
- package/bin/_cctally_tui.py +57 -53
- package/bin/_lib_account_binding.py +152 -0
- package/bin/_lib_account_protocol.py +386 -0
- package/bin/_lib_aggregators.py +4 -0
- package/bin/_lib_cache_coverage.py +13 -1
- package/bin/_lib_cache_writer_lock.py +19 -3
- package/bin/_lib_clone_copy.py +142 -0
- package/bin/_lib_codex_conversation_query.py +133 -19
- package/bin/_lib_codex_conversation_watch.py +59 -14
- package/bin/_lib_codex_drain_admission.py +214 -0
- package/bin/_lib_codex_metadata.py +18 -1
- package/bin/_lib_codex_publication_barrier.py +277 -0
- package/bin/_lib_codex_rollout_container.py +502 -0
- package/bin/_lib_conversation_dispatch.py +3 -0
- package/bin/_lib_conversation_query.py +11 -3
- package/bin/_lib_detector_access_registry.py +3817 -0
- package/bin/_lib_doctor.py +452 -1
- package/bin/_lib_five_hour.py +126 -0
- package/bin/_lib_ingest_frontier.py +414 -20
- package/bin/_lib_journal.py +141 -10
- package/bin/_lib_jsonl.py +5 -4
- package/bin/_lib_legacy_spool.py +126 -0
- package/bin/_lib_legacy_writer_evidence.py +136 -0
- package/bin/_lib_merge_gate.py +29 -302
- package/bin/_lib_meter_rate_change.py +10 -0
- package/bin/_lib_outcome_quarantine.py +690 -0
- package/bin/_lib_pricing.py +7 -2
- package/bin/_lib_quarantine.py +154 -0
- package/bin/_lib_quota_calibration.py +53 -8
- package/bin/_lib_quota_copy.py +2 -1
- package/bin/_lib_quota_model.py +288 -46
- package/bin/_lib_rederive.py +94 -5
- package/bin/_lib_segment_summary.py +35 -2
- package/bin/_lib_statusline_candidates.py +292 -23
- package/bin/cctally +23 -1
- package/package.json +14 -1
package/bin/_lib_pricing.py
CHANGED
|
@@ -92,7 +92,12 @@ def _chip_for_model(name: str) -> str:
|
|
|
92
92
|
# fingerprint a store recorded and refuses a write from an older process. That
|
|
93
93
|
# comparison is day-granular by construction, so two revisions sharing a date
|
|
94
94
|
# compare equal and the older process is authorized to write.
|
|
95
|
-
|
|
95
|
+
#
|
|
96
|
+
# A sync of this date or of either table also changes the dynamic-SQL shape
|
|
97
|
+
# pinned in `bin/_lib_detector_access_registry.py`, so its registry test fails
|
|
98
|
+
# until the affected pins are re-pinned through the reviewed procedure in that
|
|
99
|
+
# module's docstring. Bulk regeneration of the pins alone is not acceptance.
|
|
100
|
+
PRICING_SNAPSHOT_DATE = "2026-10-14"
|
|
96
101
|
PRICING_STALENESS_DAYS = 60 # release pre-flight WARNs past this age
|
|
97
102
|
|
|
98
103
|
|
|
@@ -700,7 +705,7 @@ CLAUDE_MODEL_PRICING: dict[str, dict[str, Any]] = {
|
|
|
700
705
|
"input_cost_per_token": 2e-06,
|
|
701
706
|
"output_cost_per_token": 1e-05,
|
|
702
707
|
"cache_creation_input_token_cost": 2.5e-06,
|
|
703
|
-
"cache_read_input_token_cost":
|
|
708
|
+
"cache_read_input_token_cost": 1e-07,
|
|
704
709
|
},
|
|
705
710
|
}
|
|
706
711
|
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""The one parser of tests/quarantine.json, the reviewed flaky-test list (spec 4.3).
|
|
3
|
+
|
|
4
|
+
Stdlib only. An entry's `id` takes one of two forms:
|
|
5
|
+
|
|
6
|
+
* a pytest node id, `tests/<file>.py::<node>` (a prefix: pytest's --deselect
|
|
7
|
+
semantics cover every parametrization of a listed function);
|
|
8
|
+
* a harness case, `harness:<harness-name>::<case>`, where `<harness-name>` is
|
|
9
|
+
the `NAME` of `bin/cctally-NAME-test` and `<case>` is one scenario of that
|
|
10
|
+
harness's own case inventory (`bin/cctally-NAME-test --list-cases`).
|
|
11
|
+
|
|
12
|
+
`bin/cctally-test-all`, `bin/_lib_merge_gate.py` and the quick gate read the
|
|
13
|
+
list through this module; the scheduled CI quarantine leg calls its CLI. An
|
|
14
|
+
absent list quarantines nothing (the public mirror may carry none); a list
|
|
15
|
+
that exists but cannot be read raises QuarantineError, never "nothing listed".
|
|
16
|
+
|
|
17
|
+
CLI (exit 0, or 2 on an unreadable list or bad usage):
|
|
18
|
+
python3 bin/_lib_quarantine.py pytest-ids
|
|
19
|
+
python3 bin/_lib_quarantine.py harness-cases <harness>
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from dataclasses import dataclass
|
|
23
|
+
import json
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
import re
|
|
26
|
+
import subprocess
|
|
27
|
+
import sys
|
|
28
|
+
|
|
29
|
+
LIST_PATH = Path('tests') / 'quarantine.json'
|
|
30
|
+
FIELDS = ('id', 'evidence', 'invariant', 'issue', 'replacement', 'reviewBy')
|
|
31
|
+
PYTEST_ID = re.compile(r'tests/[^:\s]+\.py::\S+')
|
|
32
|
+
HARNESS_ID = re.compile(r'harness:([a-z0-9][a-z0-9-]*)::([A-Za-z0-9][A-Za-z0-9_.-]*)')
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class QuarantineError(ValueError):
|
|
36
|
+
"""tests/quarantine.json exists but is not a list this parser can apply."""
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass(frozen=True)
|
|
40
|
+
class Entry:
|
|
41
|
+
id: str
|
|
42
|
+
kind: str # 'pytest' or 'harness'
|
|
43
|
+
harness: str | None # the harness name for a harness case
|
|
44
|
+
case: str | None # the case name for a harness case
|
|
45
|
+
fields: dict
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def parse_id(entry_id: str) -> tuple[str, str | None, str | None]:
|
|
49
|
+
"""(kind, harness, case) of one id; QuarantineError for any other form."""
|
|
50
|
+
if not isinstance(entry_id, str):
|
|
51
|
+
raise QuarantineError(f'an entry id must be a string, not {entry_id!r}')
|
|
52
|
+
if PYTEST_ID.fullmatch(entry_id):
|
|
53
|
+
return 'pytest', None, None
|
|
54
|
+
match = HARNESS_ID.fullmatch(entry_id)
|
|
55
|
+
if match:
|
|
56
|
+
return 'harness', match.group(1), match.group(2)
|
|
57
|
+
raise QuarantineError(
|
|
58
|
+
f'{entry_id!r} is neither a pytest node id (tests/<file>.py::<node>) '
|
|
59
|
+
'nor a harness case (harness:<harness>::<case>)')
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def parse(doc) -> list[Entry]:
|
|
63
|
+
if not isinstance(doc, list):
|
|
64
|
+
raise QuarantineError('expected a JSON list of entries')
|
|
65
|
+
entries = []
|
|
66
|
+
for item in doc:
|
|
67
|
+
if not isinstance(item, dict):
|
|
68
|
+
raise QuarantineError(f'expected each entry to be an object, not {item!r}')
|
|
69
|
+
kind, harness, case = parse_id(item.get('id'))
|
|
70
|
+
entries.append(Entry(item['id'], kind, harness, case, dict(item)))
|
|
71
|
+
ids = [e.id for e in entries]
|
|
72
|
+
if len(set(ids)) != len(ids):
|
|
73
|
+
raise QuarantineError('duplicate entry ids: ' + ', '.join(
|
|
74
|
+
sorted({i for i in ids if ids.count(i) > 1})))
|
|
75
|
+
return entries
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def load_path(path: Path) -> list[Entry]:
|
|
79
|
+
path = Path(path)
|
|
80
|
+
if not path.is_file():
|
|
81
|
+
return []
|
|
82
|
+
try:
|
|
83
|
+
doc = json.loads(path.read_text(encoding='utf-8'))
|
|
84
|
+
except (OSError, ValueError) as exc:
|
|
85
|
+
raise QuarantineError(f'{path}: {exc}') from None
|
|
86
|
+
return parse(doc)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def load(repo: Path) -> list[Entry]:
|
|
90
|
+
return load_path(Path(repo) / LIST_PATH)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def pytest_ids(entries) -> list[str]:
|
|
94
|
+
return [e.id for e in entries if e.kind == 'pytest']
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def harness_cases(entries, harness: str) -> list[str]:
|
|
98
|
+
return [e.case for e in entries if e.kind == 'harness' and e.harness == harness]
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def harness_inventory(repo: Path, harness: str, *, timeout: float = 60) -> list[str]:
|
|
102
|
+
"""The case names `bin/cctally-<harness>-test --list-cases` prints.
|
|
103
|
+
|
|
104
|
+
QuarantineError when the harness does not exist or has no case inventory:
|
|
105
|
+
a harness case that cannot be validated against the harness is not
|
|
106
|
+
quarantinable."""
|
|
107
|
+
script = Path(repo) / 'bin' / f'cctally-{harness}-test'
|
|
108
|
+
if not script.is_file():
|
|
109
|
+
raise QuarantineError(f'harness:{harness}: bin/cctally-{harness}-test does not exist')
|
|
110
|
+
proc = subprocess.run([str(script), '--list-cases'], cwd=repo, capture_output=True,
|
|
111
|
+
text=True, timeout=timeout)
|
|
112
|
+
if proc.returncode != 0:
|
|
113
|
+
raise QuarantineError(f'harness:{harness}: --list-cases exited {proc.returncode}: '
|
|
114
|
+
+ (proc.stderr or proc.stdout).strip()[-400:])
|
|
115
|
+
return [line.strip() for line in proc.stdout.splitlines() if line.strip()]
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def validate_harness_cases(repo: Path, entries) -> None:
|
|
119
|
+
"""Each harness case names an existing harness and a case in its inventory."""
|
|
120
|
+
inventories: dict[str, list[str]] = {}
|
|
121
|
+
for entry in entries:
|
|
122
|
+
if entry.kind != 'harness':
|
|
123
|
+
continue
|
|
124
|
+
if entry.harness not in inventories:
|
|
125
|
+
inventories[entry.harness] = harness_inventory(repo, entry.harness)
|
|
126
|
+
if entry.case not in inventories[entry.harness]:
|
|
127
|
+
raise QuarantineError(f'{entry.id}: no case {entry.case!r} in the '
|
|
128
|
+
f'{entry.harness} harness inventory')
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def main(argv=None) -> int:
|
|
132
|
+
argv = list(sys.argv[1:] if argv is None else argv)
|
|
133
|
+
repo = Path(__file__).resolve().parents[1]
|
|
134
|
+
if len(argv) >= 2 and argv[0] == '--repo':
|
|
135
|
+
repo, argv = Path(argv[1]), argv[2:]
|
|
136
|
+
try:
|
|
137
|
+
if argv == ['pytest-ids']:
|
|
138
|
+
out = pytest_ids(load(repo))
|
|
139
|
+
elif len(argv) == 2 and argv[0] == 'harness-cases':
|
|
140
|
+
out = harness_cases(load(repo), argv[1])
|
|
141
|
+
else:
|
|
142
|
+
print('usage: _lib_quarantine.py [--repo DIR] pytest-ids | harness-cases <harness>',
|
|
143
|
+
file=sys.stderr)
|
|
144
|
+
return 2
|
|
145
|
+
except QuarantineError as exc:
|
|
146
|
+
print(f'quarantine: {exc}', file=sys.stderr)
|
|
147
|
+
return 2
|
|
148
|
+
for item in out:
|
|
149
|
+
print(item)
|
|
150
|
+
return 0
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
if __name__ == '__main__':
|
|
154
|
+
raise SystemExit(main())
|
|
@@ -62,6 +62,10 @@ class ApplyRejection(str, enum.Enum):
|
|
|
62
62
|
|
|
63
63
|
UNSUPPORTED_COMPOSITION = "unsupported-composition"
|
|
64
64
|
EMPTY_POPULATION = "no-local-history"
|
|
65
|
+
#: The requested interval, or an entry of the population, lies outside
|
|
66
|
+
#: the regime's own coefficient era (#978 spec 4.6). Units from another
|
|
67
|
+
#: era are never divided by this regime's units-per-point.
|
|
68
|
+
UNVALIDATED_COEFFICIENT_ERA = "unvalidated-coefficient-era"
|
|
65
69
|
|
|
66
70
|
|
|
67
71
|
@dataclasses.dataclass(frozen=True)
|
|
@@ -88,6 +92,9 @@ class ValidatedRegime:
|
|
|
88
92
|
class_shares: dict
|
|
89
93
|
family_radius: "float | None"
|
|
90
94
|
class_radius: "float | None"
|
|
95
|
+
#: The id of the coefficient era the regime was fitted in, always a
|
|
96
|
+
#: member of `qm.COEFFICIENT_ERAS` (#978).
|
|
97
|
+
coefficient_era: str
|
|
91
98
|
qualifications: tuple = ()
|
|
92
99
|
|
|
93
100
|
|
|
@@ -196,6 +203,13 @@ def validate_regime(raw, *, account_key, expected_fingerprint,
|
|
|
196
203
|
if isinstance(revision, bool) or not isinstance(revision, int) \
|
|
197
204
|
or revision != expected_revision:
|
|
198
205
|
return RegimeRejection.REVISION
|
|
206
|
+
# #978 spec 4.5: under the current constants a regime names the
|
|
207
|
+
# coefficient era it was fitted in. Missing or unknown is malformed, never
|
|
208
|
+
# a guess at an era; a regime from an older build has already been
|
|
209
|
+
# reported above as a fingerprint mismatch.
|
|
210
|
+
era = qm.era_by_id(raw.get("coefficientEra"))
|
|
211
|
+
if era is None:
|
|
212
|
+
return RegimeRejection.MALFORMED
|
|
199
213
|
|
|
200
214
|
point = raw.get("unitsPerPoint")
|
|
201
215
|
if not _finite(point) or point <= 0.0:
|
|
@@ -256,31 +270,42 @@ def validate_regime(raw, *, account_key, expected_fingerprint,
|
|
|
256
270
|
class_shares=class_shares,
|
|
257
271
|
family_radius=family_radius,
|
|
258
272
|
class_radius=class_radius,
|
|
273
|
+
coefficient_era=era.id,
|
|
259
274
|
qualifications=marks,
|
|
260
275
|
)
|
|
261
276
|
|
|
262
277
|
|
|
263
|
-
def population_units(entries) -> float:
|
|
278
|
+
def population_units(entries, *, era) -> float:
|
|
264
279
|
"""Summed weighted quota units over the general-meter entries.
|
|
265
280
|
|
|
266
281
|
The same filter `_cctally_quota_model.analyse_account` applies to the
|
|
267
282
|
current week: an entry whose family does not drain the general weekly
|
|
268
283
|
meter contributes nothing, and one whose cache-write split is unknown is
|
|
269
|
-
skipped rather than priced at zero.
|
|
284
|
+
skipped rather than priced at zero. Every entry is weighted with `era`,
|
|
285
|
+
the regime's own coefficient era (#978).
|
|
286
|
+
|
|
287
|
+
An entry `era` does not cover raises `CoefficientEraMismatch`, as the
|
|
288
|
+
kernel's population functions do (#978 spec 4.2, invariant 5): skipping
|
|
289
|
+
it would divide part of the population by the regime's budget and report
|
|
290
|
+
the result as the whole.
|
|
270
291
|
"""
|
|
271
292
|
total = 0.0
|
|
272
293
|
for entry in entries:
|
|
273
294
|
if qm.family_participation(qm.normalize_family(entry.model)) \
|
|
274
295
|
!= "general":
|
|
275
296
|
continue
|
|
276
|
-
|
|
277
|
-
if
|
|
297
|
+
weighed = qm.weigh_entry(entry, era)
|
|
298
|
+
if weighed.cause == qm.ERA_UNAVAILABLE:
|
|
299
|
+
raise qm.CoefficientEraMismatch(
|
|
300
|
+
f"entry.at {entry.at.isoformat()} lies outside coefficient "
|
|
301
|
+
f"era {era.id!r}")
|
|
302
|
+
if weighed.units is None:
|
|
278
303
|
continue
|
|
279
|
-
total += units
|
|
304
|
+
total += weighed.units
|
|
280
305
|
return total
|
|
281
306
|
|
|
282
307
|
|
|
283
|
-
def apply_regime(regime: ValidatedRegime, entries
|
|
308
|
+
def apply_regime(regime: ValidatedRegime, entries, *, interval
|
|
284
309
|
) -> "AppliedQuota | ApplyRejection":
|
|
285
310
|
"""Modelled consumption for `entries` under `regime`.
|
|
286
311
|
|
|
@@ -288,8 +313,28 @@ def apply_regime(regime: ValidatedRegime, entries
|
|
|
288
313
|
status, because that status describes S1's fit population and this one is
|
|
289
314
|
the consumer's. The arithmetic afterwards reproduces the kernel's, so a
|
|
290
315
|
consumer never re-derives the inversion by hand.
|
|
316
|
+
|
|
317
|
+
`interval` is the half-open `[start, end)` the caller models, which a
|
|
318
|
+
consumer extrapolating over a whole week must pass as that week (#978
|
|
319
|
+
spec 4.6). Two independent checks run before any aggregation: the
|
|
320
|
+
interval must lie inside the regime's coefficient era, and so must every
|
|
321
|
+
entry. The interval check is not implied by the entry check — a week
|
|
322
|
+
starting before the era whose retained entries all fall after its start
|
|
323
|
+
is in-era entry by entry, yet modelling the whole week is forbidden.
|
|
291
324
|
"""
|
|
292
|
-
|
|
325
|
+
era = qm.era_by_id(regime.coefficient_era)
|
|
326
|
+
if era is None:
|
|
327
|
+
return ApplyRejection.UNVALIDATED_COEFFICIENT_ERA
|
|
328
|
+
start, end = interval
|
|
329
|
+
qm.require_aware(start, "interval.start")
|
|
330
|
+
qm.require_aware(end, "interval.end")
|
|
331
|
+
era_start, era_end = qm.era_bounds(era)
|
|
332
|
+
if start < era_start or (era_end is not None and end > era_end):
|
|
333
|
+
return ApplyRejection.UNVALIDATED_COEFFICIENT_ERA
|
|
334
|
+
entries = list(entries)
|
|
335
|
+
if any(not qm.era_contains(era, entry.at) for entry in entries):
|
|
336
|
+
return ApplyRejection.UNVALIDATED_COEFFICIENT_ERA
|
|
337
|
+
aggregated = qm.aggregate_composition(entries, era=era)
|
|
293
338
|
if aggregated is None:
|
|
294
339
|
return ApplyRejection.EMPTY_POPULATION
|
|
295
340
|
family_shares, class_shares = aggregated
|
|
@@ -298,7 +343,7 @@ def apply_regime(regime: ValidatedRegime, entries
|
|
|
298
343
|
regime.class_shares, regime.class_radius):
|
|
299
344
|
return ApplyRejection.UNSUPPORTED_COMPOSITION
|
|
300
345
|
|
|
301
|
-
units = population_units(entries)
|
|
346
|
+
units = population_units(entries, era=era)
|
|
302
347
|
if not _finite(units) or units < 0.0:
|
|
303
348
|
return ApplyRejection.EMPTY_POPULATION
|
|
304
349
|
|
package/bin/_lib_quota_copy.py
CHANGED
|
@@ -70,7 +70,8 @@ _LONG_FORM: dict = {
|
|
|
70
70
|
"the models in use sit outside the mix the calibration was fitted "
|
|
71
71
|
"over",
|
|
72
72
|
"unvalidated-coefficient-era":
|
|
73
|
-
"this window
|
|
73
|
+
"this window is not inside a single coefficient era the token "
|
|
74
|
+
"weights are validated for",
|
|
74
75
|
"stale":
|
|
75
76
|
"the stored calibration was fitted under different constants, so it "
|
|
76
77
|
"no longer describes this build",
|