alissa-tools-github-revloop 0.21.0__tar.gz → 0.23.0__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 (32) hide show
  1. {alissa_tools_github_revloop-0.21.0/src/main/alissa_tools_github_revloop.egg-info → alissa_tools_github_revloop-0.23.0}/PKG-INFO +1 -1
  2. {alissa_tools_github_revloop-0.21.0 → alissa_tools_github_revloop-0.23.0}/src/main/alissa/tools/github/revloop/alissa.py +116 -6
  3. {alissa_tools_github_revloop-0.21.0 → alissa_tools_github_revloop-0.23.0}/src/main/alissa/tools/github/revloop/prreview.py +3 -3
  4. alissa_tools_github_revloop-0.23.0/src/main/alissa/tools/github/revloop/version +1 -0
  5. {alissa_tools_github_revloop-0.21.0 → alissa_tools_github_revloop-0.23.0/src/main/alissa_tools_github_revloop.egg-info}/PKG-INFO +1 -1
  6. alissa_tools_github_revloop-0.21.0/src/main/alissa/tools/github/revloop/version +0 -1
  7. {alissa_tools_github_revloop-0.21.0 → alissa_tools_github_revloop-0.23.0}/LICENSE +0 -0
  8. {alissa_tools_github_revloop-0.21.0 → alissa_tools_github_revloop-0.23.0}/MANIFEST.in +0 -0
  9. {alissa_tools_github_revloop-0.21.0 → alissa_tools_github_revloop-0.23.0}/NOTICE +0 -0
  10. {alissa_tools_github_revloop-0.21.0 → alissa_tools_github_revloop-0.23.0}/README.md +0 -0
  11. {alissa_tools_github_revloop-0.21.0 → alissa_tools_github_revloop-0.23.0}/requirements.txt +0 -0
  12. {alissa_tools_github_revloop-0.21.0 → alissa_tools_github_revloop-0.23.0}/setup.cfg +0 -0
  13. {alissa_tools_github_revloop-0.21.0 → alissa_tools_github_revloop-0.23.0}/setup.py +0 -0
  14. {alissa_tools_github_revloop-0.21.0 → alissa_tools_github_revloop-0.23.0}/src/main/alissa/tools/github/revloop/__init__.py +0 -0
  15. {alissa_tools_github_revloop-0.21.0 → alissa_tools_github_revloop-0.23.0}/src/main/alissa/tools/github/revloop/__main__.py +0 -0
  16. {alissa_tools_github_revloop-0.21.0 → alissa_tools_github_revloop-0.23.0}/src/main/alissa/tools/github/revloop/config.py +0 -0
  17. {alissa_tools_github_revloop-0.21.0 → alissa_tools_github_revloop-0.23.0}/src/main/alissa/tools/github/revloop/ghclient.py +0 -0
  18. {alissa_tools_github_revloop-0.21.0 → alissa_tools_github_revloop-0.23.0}/src/main/alissa/tools/github/revloop/loop.py +0 -0
  19. {alissa_tools_github_revloop-0.21.0 → alissa_tools_github_revloop-0.23.0}/src/main/alissa/tools/github/revloop/proc.py +0 -0
  20. {alissa_tools_github_revloop-0.21.0 → alissa_tools_github_revloop-0.23.0}/src/main/alissa/tools/github/revloop/state.py +0 -0
  21. {alissa_tools_github_revloop-0.21.0 → alissa_tools_github_revloop-0.23.0}/src/main/alissa/tools/github/revloop/version.py +0 -0
  22. {alissa_tools_github_revloop-0.21.0 → alissa_tools_github_revloop-0.23.0}/src/main/alissa/tools/github/revloop/webui/__init__.py +0 -0
  23. {alissa_tools_github_revloop-0.21.0 → alissa_tools_github_revloop-0.23.0}/src/main/alissa/tools/github/revloop/webui/__main__.py +0 -0
  24. {alissa_tools_github_revloop-0.21.0 → alissa_tools_github_revloop-0.23.0}/src/main/alissa/tools/github/revloop/webui/auth.py +0 -0
  25. {alissa_tools_github_revloop-0.21.0 → alissa_tools_github_revloop-0.23.0}/src/main/alissa/tools/github/revloop/webui/page.py +0 -0
  26. {alissa_tools_github_revloop-0.21.0 → alissa_tools_github_revloop-0.23.0}/src/main/alissa/tools/github/revloop/webui/server.py +0 -0
  27. {alissa_tools_github_revloop-0.21.0 → alissa_tools_github_revloop-0.23.0}/src/main/alissa/tools/github/revloop/webui/sources.py +0 -0
  28. {alissa_tools_github_revloop-0.21.0 → alissa_tools_github_revloop-0.23.0}/src/main/alissa/tools/github/revloop/webui/sysinfo.py +0 -0
  29. {alissa_tools_github_revloop-0.21.0 → alissa_tools_github_revloop-0.23.0}/src/main/alissa_tools_github_revloop.egg-info/SOURCES.txt +0 -0
  30. {alissa_tools_github_revloop-0.21.0 → alissa_tools_github_revloop-0.23.0}/src/main/alissa_tools_github_revloop.egg-info/dependency_links.txt +0 -0
  31. {alissa_tools_github_revloop-0.21.0 → alissa_tools_github_revloop-0.23.0}/src/main/alissa_tools_github_revloop.egg-info/entry_points.txt +0 -0
  32. {alissa_tools_github_revloop-0.21.0 → alissa_tools_github_revloop-0.23.0}/src/main/alissa_tools_github_revloop.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alissa-tools-github-revloop
3
- Version: 0.21.0
3
+ Version: 0.23.0
4
4
  Summary: ALISSA-TOOLS-GITHUB-REVLOOP
5
5
  Home-page: https://alissa.app
6
6
  Author: Fahera
@@ -13,8 +13,42 @@ from .proc import CommandError, run, run_json
13
13
 
14
14
  log = logging.getLogger(__name__)
15
15
 
16
- # A review task is "open" while it can still receive a verdict.
17
- OPEN_STATUSES = {"committed", "in_progress", "pending_validation", "todo"}
16
+ # The seven canonical Alissa task statuses -- `TaskStatusSchema` in the studio
17
+ # repo (`packages/client/src/schemas/common.ts`). Mirrored here for the reason
18
+ # the studio CLI mirrors it into `TASK_STATUSES` rather than importing it: what
19
+ # this module needs is the SET of names, not the schema that validates them.
20
+ #
21
+ # It is the vocabulary the `--status` filter below is allowed to speak. CLI
22
+ # 0.2.0 validates `--status` against exactly these seven client-side and exits 1
23
+ # on anything else BEFORE issuing any request, so a single non-canonical value
24
+ # does not narrow the call slightly less -- it costs the whole narrowed call.
25
+ #
26
+ # A status Alissa adds lands HERE first: a name added to `OPEN_STATUSES` alone
27
+ # reads as non-canonical and drops the narrowing for the whole fleet. That is
28
+ # the safe direction -- wide, never a wrong task resolved -- and the canonical
29
+ # test fails on it, but it fails naming `OPEN_STATUSES`, which is the constant
30
+ # that is right; this is the one that is stale (PR #98 round 1).
31
+ CANONICAL_TASK_STATUSES = frozenset(
32
+ {
33
+ "draft",
34
+ "committed",
35
+ "in_progress",
36
+ "blocked",
37
+ "pending_validation",
38
+ "validated",
39
+ "cancelled",
40
+ }
41
+ )
42
+
43
+ # A review task is "open" while it can still receive a verdict. Every member is
44
+ # canonical, and a test pins that: a status Alissa cannot issue is dead weight
45
+ # in `is_open` and poison in the `--status` filter derived from it.
46
+ #
47
+ # `todo` was such a member until 0.22.0 (issue #97). Alissa has never had that
48
+ # status, so `is_open` could never match it -- but it was still reaching CLI
49
+ # 0.2.0's `--status` validator, which rejects the call over it, which in turn
50
+ # drops this process back to the unnarrowed whole-corpus list for good.
51
+ OPEN_STATUSES = {"committed", "in_progress", "pending_validation"}
18
52
 
19
53
  # -- narrowing the `alissa task list` call (issue #87) ------------------------
20
54
  #
@@ -26,13 +60,48 @@ OPEN_STATUSES = {"committed", "in_progress", "pending_validation", "todo"}
26
60
  # not evidence, and this daemon turns a non-zero `alissa` exit into a SKIPPED
27
61
  # decision, so sending a flag the CLI does not have costs a review.
28
62
 
29
- # Statuses a LIVE review task can hold. Deliberately OPEN_STATUSES itself and
30
- # not a hand-written list: `is_review_task_for` already rejects every other
63
+ # Statuses a LIVE review task can hold. Deliberately derived from OPEN_STATUSES
64
+ # and not a hand-written list: `is_review_task_for` already rejects every other
31
65
  # status client-side, so filtering server-side on exactly this set cannot change
32
66
  # which task the daemon resolves -- it only stops shipping the rows over the
33
67
  # wire. Any status added to `is_open` is added here by construction.
68
+ #
69
+ # The canonical guard below does not weaken that invariant, and is deliberately
70
+ # not an INTERSECTION with the canonical set, which would: an intersection makes
71
+ # the filter a strict subset of the open set the moment the two disagree, so the
72
+ # daemon would stop fetching rows `is_open` still accepts -- a skipped review,
73
+ # reported as an empty corpus. The whole derivation is kept or nothing is sent.
74
+ # Sending nothing degrades to the call the daemon has always made: wide, but
75
+ # complete, which is the direction every other failure on this path degrades in.
34
76
  TASK_LIST_STATUS_FLAG = "--status"
35
- TASK_LIST_STATUS_FILTER = ",".join(sorted(OPEN_STATUSES))
77
+
78
+
79
+ def _status_filter(statuses: "set[str] | frozenset[str]") -> str:
80
+ """The `--status` argument for `statuses`, or `""` when they cannot be one.
81
+
82
+ `""` means "do not narrow by status". It is returned for a set carrying any
83
+ non-canonical value, because such a value is not a filter the CLI serves
84
+ less precisely -- it is one the CLI refuses outright, taking the `--view`
85
+ and `--self` narrowing down with it and pinning this process to the
86
+ whole-corpus list (`list_tasks`). One wide call beats every call being wide.
87
+
88
+ Pure on purpose. The condition is a code defect and deserves saying out
89
+ loud, but not from here: this runs at IMPORT, which is the one moment the
90
+ daemon's logging is not configured yet (see
91
+ `Alissa._warn_status_filter_dropped`). The tests are what pin the condition;
92
+ the runtime warning is the second line, and it is emitted where it can both
93
+ be formatted and be true.
94
+ """
95
+ if set(statuses) - CANONICAL_TASK_STATUSES:
96
+ return ""
97
+ return ",".join(sorted(statuses))
98
+
99
+
100
+ TASK_LIST_STATUS_FILTER = _status_filter(OPEN_STATUSES)
101
+
102
+ # Why the filter above is empty, when it is -- kept for the diagnostic, which
103
+ # cannot be emitted from module scope. Empty in the healthy case.
104
+ NON_CANONICAL_OPEN_STATUSES = tuple(sorted(OPEN_STATUSES - CANONICAL_TASK_STATUSES))
36
105
 
37
106
  # `--self` drops the SPONSOR's corpus and keeps only the calling actor's rows.
38
107
  #
@@ -320,6 +389,9 @@ class Alissa:
320
389
  # wrongly is worse than a list that is large: `find_review_task` reads an
321
390
  # empty corpus as "this PR has no review task".
322
391
  self._task_list_narrowing_disabled = False
392
+ # One diagnostic per client for a status filter that could not be sent
393
+ # (see `_warn_status_filter_dropped`); this path runs every poll pass.
394
+ self._status_filter_warned = False
323
395
 
324
396
  # -- the `alissa task list` narrowing probe -----------------------------
325
397
 
@@ -365,6 +437,38 @@ class Alissa:
365
437
  self._task_list_flags = flags
366
438
  return flags
367
439
 
440
+ def _warn_status_filter_dropped(self) -> None:
441
+ """Say, once, that a status narrowing this CLI offers is going unused.
442
+
443
+ Not emitted where the filter is COMPUTED, which is import time, and that
444
+ is the whole point of it living here (PR #98 round 1): `__main__` pulls
445
+ this module in through `.loop` long before it calls
446
+ `logging.basicConfig`, so a warning at module scope falls through to
447
+ `logging.lastResort` -- bare on stderr, no timestamp or logger name, and
448
+ outside whatever handler the deployment configured.
449
+
450
+ It would also be premature there. At import nothing yet knows whether
451
+ this CLI serves `--status` at all, and on a CLI that does not, the
452
+ filter costs nothing and there is nothing to warn about. Here the
453
+ message is only emitted when the flag was on offer and could not be
454
+ used, which is the only case where the defect actually costs reads.
455
+ """
456
+ if self._status_filter_warned:
457
+ return
458
+ self._status_filter_warned = True
459
+ reason = (
460
+ "non-canonical status(es) in OPEN_STATUSES: "
461
+ + ", ".join(NON_CANONICAL_OPEN_STATUSES)
462
+ if NON_CANONICAL_OPEN_STATUSES
463
+ else "OPEN_STATUSES is empty"
464
+ )
465
+ log.warning(
466
+ "`alissa task list` offers --status and this daemon is not using "
467
+ "it (%s) -- the CLI rejects the whole call over one unknown value, "
468
+ "so the corpus is listed unnarrowed",
469
+ reason,
470
+ )
471
+
368
472
  def task_list_argv(self, *, narrow_status: bool = True) -> list[str]:
369
473
  """The narrowest `alissa task list` this CLI actually supports.
370
474
 
@@ -375,8 +479,14 @@ class Alissa:
375
479
  if self._task_list_narrowing_disabled:
376
480
  return argv
377
481
  flags = self.probe_task_list()
482
+ # An empty filter is not a filter: `_status_filter` answers `""` when
483
+ # the open set carries a status the CLI would reject, and sending
484
+ # `--status ""` would be that same rejected call with an extra step.
378
485
  if flags.status and narrow_status:
379
- argv += [TASK_LIST_STATUS_FLAG, TASK_LIST_STATUS_FILTER]
486
+ if TASK_LIST_STATUS_FILTER:
487
+ argv += [TASK_LIST_STATUS_FLAG, TASK_LIST_STATUS_FILTER]
488
+ else:
489
+ self._warn_status_filter_dropped()
380
490
  if flags.self_scope and self._task_list_self_scope:
381
491
  argv.append(TASK_LIST_SELF_FLAG)
382
492
  if flags.digest:
@@ -75,9 +75,9 @@ def _review_task_ref(alissa: Alissa, owner: str, repo: str, number: int) -> str
75
75
  Deliberately not `Alissa.find_review_task` (which filters to open tasks): we
76
76
  still want the ref just after the task is validated, to read its verdict.
77
77
  `narrow_status=False` says the same thing to the LIST call for the same
78
- reason -- the daemon's server-side status filter is exactly its `is_open`
79
- set, and narrowing by it here would re-impose the filter this function
80
- exists to avoid.
78
+ reason -- the daemon's server-side status filter is either exactly its
79
+ `is_open` set or absent altogether, and in the first case narrowing by it
80
+ here would re-impose the filter this function exists to avoid.
81
81
  """
82
82
  pattern = re.compile(
83
83
  rf"^Review PR\s+{re.escape(owner)}/{re.escape(repo)}#{number}\b", re.IGNORECASE
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alissa-tools-github-revloop
3
- Version: 0.21.0
3
+ Version: 0.23.0
4
4
  Summary: ALISSA-TOOLS-GITHUB-REVLOOP
5
5
  Home-page: https://alissa.app
6
6
  Author: Fahera