cctally 1.101.0 → 1.103.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.
@@ -764,6 +764,18 @@ def _run_conversation_events_stream(
764
764
  pass
765
765
  seen = cached_sigs(files)
766
766
 
767
+ # #630 S2: `ready` above means "this connection is live-tailing"; it fires
768
+ # BEFORE resolve() and ingest(), so it does not mean a baseline exists,
769
+ # which is why six tests used to sleep a second here. This frame means the
770
+ # baseline IS established, so any subsequent growth will be caught. The
771
+ # client listens only for `ready`, `tail` and `error`
772
+ # (dashboard/web/src/hooks/useConversationLiveTail.ts), and EventSource
773
+ # dispatches only event types something listens for, so this is inert for
774
+ # the browser by construction. It exists for tests; do not delete it as
775
+ # unread. Contract: docs/dashboard-gotchas.md.
776
+ handler.wfile.write(b"event: baselined\ndata: {}\n\n")
777
+ handler.wfile.flush()
778
+
767
779
  idle = 0.0
768
780
  cycles = 0
769
781
  while True:
@@ -995,6 +995,30 @@ def _sync_failure_envelope(
995
995
  "action": None,
996
996
  }
997
997
 
998
+ text = error.casefold()
999
+ # Maintenance ownership supplies a more specific recovery action than a
1000
+ # secondary busy attribution from another leg in the same failed tick.
1001
+ # Classify those markers before the typed busy fallback so a blocked stale
1002
+ # owner is never told merely to checkpoint the WAL.
1003
+ if (
1004
+ "stale maintenance marker" in text
1005
+ or "stale repair marker" in text
1006
+ or "recovery could not claim maintenance" in text
1007
+ ):
1008
+ return {
1009
+ "kind": "maintenance_stale",
1010
+ "label": "⚠ cache repair blocked",
1011
+ "detail": "A stale cache repair owner is blocking server sync.",
1012
+ "action": "cctally cache-sync --rebuild",
1013
+ }
1014
+ if "maintenance is in progress" in text:
1015
+ return {
1016
+ "kind": "maintenance_active",
1017
+ "label": "cache repair in progress",
1018
+ "detail": "Another cctally process is repairing the server cache.",
1019
+ "action": None,
1020
+ }
1021
+
998
1022
  # #583 S2 §7. A locked cache.db is named rather than left in the generic
999
1023
  # bucket. Typed attribution only — a blanket uncorrupted-cache branch
1000
1024
  # would be too broad, because ANY exception raised while reading cache.db
@@ -1007,9 +1031,7 @@ def _sync_failure_envelope(
1007
1031
  # without it a tick carrying a corruption-shaped cache attribution AND a
1008
1032
  # separate busy one rendered "cache database busy" and dropped the
1009
1033
  # corruption message — the more urgent of the two, whose remedy is not
1010
- # interchangeable with a checkpoint. The gate names the TYPED corruption
1011
- # attribution only: a busy attribution still outranks the legacy raw-text
1012
- # corruption legs below, which is what Preserve 10 asks for.
1034
+ # interchangeable with a checkpoint.
1013
1035
  if attributed("cache", sqlite_busy=True) and not attributed(
1014
1036
  "cache", corruption=True
1015
1037
  ):
@@ -1023,26 +1045,6 @@ def _sync_failure_envelope(
1023
1045
  "action": "cctally db checkpoint",
1024
1046
  }
1025
1047
 
1026
- text = error.casefold()
1027
- if (
1028
- "stale maintenance marker" in text
1029
- or "stale repair marker" in text
1030
- or "recovery could not claim maintenance" in text
1031
- ):
1032
- return {
1033
- "kind": "maintenance_stale",
1034
- "label": "⚠ cache repair blocked",
1035
- "detail": "A stale cache repair owner is blocking server sync.",
1036
- "action": "cctally cache-sync --rebuild",
1037
- }
1038
- if "maintenance is in progress" in text:
1039
- return {
1040
- "kind": "maintenance_active",
1041
- "label": "cache repair in progress",
1042
- "detail": "Another cctally process is repairing the server cache.",
1043
- "action": None,
1044
- }
1045
-
1046
1048
  c = sys.modules["cctally"]
1047
1049
  if (
1048
1050
  attributed("cache", corruption=True)