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
|
@@ -0,0 +1,386 @@
|
|
|
1
|
+
"""Pure account-bound protocol kernel (#825/#832, spec sections 5.1-5.4).
|
|
2
|
+
|
|
3
|
+
One durable journal and one stats index are fenced by a durable
|
|
4
|
+
``account_bound_cutover`` op and a per-record writer-generation stamp. This
|
|
5
|
+
module owns:
|
|
6
|
+
|
|
7
|
+
* the protocol constants (writer generation, op kinds, the cutover op id);
|
|
8
|
+
* the stamp the shared append preparation applies to every record the current
|
|
9
|
+
binary writes;
|
|
10
|
+
* the historical boundary the cutover op records, and its payload codec;
|
|
11
|
+
* the admission predicate every accounting reader applies BEFORE it interprets
|
|
12
|
+
a record (ingest, rebuild, rederive, journal-repair, correction selection,
|
|
13
|
+
cache recovery and the deep doctor scans).
|
|
14
|
+
|
|
15
|
+
"After the cutover" is a PHYSICAL journal position (segment and byte offset of
|
|
16
|
+
a complete line), never ``at``, capture time or provider time. A record is
|
|
17
|
+
historical when it lies inside the frozen complete-line extent the cutover op
|
|
18
|
+
recorded for its segment; a segment absent from that map (a later month, or a
|
|
19
|
+
bootstrap segment created after the cutover, which sorts FIRST in canonical
|
|
20
|
+
order) is post-boundary in its entirety.
|
|
21
|
+
|
|
22
|
+
The fence is Claude-scoped (operator Q5). Exclusion is the default: a
|
|
23
|
+
post-boundary record without the current writer generation is admitted only
|
|
24
|
+
when an explicit field its real producer writes proves it Codex-only. An
|
|
25
|
+
unknown or unclassifiable record is excluded, never admitted by omission.
|
|
26
|
+
|
|
27
|
+
Pure: no I/O, no clock, no SQLite.
|
|
28
|
+
"""
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
import dataclasses
|
|
32
|
+
import hashlib
|
|
33
|
+
import re
|
|
34
|
+
from typing import Mapping
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
PROTOCOL_GENERATION = "account-bound-v1"
|
|
38
|
+
WRITER_GENERATION_KEY = "wpg"
|
|
39
|
+
CUTOVER_OP_KIND = "account_bound_cutover"
|
|
40
|
+
CUTOVER_OP_ID = "account-bound-cutover-v1"
|
|
41
|
+
LEGACY_SPOOL_ACTIVITY_KIND = "legacy_spool_activity"
|
|
42
|
+
#: Spec revision 5 (section 7, operator decision on Codex design-fix-round-5
|
|
43
|
+
#: Q4): the `legacy_spool_activity` reason of an incomplete inventory. Such an
|
|
44
|
+
#: op records typed failure evidence and no digest, so it anchors no baseline.
|
|
45
|
+
INVENTORY_INCOMPLETE_REASON = "inventory-incomplete"
|
|
46
|
+
#: Fix round K12 (K11R-4; spec 5.5 step 6, 5.6 item 1(b) and section 7): the
|
|
47
|
+
#: reason of the anchoring op the first complete inventory appends after a
|
|
48
|
+
#: cutover that recorded inventory uncertainty and so anchored no baseline.
|
|
49
|
+
#: It records the complete digest and ends that episode. Like every
|
|
50
|
+
#: `legacy_spool_activity` op it is a trigger, but of the same uncertainty,
|
|
51
|
+
#: never an observed legacy change, and it clears nothing (residual 6).
|
|
52
|
+
INVENTORY_COMPLETE_REASON = "inventory-complete"
|
|
53
|
+
#: The reasons whose unresolved markers an incomplete inventory left.
|
|
54
|
+
INVENTORY_REASONS = (INVENTORY_INCOMPLETE_REASON, INVENTORY_COMPLETE_REASON)
|
|
55
|
+
#: The operations an inventory can fail at (the evidence's ``operation``).
|
|
56
|
+
INVENTORY_OPERATIONS = ("list", "stat", "read")
|
|
57
|
+
|
|
58
|
+
#: The versioned successors of the two administrative op kinds whose
|
|
59
|
+
#: previous-release validators require exact keys (spec 5.1). The previous
|
|
60
|
+
#: release ignores an unknown op kind, so the current binary emits only these.
|
|
61
|
+
PROTOCOL_RESOLUTION_V2_KIND = "journal_protocol_resolution_v2"
|
|
62
|
+
WEEKLY_OBSERVATION_DECISION_V2_KIND = "claude_weekly_observation_decision_v2"
|
|
63
|
+
|
|
64
|
+
#: Admission families. ``codex`` is the only family admitted without a stamp.
|
|
65
|
+
FAMILIES = (
|
|
66
|
+
"claude-observation",
|
|
67
|
+
"claude-op",
|
|
68
|
+
"claude-event",
|
|
69
|
+
"administrative",
|
|
70
|
+
"protocol",
|
|
71
|
+
"codex",
|
|
72
|
+
"unknown",
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
ADMISSION_REASONS = ("no-cutover", "historical", "stamped", "codex-only",
|
|
76
|
+
"excluded")
|
|
77
|
+
|
|
78
|
+
_DIGEST_RE = re.compile(r"^sha256:[0-9a-f]{64}$")
|
|
79
|
+
|
|
80
|
+
_CLAUDE_OP_KINDS = frozenset((
|
|
81
|
+
"account_observe", "account_label", "accounts_cutover",
|
|
82
|
+
"weekly_credit_floor", "sync_week",
|
|
83
|
+
))
|
|
84
|
+
_CODEX_OP_KINDS = frozenset((
|
|
85
|
+
"codex_file_account", "codex_window_attribution",
|
|
86
|
+
"codex_window_attribution_retract",
|
|
87
|
+
))
|
|
88
|
+
_ADMIN_OP_KINDS = frozenset((
|
|
89
|
+
"journal_protocol_resolution", "claude_weekly_observation_decision",
|
|
90
|
+
PROTOCOL_RESOLUTION_V2_KIND, WEEKLY_OBSERVATION_DECISION_V2_KIND,
|
|
91
|
+
))
|
|
92
|
+
_PROTOCOL_OP_KINDS = frozenset((CUTOVER_OP_KIND, LEGACY_SPOOL_ACTIVITY_KIND))
|
|
93
|
+
_ADMIN_RECORD_TYPES = frozenset(("correction", "correction_batch"))
|
|
94
|
+
_CLAUDE_EVT_KINDS = frozenset((
|
|
95
|
+
"snapshot_accept", "weekly_cost_snapshot", "week_reset", "five_hour_credit",
|
|
96
|
+
"five_hour_block_close", "percent_milestone", "five_hour_milestone",
|
|
97
|
+
"weekly_credit_effects", "projected", "project_budget", "meter_rate_change",
|
|
98
|
+
))
|
|
99
|
+
#: Evt kinds admitted without a stamp only when their payload proves a Codex
|
|
100
|
+
#: quota source (``payload.source == "codex"``, the source every Codex quota
|
|
101
|
+
#: identity carries and every Codex quota table filters on).
|
|
102
|
+
_QUOTA_EVT_KINDS = frozenset(("quota_alert_arming", "quota_threshold_event"))
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@dataclasses.dataclass(frozen=True)
|
|
106
|
+
class Coordinate:
|
|
107
|
+
"""Physical journal position of a complete line: its segment name and the
|
|
108
|
+
byte offset at which the line STARTS."""
|
|
109
|
+
|
|
110
|
+
segment: str
|
|
111
|
+
offset: int
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@dataclasses.dataclass(frozen=True)
|
|
115
|
+
class CutoverBoundary:
|
|
116
|
+
"""The historical boundary recorded by the ``account_bound_cutover`` op.
|
|
117
|
+
|
|
118
|
+
``segment``/``end_offset`` name where the op itself was appended: the
|
|
119
|
+
target segment and its complete-line end at that moment, which is the op's
|
|
120
|
+
own start offset. ``frozen_extents`` maps every
|
|
121
|
+
segment that existed before the op to its complete-line byte length at that
|
|
122
|
+
moment; only bytes inside those extents are historical.
|
|
123
|
+
|
|
124
|
+
Spec revision 5: when the establishment could not complete its legacy
|
|
125
|
+
spool inventory, the op records the uncertainty itself —
|
|
126
|
+
``legacy_spool_inventory_failure`` holds the typed failure evidence and
|
|
127
|
+
``legacy_spool_baseline_digest`` is None, so the op anchors no baseline
|
|
128
|
+
(the next complete inventory installs one under its own anchoring op).
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
segment: str
|
|
132
|
+
end_offset: int
|
|
133
|
+
frozen_extents: Mapping[str, int]
|
|
134
|
+
legacy_spool_baseline_digest: "str | None"
|
|
135
|
+
legacy_spool_inventory_failure: "Mapping[str, object] | None" = None
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@dataclasses.dataclass(frozen=True)
|
|
139
|
+
class Admission:
|
|
140
|
+
admitted: bool
|
|
141
|
+
family: str
|
|
142
|
+
reason: str
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
# --------------------------------------------------------------------------
|
|
146
|
+
# writer-generation stamp
|
|
147
|
+
# --------------------------------------------------------------------------
|
|
148
|
+
|
|
149
|
+
def stamp(record: dict) -> dict:
|
|
150
|
+
"""Return a shallow copy of ``record`` carrying the current writer
|
|
151
|
+
generation. Idempotent; refuses a record that already names a DIFFERENT
|
|
152
|
+
generation (a caller must never relabel another protocol's record). The
|
|
153
|
+
payload is never touched: the stamp is transport metadata."""
|
|
154
|
+
if not isinstance(record, dict):
|
|
155
|
+
raise ValueError("journal record must be a dict")
|
|
156
|
+
existing = record.get(WRITER_GENERATION_KEY)
|
|
157
|
+
if existing is not None and existing != PROTOCOL_GENERATION:
|
|
158
|
+
raise ValueError(
|
|
159
|
+
f"record already carries writer generation {existing!r}")
|
|
160
|
+
out = dict(record)
|
|
161
|
+
out[WRITER_GENERATION_KEY] = PROTOCOL_GENERATION
|
|
162
|
+
return out
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def is_stamped(record: dict) -> bool:
|
|
166
|
+
return (isinstance(record, dict)
|
|
167
|
+
and record.get(WRITER_GENERATION_KEY) == PROTOCOL_GENERATION)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def strip_stamp(record: dict) -> dict:
|
|
171
|
+
"""``record`` without its writer-generation field — the comparison shape
|
|
172
|
+
for semantic event duplicate/conflict classification."""
|
|
173
|
+
if not isinstance(record, dict) or WRITER_GENERATION_KEY not in record:
|
|
174
|
+
return record
|
|
175
|
+
return {k: v for k, v in record.items() if k != WRITER_GENERATION_KEY}
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
# --------------------------------------------------------------------------
|
|
179
|
+
# boundary
|
|
180
|
+
# --------------------------------------------------------------------------
|
|
181
|
+
|
|
182
|
+
def is_historical(coord: Coordinate, boundary: CutoverBoundary) -> bool:
|
|
183
|
+
extent = boundary.frozen_extents.get(coord.segment)
|
|
184
|
+
return extent is not None and coord.offset < extent
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def inventory_failure_evidence(*, path: str, operation: str,
|
|
188
|
+
error: str, message: str = "") -> dict:
|
|
189
|
+
"""The typed evidence of a failed legacy spool inventory (spec revision
|
|
190
|
+
5): the path, the failed operation and the error (its errno name). It is
|
|
191
|
+
diagnostic evidence only, never accounting input."""
|
|
192
|
+
evidence = {"path": str(path), "operation": str(operation),
|
|
193
|
+
"error": str(error)}
|
|
194
|
+
if message:
|
|
195
|
+
evidence["message"] = str(message)
|
|
196
|
+
if not is_inventory_failure(evidence):
|
|
197
|
+
raise ValueError(f"invalid inventory failure evidence {evidence!r}")
|
|
198
|
+
return evidence
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def is_inventory_failure(value) -> bool:
|
|
202
|
+
"""Whether ``value`` is well-formed inventory failure evidence."""
|
|
203
|
+
if not isinstance(value, dict):
|
|
204
|
+
return False
|
|
205
|
+
if not (isinstance(value.get("path"), str)
|
|
206
|
+
and value.get("operation") in INVENTORY_OPERATIONS
|
|
207
|
+
and isinstance(value.get("error"), str) and value["error"]):
|
|
208
|
+
return False
|
|
209
|
+
message = value.get("message")
|
|
210
|
+
return message is None or isinstance(message, str)
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def boundary_payload(boundary: CutoverBoundary) -> dict:
|
|
214
|
+
"""The camelCase payload fields of the cutover op (without ``kind``).
|
|
215
|
+
``legacySpoolInventoryFailure`` is present only on a cutover that
|
|
216
|
+
recorded inventory uncertainty, so a complete cutover's bytes are
|
|
217
|
+
unchanged."""
|
|
218
|
+
payload = {
|
|
219
|
+
"segment": boundary.segment,
|
|
220
|
+
"endOffset": boundary.end_offset,
|
|
221
|
+
"frozenExtents": {k: boundary.frozen_extents[k]
|
|
222
|
+
for k in sorted(boundary.frozen_extents)},
|
|
223
|
+
"legacySpoolBaselineDigest": boundary.legacy_spool_baseline_digest,
|
|
224
|
+
}
|
|
225
|
+
if boundary.legacy_spool_inventory_failure is not None:
|
|
226
|
+
payload["legacySpoolInventoryFailure"] = dict(
|
|
227
|
+
boundary.legacy_spool_inventory_failure)
|
|
228
|
+
return payload
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
def _nonneg_int(value) -> bool:
|
|
232
|
+
return type(value) is int and value >= 0
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def boundary_from_op(op_record: dict) -> CutoverBoundary:
|
|
236
|
+
"""Decode the boundary from a cutover op. Raises ``ValueError`` on any
|
|
237
|
+
shape failure: a malformed cutover op never yields a boundary."""
|
|
238
|
+
if not isinstance(op_record, dict) or op_record.get("t") != "op":
|
|
239
|
+
raise ValueError("cutover boundary must come from an op record")
|
|
240
|
+
payload = op_record.get("payload")
|
|
241
|
+
if not isinstance(payload, dict) or payload.get("kind") != CUTOVER_OP_KIND:
|
|
242
|
+
raise ValueError("record is not an account_bound_cutover op")
|
|
243
|
+
segment = payload.get("segment")
|
|
244
|
+
end_offset = payload.get("endOffset")
|
|
245
|
+
extents = payload.get("frozenExtents")
|
|
246
|
+
digest = payload.get("legacySpoolBaselineDigest")
|
|
247
|
+
if not isinstance(segment, str) or not segment:
|
|
248
|
+
raise ValueError("cutover boundary segment is invalid")
|
|
249
|
+
if not _nonneg_int(end_offset):
|
|
250
|
+
raise ValueError("cutover boundary endOffset is invalid")
|
|
251
|
+
if not isinstance(extents, dict) or not all(
|
|
252
|
+
isinstance(k, str) and k and _nonneg_int(v)
|
|
253
|
+
for k, v in extents.items()):
|
|
254
|
+
raise ValueError("cutover boundary frozenExtents is invalid")
|
|
255
|
+
failure = payload.get("legacySpoolInventoryFailure")
|
|
256
|
+
if failure is None:
|
|
257
|
+
if not isinstance(digest, str) or not _DIGEST_RE.match(digest):
|
|
258
|
+
raise ValueError("cutover boundary legacy spool digest is invalid")
|
|
259
|
+
else:
|
|
260
|
+
# Revision 5: an uncertain inventory records its evidence and NO
|
|
261
|
+
# digest; a digest beside failure evidence is malformed.
|
|
262
|
+
if not is_inventory_failure(failure) or digest is not None:
|
|
263
|
+
raise ValueError(
|
|
264
|
+
"cutover boundary legacy spool inventory failure is invalid")
|
|
265
|
+
failure = dict(failure)
|
|
266
|
+
return CutoverBoundary(
|
|
267
|
+
segment=segment,
|
|
268
|
+
end_offset=end_offset,
|
|
269
|
+
frozen_extents=dict(extents),
|
|
270
|
+
legacy_spool_baseline_digest=digest,
|
|
271
|
+
legacy_spool_inventory_failure=failure,
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
# --------------------------------------------------------------------------
|
|
276
|
+
# admission
|
|
277
|
+
# --------------------------------------------------------------------------
|
|
278
|
+
|
|
279
|
+
def _family(record: dict) -> tuple[str, bool]:
|
|
280
|
+
"""``(family, codex_only)`` from an explicit allow-list per kind. Anything
|
|
281
|
+
not named here is ``("unknown", False)``."""
|
|
282
|
+
if not isinstance(record, dict):
|
|
283
|
+
return ("unknown", False)
|
|
284
|
+
t = record.get("t")
|
|
285
|
+
if t in _ADMIN_RECORD_TYPES:
|
|
286
|
+
return ("administrative", False)
|
|
287
|
+
payload = record.get("payload")
|
|
288
|
+
if not isinstance(payload, dict):
|
|
289
|
+
return ("unknown", False)
|
|
290
|
+
kind = payload.get("kind")
|
|
291
|
+
if t == "obs":
|
|
292
|
+
provider = record.get("provider")
|
|
293
|
+
if provider == "claude":
|
|
294
|
+
return ("claude-observation", False)
|
|
295
|
+
if provider == "codex" and kind == "quota_window_snapshot":
|
|
296
|
+
return ("codex", True)
|
|
297
|
+
return ("unknown", False)
|
|
298
|
+
if t == "op":
|
|
299
|
+
if kind in _CODEX_OP_KINDS:
|
|
300
|
+
return ("codex", True)
|
|
301
|
+
if kind in ("account_observe", "account_label"):
|
|
302
|
+
if payload.get("provider") == "codex":
|
|
303
|
+
return ("codex", True)
|
|
304
|
+
return ("claude-op", False)
|
|
305
|
+
if kind in _CLAUDE_OP_KINDS:
|
|
306
|
+
return ("claude-op", False)
|
|
307
|
+
if kind in _ADMIN_OP_KINDS:
|
|
308
|
+
return ("administrative", False)
|
|
309
|
+
if kind in _PROTOCOL_OP_KINDS:
|
|
310
|
+
return ("protocol", False)
|
|
311
|
+
return ("unknown", False)
|
|
312
|
+
if t == "evt":
|
|
313
|
+
if kind in _CLAUDE_EVT_KINDS:
|
|
314
|
+
return ("claude-event", False)
|
|
315
|
+
if kind == "budget":
|
|
316
|
+
if payload.get("vendor") == "codex":
|
|
317
|
+
return ("codex", True)
|
|
318
|
+
return ("claude-event", False)
|
|
319
|
+
if kind in _QUOTA_EVT_KINDS:
|
|
320
|
+
if payload.get("source") == "codex":
|
|
321
|
+
return ("codex", True)
|
|
322
|
+
return ("claude-event", False)
|
|
323
|
+
return ("unknown", False)
|
|
324
|
+
return ("unknown", False)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def record_family(record: dict) -> str:
|
|
328
|
+
return _family(record)[0]
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def classify_admission(record: dict, coord: Coordinate,
|
|
332
|
+
boundary: CutoverBoundary | None) -> Admission:
|
|
333
|
+
"""The single admission predicate (spec 5.3).
|
|
334
|
+
|
|
335
|
+
Admitted when there is no cutover yet, when the record lies inside the
|
|
336
|
+
historical boundary, when it carries the current writer generation, or when
|
|
337
|
+
its family is proven Codex-only. Every other post-boundary record is
|
|
338
|
+
excluded."""
|
|
339
|
+
family, codex_only = _family(record)
|
|
340
|
+
if boundary is None:
|
|
341
|
+
return Admission(True, family, "no-cutover")
|
|
342
|
+
if is_historical(coord, boundary):
|
|
343
|
+
return Admission(True, family, "historical")
|
|
344
|
+
if is_stamped(record):
|
|
345
|
+
return Admission(True, family, "stamped")
|
|
346
|
+
if codex_only:
|
|
347
|
+
return Admission(True, family, "codex-only")
|
|
348
|
+
return Admission(False, family, "excluded")
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
# --------------------------------------------------------------------------
|
|
352
|
+
# excluded-population digest (spec section 8)
|
|
353
|
+
# --------------------------------------------------------------------------
|
|
354
|
+
|
|
355
|
+
_DIGEST_MODULUS = 1 << 256
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def _coordinate_term(coord: Coordinate) -> int:
|
|
359
|
+
data = f"{coord.segment}\x00{coord.offset}".encode("utf-8")
|
|
360
|
+
return int.from_bytes(hashlib.sha256(data).digest(), "big")
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def coordinate_population_digest(coords, base: str | None = None) -> str:
|
|
364
|
+
"""An ORDER-INDEPENDENT digest over a set of physical coordinates: the sum
|
|
365
|
+
of each coordinate's sha256 modulo 2**256. ``base`` extends an earlier
|
|
366
|
+
digest incrementally, so an ingest cycle can add the coordinates it newly
|
|
367
|
+
excludes and a full scan reproduces the same value in any order."""
|
|
368
|
+
total = 0
|
|
369
|
+
if base is not None:
|
|
370
|
+
if not isinstance(base, str) or not _DIGEST_RE.match(base):
|
|
371
|
+
raise ValueError("coordinate population digest is invalid")
|
|
372
|
+
total = int(base[len("sha256:"):], 16)
|
|
373
|
+
for coord in coords:
|
|
374
|
+
total = (total + _coordinate_term(coord)) % _DIGEST_MODULUS
|
|
375
|
+
return "sha256:" + format(total, "064x")
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
#: The record type of an inert positional placeholder for consumers that need
|
|
379
|
+
#: a dict in an excluded record's slot. No reader recognises the type, so the
|
|
380
|
+
#: selector, the pipeline and the legacy classifier all skip it while it still
|
|
381
|
+
#: consumes the slot's sequence number.
|
|
382
|
+
EXCLUDED_PLACEHOLDER_TYPE = "excluded"
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
def excluded_placeholder() -> dict:
|
|
386
|
+
return {"t": EXCLUDED_PLACEHOLDER_TYPE}
|
package/bin/_lib_aggregators.py
CHANGED
|
@@ -46,6 +46,8 @@ import threading
|
|
|
46
46
|
from dataclasses import dataclass
|
|
47
47
|
from typing import Any, Callable, Iterable
|
|
48
48
|
|
|
49
|
+
import _lib_codex_rollout_container as _rc
|
|
50
|
+
|
|
49
51
|
|
|
50
52
|
def _cctally():
|
|
51
53
|
"""Resolve the current `cctally` module at call-time (spec §5.5)."""
|
|
@@ -687,6 +689,7 @@ def _session_path_parts(source_path: str) -> tuple[str, str, str]:
|
|
|
687
689
|
keyed on ``(scope roots, source_path)``. Outside one the roots are
|
|
688
690
|
resolved per call, which is the historic behaviour every CLI caller keeps.
|
|
689
691
|
"""
|
|
692
|
+
source_path = _rc.logical_path(source_path)
|
|
690
693
|
scope = getattr(_CODEX_PATH_SCOPE, "scope", None)
|
|
691
694
|
if scope is not None:
|
|
692
695
|
return scope.parts(source_path)
|
|
@@ -713,6 +716,7 @@ def _session_path_parts_for_roots(
|
|
|
713
716
|
files stay free of maintainer absolute paths), then basename. Direct-JSONL
|
|
714
717
|
roots yield an id relative to <entry> itself (no sessions/ prefix).
|
|
715
718
|
"""
|
|
719
|
+
source_path = _rc.logical_path(source_path)
|
|
716
720
|
p = pathlib.Path(source_path)
|
|
717
721
|
rel: pathlib.PurePath | None = None
|
|
718
722
|
for root in roots:
|
|
@@ -69,7 +69,19 @@ PROGRESS_KEY = "codex_journal_coverage_progress"
|
|
|
69
69
|
#: `REASON_MALFORMED` and falls back to a full replay — the same outcome a bump
|
|
70
70
|
#: would produce. A bump would additionally invalidate certificates written by
|
|
71
71
|
#: an in-development binary that already carries the field, for no gain.
|
|
72
|
-
|
|
72
|
+
#:
|
|
73
|
+
#: Version 2 (#946; #825/#832 spec 4.7 revision 2, Codex design-fix-round-3
|
|
74
|
+
#: Q5): every earlier writer took `appliedThrough` from an ingest cursor that
|
|
75
|
+
#: could advance past a torn suffix. `_repair_torn_tail` then wrote a record
|
|
76
|
+
#: across that coordinate, the next ingest started inside it and skipped it,
|
|
77
|
+
#: and a contiguous advance certified it covered — a version-1 certificate can
|
|
78
|
+
#: therefore claim coverage of an unapplied record at an endpoint that follows
|
|
79
|
+
#: a newline, which no endpoint check can see. `prior_is_extendable` and
|
|
80
|
+
#: `certificate_is_valid` both reject version 1, so the first current binary
|
|
81
|
+
#: forces the full journal recovery and never extends or restamps one. A
|
|
82
|
+
#: previous release reading a version-2 certificate rejects it the same way
|
|
83
|
+
#: and replays (C2).
|
|
84
|
+
COVERAGE_VERSION = 2
|
|
73
85
|
|
|
74
86
|
#: Bumped whenever the journal-record-to-cache-row materialization changes —
|
|
75
87
|
#: `_apply_quota_records`, `_apply_file_account_records`, or the §3.5 precedence
|
|
@@ -12,18 +12,28 @@ import fcntl
|
|
|
12
12
|
import os
|
|
13
13
|
import time
|
|
14
14
|
from pathlib import Path
|
|
15
|
+
from typing import Callable
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
def acquire_ordered_flocks(
|
|
18
19
|
locks: list[tuple[Path, int]],
|
|
19
20
|
*,
|
|
20
21
|
timeout: float | None = None,
|
|
22
|
+
on_failure: Callable[[Path, BaseException], None] | None = None,
|
|
21
23
|
) -> list[int] | None:
|
|
22
|
-
"""Acquire ``(path, LOCK_SH|LOCK_EX)`` entries in caller-supplied order.
|
|
24
|
+
"""Acquire ``(path, LOCK_SH|LOCK_EX)`` entries in caller-supplied order.
|
|
25
|
+
|
|
26
|
+
``on_failure(path, exc)``, when given, is told which lock the set lost to
|
|
27
|
+
before this returns None (``exc`` a ``BlockingIOError``: the lock was
|
|
28
|
+
held) or re-raises an ``OSError`` (an I/O error no holder caused), so a
|
|
29
|
+
caller can wait for exactly that lock's release (#825/#832).
|
|
30
|
+
"""
|
|
23
31
|
deadline = None if timeout is None else time.monotonic() + max(timeout, 0.0)
|
|
24
32
|
held: list[int] = []
|
|
33
|
+
current: Path | None = None
|
|
25
34
|
try:
|
|
26
35
|
for path, mode in locks:
|
|
36
|
+
current = path
|
|
27
37
|
fd = os.open(str(path), os.O_RDWR | os.O_CREAT, 0o600)
|
|
28
38
|
os.fchmod(fd, 0o600)
|
|
29
39
|
held.append(fd)
|
|
@@ -31,14 +41,18 @@ def acquire_ordered_flocks(
|
|
|
31
41
|
try:
|
|
32
42
|
fcntl.flock(fd, mode | fcntl.LOCK_NB)
|
|
33
43
|
break
|
|
34
|
-
except BlockingIOError:
|
|
44
|
+
except BlockingIOError as exc:
|
|
35
45
|
if deadline is None or time.monotonic() >= deadline:
|
|
36
46
|
release_cache_writer_flocks(held)
|
|
47
|
+
if on_failure is not None:
|
|
48
|
+
on_failure(path, exc)
|
|
37
49
|
return None
|
|
38
50
|
time.sleep(min(0.2, max(0.0, deadline - time.monotonic())))
|
|
39
51
|
return held
|
|
40
|
-
except BaseException:
|
|
52
|
+
except BaseException as exc:
|
|
41
53
|
release_cache_writer_flocks(held)
|
|
54
|
+
if on_failure is not None and current is not None and isinstance(exc, OSError):
|
|
55
|
+
on_failure(current, exc)
|
|
42
56
|
raise
|
|
43
57
|
|
|
44
58
|
|
|
@@ -47,6 +61,7 @@ def acquire_cache_writer_flocks(
|
|
|
47
61
|
provider_path: Path | None = None,
|
|
48
62
|
*,
|
|
49
63
|
timeout: float | None = None,
|
|
64
|
+
on_failure: Callable[[Path, BaseException], None] | None = None,
|
|
50
65
|
) -> list[int] | None:
|
|
51
66
|
"""Acquire global then optional provider flock, or return ``None``.
|
|
52
67
|
|
|
@@ -61,6 +76,7 @@ def acquire_cache_writer_flocks(
|
|
|
61
76
|
return acquire_ordered_flocks(
|
|
62
77
|
[(path, fcntl.LOCK_EX) for path in paths],
|
|
63
78
|
timeout=timeout,
|
|
79
|
+
on_failure=on_failure,
|
|
64
80
|
)
|
|
65
81
|
|
|
66
82
|
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"""Clone-first file and tree copies (stdlib only; #940 D2, spec §6.7).
|
|
2
|
+
|
|
3
|
+
On APFS, ``fclonefileat(2)`` gives a copy that shares the source's blocks
|
|
4
|
+
until either side is written, so a copy of a large test scaffold writes no
|
|
5
|
+
data. This module takes that clone wherever it can and a real copy wherever
|
|
6
|
+
it cannot, and otherwise behaves exactly as the stdlib copier it stands in
|
|
7
|
+
for:
|
|
8
|
+
|
|
9
|
+
- :func:`copy2` is ``shutil.copy2`` for the same arguments: a directory
|
|
10
|
+
destination keeps the source's basename, ``follow_symlinks=False`` copies a
|
|
11
|
+
link as a link, the same file raises ``shutil.SameFileError``, and an
|
|
12
|
+
existing destination is overwritten in place (same inode), which a clone
|
|
13
|
+
cannot do, so that case is a real copy. Metadata is ``shutil.copystat``'s,
|
|
14
|
+
applied after the clone as after a copy.
|
|
15
|
+
- :func:`copytree` is ``shutil.copytree`` with :func:`copy2` as its file
|
|
16
|
+
copier, so symlink policy, ignore lists, dangling-link errors and
|
|
17
|
+
destination collisions are the stdlib's own code.
|
|
18
|
+
|
|
19
|
+
A clone is attempted only on Darwin. An unsupported filesystem, a
|
|
20
|
+
cross-volume destination, a missing ``fclonefileat`` and any non-Darwin
|
|
21
|
+
platform take the real copy; any other error (EIO, ENOSPC, EACCES, …)
|
|
22
|
+
propagates and is never retried as a copy. A clone is never a hardlink: the
|
|
23
|
+
copy is a new inode with a link count of one.
|
|
24
|
+
|
|
25
|
+
The optional ``counts`` mapping is incremented under ``"clone"`` or
|
|
26
|
+
``"copy"`` per file, so a caller or a measurement can see which path ran.
|
|
27
|
+
This module is public and imports nothing outside the standard library.
|
|
28
|
+
|
|
29
|
+
It binds ``fclonefileat`` itself rather than importing the binding in
|
|
30
|
+
``bin/_lib_remote_staging.py`` (#940 D-2 F17): that module is private, so it
|
|
31
|
+
is absent from the public tree that this module and its public callers ship
|
|
32
|
+
in, and a public module may not import it. The staging module may import
|
|
33
|
+
from this one instead (spec §6.7).
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
from __future__ import annotations
|
|
37
|
+
|
|
38
|
+
import ctypes
|
|
39
|
+
import errno
|
|
40
|
+
import os
|
|
41
|
+
import shutil
|
|
42
|
+
import stat
|
|
43
|
+
import sys
|
|
44
|
+
|
|
45
|
+
# Errors that mean "this filesystem or this pair of volumes cannot clone",
|
|
46
|
+
# never "the data could not be written". EEXIST is a destination created
|
|
47
|
+
# after the existence check: `shutil.copy2` overwrites it, and so do we.
|
|
48
|
+
_FALLBACK_ERRNOS = frozenset(
|
|
49
|
+
code for code in (
|
|
50
|
+
getattr(errno, "ENOTSUP", None), getattr(errno, "EOPNOTSUPP", None),
|
|
51
|
+
errno.ENOSYS, errno.EXDEV, errno.EINVAL, errno.EPERM, errno.EEXIST)
|
|
52
|
+
if code is not None)
|
|
53
|
+
|
|
54
|
+
_AT_FDCWD = -2 # Darwin's AT_FDCWD
|
|
55
|
+
_CLONE_NOOWNERCOPY = 0x0002 # leave ownership to the caller, as a copy does
|
|
56
|
+
|
|
57
|
+
_LIBC_CLONE = [] # the resolved fclonefileat, once looked up
|
|
58
|
+
# The platform this module decides on; a test sets it here instead of
|
|
59
|
+
# patching the interpreter-wide ``sys.platform`` (#940 D-2 F16).
|
|
60
|
+
_PLATFORM = sys.platform
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _fclonefileat():
|
|
64
|
+
"""The C ``fclonefileat`` on Darwin, else None."""
|
|
65
|
+
if _PLATFORM != "darwin":
|
|
66
|
+
return None
|
|
67
|
+
if not _LIBC_CLONE:
|
|
68
|
+
libc = ctypes.CDLL(None, use_errno=True)
|
|
69
|
+
clone = getattr(libc, "fclonefileat", None)
|
|
70
|
+
if clone is not None:
|
|
71
|
+
clone.argtypes = (ctypes.c_int, ctypes.c_int, ctypes.c_char_p, ctypes.c_uint32)
|
|
72
|
+
clone.restype = ctypes.c_int
|
|
73
|
+
_LIBC_CLONE.append(clone)
|
|
74
|
+
return _LIBC_CLONE[0]
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _clone_fd(source_fd: int, destination: str) -> None:
|
|
78
|
+
"""Clone the open regular file `source_fd` to the new path `destination`."""
|
|
79
|
+
clone = _fclonefileat()
|
|
80
|
+
if clone is None:
|
|
81
|
+
raise OSError(errno.ENOTSUP, "fclonefileat is unavailable", destination)
|
|
82
|
+
if clone(source_fd, _AT_FDCWD, os.fsencode(os.path.abspath(destination)),
|
|
83
|
+
_CLONE_NOOWNERCOPY) != 0:
|
|
84
|
+
code = ctypes.get_errno()
|
|
85
|
+
raise OSError(code, os.strerror(code), destination)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _count(counts, method: str) -> None:
|
|
89
|
+
if counts is not None:
|
|
90
|
+
counts[method] = counts.get(method, 0) + 1
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def copy2(src, dst, *, follow_symlinks: bool = True, counts=None):
|
|
94
|
+
"""``shutil.copy2(src, dst)``, by clone where the filesystem allows it.
|
|
95
|
+
|
|
96
|
+
Returns the destination path, as ``shutil.copy2`` does.
|
|
97
|
+
"""
|
|
98
|
+
if os.path.isdir(dst):
|
|
99
|
+
dst = os.path.join(dst, os.path.basename(src))
|
|
100
|
+
if (not follow_symlinks and os.path.islink(src)) or os.path.lexists(dst):
|
|
101
|
+
# A link copied as a link, or a destination to overwrite in place.
|
|
102
|
+
result = shutil.copy2(src, dst, follow_symlinks=follow_symlinks)
|
|
103
|
+
_count(counts, "copy")
|
|
104
|
+
return result
|
|
105
|
+
try:
|
|
106
|
+
fd = os.open(src, os.O_RDONLY | getattr(os, "O_CLOEXEC", 0))
|
|
107
|
+
except OSError:
|
|
108
|
+
# The stdlib's own error for this source (missing, unreadable, …).
|
|
109
|
+
return shutil.copy2(src, dst, follow_symlinks=follow_symlinks)
|
|
110
|
+
try:
|
|
111
|
+
if not stat.S_ISREG(os.fstat(fd).st_mode):
|
|
112
|
+
# A FIFO, a device or a directory: the stdlib decides.
|
|
113
|
+
os.close(fd)
|
|
114
|
+
fd = -1
|
|
115
|
+
return shutil.copy2(src, dst, follow_symlinks=follow_symlinks)
|
|
116
|
+
try:
|
|
117
|
+
_clone_fd(fd, dst)
|
|
118
|
+
except OSError as exc:
|
|
119
|
+
if exc.errno not in _FALLBACK_ERRNOS:
|
|
120
|
+
raise
|
|
121
|
+
result = shutil.copy2(src, dst, follow_symlinks=follow_symlinks)
|
|
122
|
+
_count(counts, "copy")
|
|
123
|
+
return result
|
|
124
|
+
shutil.copystat(src, dst, follow_symlinks=follow_symlinks)
|
|
125
|
+
_count(counts, "clone")
|
|
126
|
+
return dst
|
|
127
|
+
finally:
|
|
128
|
+
if fd >= 0:
|
|
129
|
+
os.close(fd)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def copytree(src, dst, symlinks: bool = False, ignore=None,
|
|
133
|
+
ignore_dangling_symlinks: bool = False, dirs_exist_ok: bool = False,
|
|
134
|
+
*, counts=None):
|
|
135
|
+
"""``shutil.copytree`` with :func:`copy2` as its file copier."""
|
|
136
|
+
def copier(source, destination):
|
|
137
|
+
return copy2(source, destination, counts=counts)
|
|
138
|
+
|
|
139
|
+
return shutil.copytree(src, dst, symlinks=symlinks, ignore=ignore,
|
|
140
|
+
copy_function=copier,
|
|
141
|
+
ignore_dangling_symlinks=ignore_dangling_symlinks,
|
|
142
|
+
dirs_exist_ok=dirs_exist_ok)
|