proactive-gate 0.7.2__tar.gz → 0.7.3__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.
- {proactive_gate-0.7.2 → proactive_gate-0.7.3}/PKG-INFO +1 -1
- {proactive_gate-0.7.2 → proactive_gate-0.7.3}/pyproject.toml +1 -1
- {proactive_gate-0.7.2 → proactive_gate-0.7.3}/src/proactive_gate/__init__.py +1 -1
- {proactive_gate-0.7.2 → proactive_gate-0.7.3}/src/proactive_gate/explain.py +12 -3
- {proactive_gate-0.7.2 → proactive_gate-0.7.3}/tests/test_explain.py +21 -3
- {proactive_gate-0.7.2 → proactive_gate-0.7.3}/.gitignore +0 -0
- {proactive_gate-0.7.2 → proactive_gate-0.7.3}/README.md +0 -0
- {proactive_gate-0.7.2 → proactive_gate-0.7.3}/src/proactive_gate/checks.py +0 -0
- {proactive_gate-0.7.2 → proactive_gate-0.7.3}/src/proactive_gate/clock.py +0 -0
- {proactive_gate-0.7.2 → proactive_gate-0.7.3}/src/proactive_gate/conformance.py +0 -0
- {proactive_gate-0.7.2 → proactive_gate-0.7.3}/src/proactive_gate/gate.py +0 -0
- {proactive_gate-0.7.2 → proactive_gate-0.7.3}/src/proactive_gate/policy.py +0 -0
- {proactive_gate-0.7.2 → proactive_gate-0.7.3}/src/proactive_gate/presets.py +0 -0
- {proactive_gate-0.7.2 → proactive_gate-0.7.3}/src/proactive_gate/py.typed +0 -0
- {proactive_gate-0.7.2 → proactive_gate-0.7.3}/src/proactive_gate/stores.py +0 -0
- {proactive_gate-0.7.2 → proactive_gate-0.7.3}/src/proactive_gate/types.py +0 -0
- {proactive_gate-0.7.2 → proactive_gate-0.7.3}/tests/conftest.py +0 -0
- {proactive_gate-0.7.2 → proactive_gate-0.7.3}/tests/test_clock.py +0 -0
- {proactive_gate-0.7.2 → proactive_gate-0.7.3}/tests/test_conformance.py +0 -0
- {proactive_gate-0.7.2 → proactive_gate-0.7.3}/tests/test_stores.py +0 -0
- {proactive_gate-0.7.2 → proactive_gate-0.7.3}/tests/test_version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: proactive-gate
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.3
|
|
4
4
|
Summary: Decide whether a proactive assistant may speak now: ordered checks, budgets, quiet hours, JSON policies, shared conformance fixtures with the TypeScript package.
|
|
5
5
|
Project-URL: Homepage, https://bubblegunn.github.io/proactive-gate/
|
|
6
6
|
Project-URL: Documentation, https://bubblegunn.github.io/proactive-gate/start/
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "proactive-gate"
|
|
7
|
-
version = "0.7.
|
|
7
|
+
version = "0.7.3"
|
|
8
8
|
description = "Decide whether a proactive assistant may speak now: ordered checks, budgets, quiet hours, JSON policies, shared conformance fixtures with the TypeScript package."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -501,10 +501,19 @@ def _summarize(decision: Decision, sentences: Mapping[str, SentenceTemplate]) ->
|
|
|
501
501
|
clause = _t(sentences, template, facts)
|
|
502
502
|
# For a deferral the hold ends at retryAt; when the clause already names the
|
|
503
503
|
# instant (snooze's reason is "snoozed until X"), naming it twice reads worse.
|
|
504
|
+
#
|
|
505
|
+
# The summary may headline an instant ONLY when the decision will actually be
|
|
506
|
+
# reconsidered at it. quietHours can only reject, and its facts carry the
|
|
507
|
+
# window end as holdUntil, so this used to render a reject as "held until
|
|
508
|
+
# 08:00" -- telling the reader the message goes out at 08:00 when nothing
|
|
509
|
+
# would ever send it. The window is not lost: the clause still names it.
|
|
504
510
|
clause_says_when = "until" in facts
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
511
|
+
will_be_reconsidered = entry.outcome == "defer"
|
|
512
|
+
until = None
|
|
513
|
+
if will_be_reconsidered:
|
|
514
|
+
until = facts.get("holdUntil")
|
|
515
|
+
if until is None and not clause_says_when and decision.retry_at is not None:
|
|
516
|
+
until = iso_z(decision.retry_at)
|
|
508
517
|
held_facts: Facts = {"clause": clause}
|
|
509
518
|
if until is not None:
|
|
510
519
|
held_facts["until"] = until
|
|
@@ -63,7 +63,21 @@ def test_plain_sentence_and_machine_reason_are_both_on_hand() -> None:
|
|
|
63
63
|
d = evaluate([checks.QuietHours()], now=NIGHT)
|
|
64
64
|
e = explain(d)
|
|
65
65
|
assert d.reason == "quiet hours 22:00 to 08:00 Europe/Istanbul; priority normal is below the floor (critical)"
|
|
66
|
-
|
|
66
|
+
# Not "Held until 08:00": QuietHours only rejects, and nothing retries a
|
|
67
|
+
# reject. The expectation was changed because it pinned a defect, not
|
|
68
|
+
# because a test failed. See test_a_reject_never_names_an_instant below.
|
|
69
|
+
assert e.summary == "Held because the user's quiet hours run 22:00 to 08:00 Europe/Istanbul and normal priority is below the critical floor needed to override them."
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def test_a_reject_never_names_an_instant() -> None:
|
|
73
|
+
"""The summary may headline a time only when the decision is reconsidered at it."""
|
|
74
|
+
d = evaluate([checks.QuietHours()], now=NIGHT)
|
|
75
|
+
assert d.allowed is False
|
|
76
|
+
assert d.retry_at is None, "the fixture must be a reject, or this test proves nothing"
|
|
77
|
+
assert d.deferred_by is None
|
|
78
|
+
summary = explain(d).summary
|
|
79
|
+
assert "until" not in summary, f"a reject must not name an instant: {summary}"
|
|
80
|
+
assert "quiet hours run 22:00 to 08:00" in summary
|
|
67
81
|
|
|
68
82
|
|
|
69
83
|
def test_not_stopped_every_check_gets_a_sentence() -> None:
|
|
@@ -353,9 +367,13 @@ def test_language_is_a_parameter() -> None:
|
|
|
353
367
|
assert explain(d).summary == explain(d, language="en").summary
|
|
354
368
|
with pytest.raises(ValueError, match='no sentence catalog for language "tr"'):
|
|
355
369
|
explain(d, language="tr")
|
|
356
|
-
|
|
370
|
+
# `until` is optional: the summary carries it only when the decision will be
|
|
371
|
+
# reconsidered at it, so a caller catalog must handle its absence the way the
|
|
372
|
+
# shipped English one does. This template used to index it directly and only
|
|
373
|
+
# worked because the fixture happened to be a reject that wrongly named a time.
|
|
374
|
+
tr = {"summary.held": lambda f: f"{f['until'] + ' kadar ' if f.get('until') else ''}bekletildi, çünkü {f['clause']}"}
|
|
357
375
|
e = explain(d, language="tr", catalogs={"tr": tr})
|
|
358
|
-
assert e.summary.startswith("
|
|
376
|
+
assert e.summary.startswith("Bekletildi, çünkü")
|
|
359
377
|
assert len(EN) > 0
|
|
360
378
|
|
|
361
379
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|