log-foundry 0.10.2.dev113__tar.gz → 0.10.2.dev115__tar.gz

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.
Files changed (59) hide show
  1. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/PKG-INFO +2 -2
  2. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/README.md +1 -1
  3. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/pyproject.toml +1 -1
  4. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/results.py +5 -2
  5. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/worker.py +153 -15
  6. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/LICENSE +0 -0
  7. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/__init__.py +0 -0
  8. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/_diag.py +0 -0
  9. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/_fork.py +0 -0
  10. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/_lifecycle.py +0 -0
  11. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/api.py +0 -0
  12. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/config.py +0 -0
  13. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/console.py +0 -0
  14. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/context.py +0 -0
  15. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/decorator.py +0 -0
  16. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/ids.py +0 -0
  17. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/model.py +0 -0
  18. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/py.typed +0 -0
  19. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sanitize.py +0 -0
  20. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/__init__.py +0 -0
  21. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/_batch.py +0 -0
  22. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/_chunk.py +0 -0
  23. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/_retry.py +0 -0
  24. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/_socket.py +0 -0
  25. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/_time.py +0 -0
  26. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/base.py +0 -0
  27. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/callback.py +0 -0
  28. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/clickhouse.py +0 -0
  29. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/datadog.py +0 -0
  30. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/elasticsearch.py +0 -0
  31. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/eventhubs.py +0 -0
  32. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/file.py +0 -0
  33. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/filtering.py +0 -0
  34. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/firehose.py +0 -0
  35. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/honeycomb.py +0 -0
  36. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/http.py +0 -0
  37. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/kafka.py +0 -0
  38. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/kinesis.py +0 -0
  39. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/logging_sink.py +0 -0
  40. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/logstash.py +0 -0
  41. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/loki.py +0 -0
  42. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/memory.py +0 -0
  43. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/mongodb.py +0 -0
  44. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/multi.py +0 -0
  45. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/nats.py +0 -0
  46. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/newrelic.py +0 -0
  47. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/null.py +0 -0
  48. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/postgres.py +0 -0
  49. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/pubsub.py +0 -0
  50. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/rabbitmq.py +0 -0
  51. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/redis.py +0 -0
  52. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/sentry.py +0 -0
  53. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/sns.py +0 -0
  54. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/splunk.py +0 -0
  55. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/sqlite.py +0 -0
  56. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/sqs.py +0 -0
  57. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/stdout.py +0 -0
  58. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/syslog.py +0 -0
  59. {log_foundry-0.10.2.dev113 → log_foundry-0.10.2.dev115}/src/log_foundry/sinks/transform.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: log-foundry
3
- Version: 0.10.2.dev113
3
+ Version: 0.10.2.dev115
4
4
  Summary: Generate logs for your console and JSON events for downstream consumption.
5
5
  License-Expression: MIT
6
6
  License-File: LICENSE
@@ -1208,7 +1208,7 @@ Falsy carries a `reason` saying which of these happened, because they need diffe
1208
1208
  | `"retired"` | `shutdown()` was already called. Your lifecycle is wrong, not the sink. |
1209
1209
  | `"thread-died"` | The drain thread is gone; see `health().stopped_reason`. |
1210
1210
  | `"queue-full"` | Backpressure — the queue could not even accept the marker. |
1211
- | `"abandoned"` | A batch was given up on after its retry budget. The destination is broken. |
1211
+ | `"abandoned"` | The drain that would have carried those events did not confirm delivery of them. A broken destination is one cause, but not the only one — an expiring bounded `shutdown()` answers waiting calls pessimistically, and the drain may still deliver afterwards. So this is not by itself evidence that anything was lost: `health()` is what says whether it was. |
1212
1212
  | `"sink-flush"` | Everything queued reached the sink, but the sink's own `flush()` raised — a client-side buffer that did not go out. Distinct from `"abandoned"`: the library delivered, the sink did not. |
1213
1213
 
1214
1214
  ```python
@@ -1157,7 +1157,7 @@ Falsy carries a `reason` saying which of these happened, because they need diffe
1157
1157
  | `"retired"` | `shutdown()` was already called. Your lifecycle is wrong, not the sink. |
1158
1158
  | `"thread-died"` | The drain thread is gone; see `health().stopped_reason`. |
1159
1159
  | `"queue-full"` | Backpressure — the queue could not even accept the marker. |
1160
- | `"abandoned"` | A batch was given up on after its retry budget. The destination is broken. |
1160
+ | `"abandoned"` | The drain that would have carried those events did not confirm delivery of them. A broken destination is one cause, but not the only one — an expiring bounded `shutdown()` answers waiting calls pessimistically, and the drain may still deliver afterwards. So this is not by itself evidence that anything was lost: `health()` is what says whether it was. |
1161
1161
  | `"sink-flush"` | Everything queued reached the sink, but the sink's own `flush()` raised — a client-side buffer that did not go out. Distinct from `"abandoned"`: the library delivered, the sink did not. |
1162
1162
 
1163
1163
  ```python
@@ -74,7 +74,7 @@ keywords = [
74
74
  # vulnerability-reporting channel. The repository is still named `log-forge` — the ORIGINAL name,
75
75
  # which PyPI rejected for the distribution — so these URLs deliberately do not match the package
76
76
  # name. See the note on `name` above before "correcting" them.
77
- version = "0.10.2.dev113"
77
+ version = "0.10.2.dev115"
78
78
 
79
79
  [project.urls]
80
80
  Homepage = "https://github.com/agriffi10/log-forge"
@@ -55,8 +55,11 @@ class FlushResult(_Result):
55
55
  ``"sink-flush"`` is the one SPEC-036 added (FR-002 AC-8) and it is worth distinguishing: the
56
56
  queue drained cleanly and the **sink's own client buffer** did not, so the events are past
57
57
  this library and inside a driver. ``"abandoned"`` is the neighbouring case where this call
58
- could not hand them over at all. New tokens may appear in any release, which is what this
59
- type exists for — branch on ``bool()``.
58
+ could not confirm they were handed over. It has **four** producers — a drain that spent its
59
+ retries, an expiring ``shutdown()`` answering pessimistically, a marker that arrived after
60
+ that, and one that never reached the queue at all because it was full — so it means "not
61
+ confirmed delivered" and never "confirmed lost" (SPEC-050 FR-001). ``ok=True`` carries the guarantee instead: the sink took them. New tokens may appear
62
+ in any release, which is what this type exists for — branch on ``bool()``.
60
63
  """
61
64
 
62
65
 
@@ -29,6 +29,13 @@ SPEC-034 froze — keeps naming the same object.
29
29
 
30
30
  _DROP_WARN_EVERY = 1000
31
31
 
32
+ _PUT_POLL_SECONDS = 0.05
33
+ """How long a full-queue ``flush()`` waits before re-asking whether the drain was abandoned.
34
+
35
+ Small enough that an unbounded caller notices promptly, large enough that a full queue does not
36
+ become a spin. It bounds only the *slice*, never the caller's own deadline (SPEC-050 FR-001).
37
+ """
38
+
32
39
  DEFAULT_SWAP_TIMEOUT = _lifecycle.DEFAULT_SWAP_TIMEOUT
33
40
  """Re-exported from ``_lifecycle``, which owns it (SPEC-040 FR-001).
34
41
 
@@ -755,6 +762,30 @@ class Worker:
755
762
  ``put`` and the sweep's snapshot both take the queue's own mutex, so a marker either
756
763
  lands before the snapshot and is answered, or lands after it and finds the flag set.
757
764
 
765
+ **``_drain_settled`` is the third flag, and an expired ``shutdown()`` sets only that
766
+ one** (SPEC-050 FR-001). On that path the drain is still alive and still inside ``emit``,
767
+ so ``_drain_finished`` is clear and ``is_alive()`` is true — a marker landing after that
768
+ sweep found every existing condition false and waited on a drain the process had already
769
+ given up on. Measured: the flusher still waiting three seconds later, released only by
770
+ re-running the sweep by hand. ``timeout=None`` makes it permanent, and in an application
771
+ that is a non-daemon thread the interpreter joins at exit, so the process does not exit.
772
+ ``_drain_settled`` set with ``_drain_finished`` clear is *uniquely* that branch — every
773
+ other setter sets both, and :meth:`_run` sets ``_drain_finished`` **first**, so a true
774
+ read of settled implies finished was already set — which is what lets this report
775
+ ``"abandoned"`` rather than ``"thread-died"`` for a thread that is demonstrably alive.
776
+ Inverting that pair in :meth:`_run` survives the whole suite and would produce the
777
+ mislabel; it is held by :meth:`_run`'s docstring naming the order rather than by a test,
778
+ because the consequence is a wrong *reason* on two falsy results and not a wrong verdict.
779
+
780
+ **The delivered test comes first, and a test holds it there.** A marker the drain answered
781
+ ``delivered=True`` between the sweep and ``_drain_finished`` being set must report
782
+ ``ok=True``, not ``"abandoned"`` — the drain adjudicated it, and a false negative here is
783
+ what makes :meth:`swap_sink` count an ``incomplete_swaps`` and write a loss line for a swap
784
+ that completed. A wrong verdict, not a wrong reason. An earlier draft of this docstring
785
+ called the window unreachable without interposing inside this method; that was wrong twice
786
+ over — parking after the put reaches it, and holding ``_release_marker`` widens it — and a
787
+ reviewer built both.
788
+
758
789
  Reporting is by the **marker**, never by the check alone. A drain that answered this
759
790
  marker and then exited has delivered, and saying otherwise would be a false failure —
760
791
  one ``swap_sink`` reads as an unconfirmed drain, counting ``incomplete_swaps``, leaving
@@ -767,9 +798,17 @@ class Worker:
767
798
  Returns:
768
799
  A :class:`FlushResult`, truthy once the worker has delivered them and otherwise
769
800
  falsy with a ``reason``: ``"timed-out"``, ``"retired"``, ``"thread-died"``,
770
- ``"queue-full"``, or ``"abandoned"`` when the drain carrying those events gave up
771
- after exhausting retries (SPEC-021 FR-001) — that last case used to return True, a
772
- false success exactly where ``flush()`` matters most. **The inner call carries the
801
+ ``"queue-full"``, or ``"abandoned"``. That last one has **four** producers, not one:
802
+ the drain carrying those events gave up after exhausting retries (SPEC-021 FR-001,
803
+ the original — and it used to return True, a false success exactly where ``flush()``
804
+ matters most); an expiring ``shutdown()`` answered the marker pessimistically rather
805
+ than leave it on a drain it had abandoned (SPEC-050 FR-001); this call's own marker
806
+ arrived after that had already happened; or it never reached the queue, because that
807
+ was full and the drain had already been given up on. Only the first says the drain
808
+ adjudicated the batch, so ``"abandoned"`` means "not confirmed delivered", never
809
+ "confirmed lost". ``ok=True`` is unaffected and still means the sink took them:
810
+ ``delivered`` starts ``False`` and is written only by the drain, only after its emit
811
+ returned. **The inner call carries the
773
812
  type too, not only the public ``log_foundry.flush``** (SPEC-034 FR-007 AC-1b): the
774
813
  five outcomes are distinguishable only here, so a public wrapper over a bare ``bool``
775
814
  could name none of them without guessing.
@@ -785,16 +824,20 @@ class Worker:
785
824
  with self._lock:
786
825
  marker = _FlushMarker(self.failed_batches)
787
826
  deadline = None if timeout is None else time.monotonic() + timeout
788
- try:
789
- self._queue.put(marker, timeout=timeout)
790
- except queue.Full:
827
+ if not self._put_marker(marker, deadline):
828
+ if self._given_up():
829
+ return FlushResult(ok=False, reason="abandoned")
830
+ if not self._thread.is_alive() or self._drain_finished.is_set():
831
+ return FlushResult(ok=False, reason="thread-died")
791
832
  return FlushResult(ok=False, reason="queue-full")
792
- if self._drain_finished.is_set() or not self._thread.is_alive():
833
+ given_up = self._given_up()
834
+ if self._settled():
793
835
  answered = marker.event.is_set()
794
- delivered = answered and marker.delivered
795
- if delivered:
836
+ if answered and marker.delivered:
796
837
  return FlushResult(ok=True)
797
- return FlushResult(ok=False, reason="abandoned" if answered else "thread-died")
838
+ if answered or given_up:
839
+ return FlushResult(ok=False, reason="abandoned")
840
+ return FlushResult(ok=False, reason="thread-died")
798
841
  remaining = None if deadline is None else max(0.0, deadline - time.monotonic())
799
842
  if not marker.event.wait(remaining):
800
843
  return FlushResult(ok=False, reason="timed-out")
@@ -802,6 +845,92 @@ class Worker:
802
845
  return FlushResult(ok=False, reason="abandoned")
803
846
  return FlushResult(ok=True)
804
847
 
848
+ def _given_up(self) -> bool:
849
+ """Whether a bounded ``shutdown()`` expired on a drain that is still running.
850
+
851
+ ``_drain_settled`` set with ``_drain_finished`` clear is **uniquely** that branch: every
852
+ other setter sets both, and :meth:`_run` sets ``_drain_finished`` first, so a true read of
853
+ settled implies finished was already set. That ordering is what makes this predicate
854
+ precise rather than merely suggestive, and :meth:`_run` names it in turn.
855
+
856
+ Args:
857
+ None.
858
+
859
+ Returns:
860
+ Whether the drain has been abandoned while still alive.
861
+
862
+ Raises:
863
+ None.
864
+ """
865
+ return self._drain_settled.is_set() and not self._drain_finished.is_set()
866
+
867
+ def _settled(self) -> bool:
868
+ """Whether nothing will drain this queue again, for any of the three reasons.
869
+
870
+ The disjunction :meth:`flush`'s post-put re-check tests, named once so the pre-put wait
871
+ and the post-put check cannot drift apart: the drain finished, a bounded ``shutdown()``
872
+ gave up on it, or the thread is gone. :meth:`_given_up` is the narrower question of *which*
873
+ of those it was, which only the reason mapping needs.
874
+
875
+ Args:
876
+ None.
877
+
878
+ Returns:
879
+ Whether the drain has stopped, been abandoned, or died.
880
+
881
+ Raises:
882
+ None.
883
+ """
884
+ return self._drain_finished.is_set() or self._given_up() or not self._thread.is_alive()
885
+
886
+ def _put_marker(self, marker: _FlushMarker, deadline: float | None) -> bool:
887
+ """Queues a flush marker, giving up if the drain is abandoned while the queue is full.
888
+
889
+ A plain ``put`` with ``timeout=None`` blocks until the queue has room, and on a full queue
890
+ behind a permanently wedged sink there is never any — so the caller waited forever one
891
+ line *before* the post-put re-check that exists to prevent exactly that. Measured at
892
+ process level: a non-daemon flusher parked in ``Queue.put`` for the whole run, and an
893
+ interpreter that could not exit because it joins that thread. The re-check cannot help,
894
+ because ``_release_waiters`` can only answer a marker that is already in the queue.
895
+
896
+ So the wait is taken in slices and :meth:`_settled` is consulted between them — the
897
+ re-check's own disjunction, not merely :meth:`_given_up`, because a *terminally dead*
898
+ drain sets both flags and would leave an unbounded caller parked forever on the narrower
899
+ test. The deadline is consulted **after** a put has been attempted, never before: a
900
+ ``flush(timeout=0)`` is "enqueue and do not wait", and testing the deadline first turned
901
+ it into a call that never enqueued at all and reported backpressure that did not exist —
902
+ which is the one outcome SPEC-034 FR-007 named ``reason`` to tell apart. A bounded caller
903
+ is otherwise unaffected: the deadline still ends it, with the same ``"queue-full"``.
904
+ The slice is a polling granularity on a queue that is *already* full, which is a degraded
905
+ state the caller is being told about either way, and it bounds only the slice — a negative
906
+ or zero remainder clamps to an immediate attempt rather than raising, where ``Queue.put``
907
+ rejects a negative timeout before it even looks at capacity. That makes the **put** total,
908
+ which is what ``Raises: None`` said all along; the *wait* below is unchanged and a timeout
909
+ large enough to overflow ``time_t`` still raises out of it, on this tree and on every
910
+ earlier one.
911
+
912
+ Args:
913
+ marker: The marker to enqueue.
914
+ deadline: The caller's monotonic deadline, or ``None`` for an unbounded caller.
915
+
916
+ Returns:
917
+ Whether the marker reached the queue.
918
+
919
+ Raises:
920
+ None.
921
+ """
922
+ while True:
923
+ slice_seconds = _PUT_POLL_SECONDS
924
+ if deadline is not None:
925
+ slice_seconds = min(slice_seconds, max(0.0, deadline - time.monotonic()))
926
+ try:
927
+ self._queue.put(marker, timeout=slice_seconds)
928
+ except queue.Full:
929
+ if self._settled() or (deadline is not None and time.monotonic() >= deadline):
930
+ return False
931
+ else:
932
+ return True
933
+
805
934
  def swap_sink(self, new_sink: Sink, timeout: float | None = DEFAULT_SWAP_TIMEOUT) -> bool:
806
935
  """Retargets delivery at a new sink, draining and closing the previous one (FR-003).
807
936
 
@@ -1321,7 +1450,13 @@ class Worker:
1321
1450
  would be a worse failure than the one this prevents.
1322
1451
 
1323
1452
  The two ``finally`` blocks are nested rather than merged, and the order they impose is
1324
- load-bearing twice over. ``_drain_finished`` is set the instant the loop stops reading
1453
+ load-bearing three times over. ``_drain_finished`` is set **before** ``_drain_settled``,
1454
+ and :meth:`_given_up` reads the pair in the opposite order to conclude that a shutdown
1455
+ expired on a live drain — so inverting these two lines would make an ordinary terminal
1456
+ exit indistinguishable from an abandoned one for a moment, and a ``flush()`` landing there
1457
+ would be told ``"abandoned"`` where ``"thread-died"`` is the truth. Both are falsy, so the
1458
+ cost is a wrong reason rather than a wrong verdict, which is why it is recorded here
1459
+ rather than pinned by a test. ``_drain_finished`` is set the instant the loop stops reading
1325
1460
  the queue — *before* :meth:`_terminal_failure`, which writes to stderr and can block on
1326
1461
  a slow reader — so a ``shutdown()`` arriving during that window sees a drain that is
1327
1462
  already finished and declines to queue a sentinel nothing would consume. And it is set
@@ -1403,7 +1538,7 @@ class Worker:
1403
1538
  self._taken_markers = [m for m in self._taken_markers if m is not marker]
1404
1539
 
1405
1540
  def _release_waiters(self) -> None:
1406
- """Answers every outstanding ``flush()`` marker, so no caller waits out its timeout.
1541
+ """Answers the ``flush()`` markers outstanding at the moment it runs.
1407
1542
 
1408
1543
  **Two populations, and the second is not optional** (SPEC-050 FR-001). A marker still in
1409
1544
  the queue is read from it; a marker the drain thread has already **taken** is read from
@@ -1430,9 +1565,12 @@ class Worker:
1430
1565
  than too long. The marker keeps its pessimistic ``delivered``, which is the honest
1431
1566
  answer: the drain that would have carried it is gone.
1432
1567
 
1433
- One residual race, stated rather than papered over: a ``flush()`` that passed its
1434
- liveness check microseconds before *this* sweep can still enqueue a marker after it,
1435
- and that one waits out its timeout — then returns False, which is correct either way.
1568
+ **It answers a snapshot, so "every" would be the wrong word** and a caller arriving
1569
+ after it is not covered here. A ``flush()`` that passed its guards microseconds before
1570
+ this sweep can still enqueue its marker after it. That caller is answered by
1571
+ :meth:`flush`'s own post-put re-check instead, which consults the same
1572
+ ``_drain_settled`` this path sets — before SPEC-050 FR-001 added it there, such a caller
1573
+ waited out its timeout, and a ``timeout=None`` one waited forever.
1436
1574
  A marker left queued is also still counted by ``health().queued``, which describes
1437
1575
  submissions; removing it would mean deleting a specific item, which ``Queue`` has no
1438
1576
  public way to do, and the read above is the access ``architecture.md`` §13 sanctions.