alissa-tools-github-revloop 0.31.7__tar.gz → 0.31.9__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 (38) hide show
  1. {alissa_tools_github_revloop-0.31.7/src/main/alissa_tools_github_revloop.egg-info → alissa_tools_github_revloop-0.31.9}/PKG-INFO +1 -1
  2. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/src/main/alissa/tools/github/revloop/alissa.py +78 -3
  3. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/src/main/alissa/tools/github/revloop/loop.py +40 -1
  4. alissa_tools_github_revloop-0.31.9/src/main/alissa/tools/github/revloop/version +1 -0
  5. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9/src/main/alissa_tools_github_revloop.egg-info}/PKG-INFO +1 -1
  6. alissa_tools_github_revloop-0.31.7/src/main/alissa/tools/github/revloop/version +0 -1
  7. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/LICENSE +0 -0
  8. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/MANIFEST.in +0 -0
  9. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/NOTICE +0 -0
  10. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/README.md +0 -0
  11. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/requirements.txt +0 -0
  12. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/setup.cfg +0 -0
  13. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/setup.py +0 -0
  14. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/src/main/alissa/tools/github/revloop/__init__.py +0 -0
  15. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/src/main/alissa/tools/github/revloop/__main__.py +0 -0
  16. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/src/main/alissa/tools/github/revloop/alissa_client.py +0 -0
  17. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/src/main/alissa/tools/github/revloop/bows.py +0 -0
  18. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/src/main/alissa/tools/github/revloop/config.py +0 -0
  19. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/src/main/alissa/tools/github/revloop/fleet_vitals.py +0 -0
  20. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/src/main/alissa/tools/github/revloop/ghclient.py +0 -0
  21. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/src/main/alissa/tools/github/revloop/loop_events.py +0 -0
  22. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/src/main/alissa/tools/github/revloop/proc.py +0 -0
  23. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/src/main/alissa/tools/github/revloop/prompts.py +0 -0
  24. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/src/main/alissa/tools/github/revloop/prreview.py +0 -0
  25. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/src/main/alissa/tools/github/revloop/state.py +0 -0
  26. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/src/main/alissa/tools/github/revloop/trust.py +0 -0
  27. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/src/main/alissa/tools/github/revloop/version.py +0 -0
  28. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/src/main/alissa/tools/github/revloop/webui/__init__.py +0 -0
  29. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/src/main/alissa/tools/github/revloop/webui/__main__.py +0 -0
  30. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/src/main/alissa/tools/github/revloop/webui/auth.py +0 -0
  31. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/src/main/alissa/tools/github/revloop/webui/page.py +0 -0
  32. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/src/main/alissa/tools/github/revloop/webui/server.py +0 -0
  33. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/src/main/alissa/tools/github/revloop/webui/sources.py +0 -0
  34. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/src/main/alissa/tools/github/revloop/webui/sysinfo.py +0 -0
  35. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/src/main/alissa_tools_github_revloop.egg-info/SOURCES.txt +0 -0
  36. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/src/main/alissa_tools_github_revloop.egg-info/dependency_links.txt +0 -0
  37. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/src/main/alissa_tools_github_revloop.egg-info/entry_points.txt +0 -0
  38. {alissa_tools_github_revloop-0.31.7 → alissa_tools_github_revloop-0.31.9}/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.31.7
3
+ Version: 0.31.9
4
4
  Summary: ALISSA-TOOLS-GITHUB-REVLOOP
5
5
  Home-page: https://alissa.app
6
6
  Author: Fahera
@@ -219,6 +219,30 @@ def _advertises(helptext: str, flag: str) -> bool:
219
219
  ) is not None
220
220
 
221
221
 
222
+ # The two spawn stamps of the Studio loop cost meter (studio.alissa.app
223
+ # docs/design/loop-cost-meter.md §2.3, §2.4, lane L4; issue #153, the port of
224
+ # devloop's issue #149): `--task` names the ORIGIN task the reviewer serves and
225
+ # `--repo` the canonical lowercase `owner/name`, so the session row carries
226
+ # `focusTaskId` and `repo` from its first second. The reviewer's skill then
227
+ # re-anchors `current_task` on its review task and that write wins the focus;
228
+ # the origin keeps its `code_session` evidence row and the meter resolves the
229
+ # rest at read time (§2.4).
230
+ QUEUE_ADD_TASK_FLAG = "--task"
231
+ QUEUE_ADD_REPO_FLAG = "--repo"
232
+
233
+
234
+ def queue_add_accepts_stamps(helptext: str) -> bool:
235
+ """Whether `alissa tmux queue add --help` lists BOTH `--task` and `--repo`.
236
+
237
+ Both or neither: the CLI this daemon shipped against (0.3.0) already lists
238
+ `-t, --task <ref>`, but with the older meaning -- link a queue item for
239
+ shell write-back -- and has no `--repo`; that CLI is not the stamp-aware
240
+ one, so it gets neither flag.
241
+ """
242
+ text = helptext or ""
243
+ return _advertises(text, QUEUE_ADD_TASK_FLAG) and _advertises(text, QUEUE_ADD_REPO_FLAG)
244
+
245
+
222
246
  # CR6 verdict envelope outcomes.
223
247
  VERDICT_APPROVE = "approve"
224
248
  VERDICT_REQUEST_CHANGES = "request_changes"
@@ -879,6 +903,9 @@ class Alissa:
879
903
  # One diagnostic per client for a status filter that could not be sent
880
904
  # (see `_warn_status_filter_dropped`); this path runs every poll pass.
881
905
  self._status_filter_warned = False
906
+ # The spawn-stamp probe's answer, memoized like `_task_list_flags`;
907
+ # None = not probed yet (or the last probe could not run).
908
+ self._spawn_stamps_accepted: "bool | None" = None
882
909
 
883
910
  @property
884
911
  def task_list_bow_id(self) -> "str | None":
@@ -1385,6 +1412,37 @@ class Alissa:
1385
1412
  break
1386
1413
  return count
1387
1414
 
1415
+ # -- the `alissa tmux queue add` spawn-stamp probe -----------------------
1416
+
1417
+ def accepts_spawn_stamps(self) -> bool:
1418
+ """Whether the installed `queue add` takes `--task` AND `--repo`.
1419
+
1420
+ Read off the CLI's own `--help`, for the reasons `probe_task_list`
1421
+ gives. An answer, yes or no, is memoized for the process; a probe that
1422
+ cannot run at all reads as "no" for THIS spawn and is not memoized, so
1423
+ a transient failure never switches the stamps off until a restart.
1424
+ """
1425
+ if self._spawn_stamps_accepted is not None:
1426
+ return self._spawn_stamps_accepted
1427
+ try:
1428
+ helptext = run(["alissa", "tmux", "queue", "add", "--help"], timeout=20)
1429
+ except CommandError as exc:
1430
+ log.warning(
1431
+ "could not probe `alissa tmux queue add --help` (%s) — this "
1432
+ "reviewer is enqueued without --task/--repo", exc,
1433
+ )
1434
+ return False
1435
+ except Exception: # pragma: no cover - defence in depth
1436
+ log.exception("unexpected failure probing `alissa tmux queue add --help`")
1437
+ return False
1438
+ self._spawn_stamps_accepted = queue_add_accepts_stamps(helptext)
1439
+ if not self._spawn_stamps_accepted:
1440
+ log.info(
1441
+ "the installed alissa CLI does not accept `queue add --task/--repo`; "
1442
+ "reviewer spawns go unstamped"
1443
+ )
1444
+ return self._spawn_stamps_accepted
1445
+
1388
1446
  def enqueue_reviewer(
1389
1447
  self,
1390
1448
  *,
@@ -1393,8 +1451,18 @@ class Alissa:
1393
1451
  cwd: Path,
1394
1452
  agent: str,
1395
1453
  task_ref: str | None,
1454
+ repo: str | None = None,
1396
1455
  dry_run: bool = False,
1397
1456
  ) -> None:
1457
+ """Enqueue one reviewer session.
1458
+
1459
+ `task_ref` is the ORIGIN task (`TASK-<n>`) the round serves -- not the
1460
+ review task, which the directive names and the ledger records -- and
1461
+ `repo` the PR's `owner/name`, lowercased here. They are passed as
1462
+ `--task` / `--repo` only when the installed CLI accepts both (see
1463
+ `accepts_spawn_stamps`); an older CLI gets neither, so the enqueue
1464
+ never fails for want of a stamp.
1465
+ """
1398
1466
  argv = [
1399
1467
  "alissa",
1400
1468
  "tmux",
@@ -1406,14 +1474,21 @@ class Alissa:
1406
1474
  "--cwd",
1407
1475
  str(cwd),
1408
1476
  ]
1477
+ stamps: "list[str]" = []
1409
1478
  if task_ref:
1410
- argv += ["--task", task_ref]
1411
- argv.append(directive)
1479
+ stamps += [QUEUE_ADD_TASK_FLAG, task_ref]
1480
+ if repo:
1481
+ stamps += [QUEUE_ADD_REPO_FLAG, repo.lower()]
1412
1482
 
1413
1483
  if dry_run:
1414
- log.info("[dry-run] would enqueue: %s", " ".join(argv[:-1]) + " <directive>")
1484
+ # Runs nothing, the probe included: the line shows the stamps a
1485
+ # stamp-aware CLI would receive.
1486
+ log.info("[dry-run] would enqueue: %s", " ".join(argv + stamps) + " <directive>")
1415
1487
  return
1416
1488
 
1489
+ if stamps and self.accepts_spawn_stamps():
1490
+ argv += stamps
1491
+ argv.append(directive)
1417
1492
  run(argv, timeout=60)
1418
1493
 
1419
1494
  # Reviewers are one-shot per round (CR3): once the session finishes and is
@@ -1215,6 +1215,44 @@ def plan_id_of(pr: PullRequest) -> "str | None":
1215
1215
  return match.group(1) if match else None
1216
1216
 
1217
1217
 
1218
+ # The ORIGIN task a reviewer spawn is stamped with (`--task`, issue #153; the
1219
+ # Studio cost meter's lane L4). First from the review task's CR2 title --
1220
+ # `Review PR <org>/<repo>#<n> (TASK-<origin>)` -- then from the PR body, the
1221
+ # way devloop's `pr_origin_task_ref` reads it (issue #149 there): the
1222
+ # `Alissa-Task:` trailer, then the worker's own `Origin task:` / `Origin Alissa
1223
+ # task:` line (both spellings are on live bodies). There is deliberately NO
1224
+ # `Implementation task:` fallback -- the stamp claims the session serves the
1225
+ # origin, and a downstream task is not one. A plan's review title carries a
1226
+ # plan id, not a task, so a plan PR normally resolves to None.
1227
+ _REVIEW_TITLE_ORIGIN_RE = re.compile(r"\(\s*TASK-(\d+)\s*\)", re.IGNORECASE)
1228
+ _BODY_ORIGIN_TASK_RES = tuple(
1229
+ re.compile(rf"{key}\**\s*:\**\s*`?TASK-(\d+)", re.IGNORECASE)
1230
+ for key in (r"Alissa-Task", r"Origin(?:\s+Alissa)?\s+task")
1231
+ )
1232
+
1233
+
1234
+ def pr_origin_task_ref(body: str) -> "str | None":
1235
+ """`TASK-<n>` for the origin task a PR body names, or None."""
1236
+ text = body or ""
1237
+ for pattern in _BODY_ORIGIN_TASK_RES:
1238
+ match = pattern.search(text)
1239
+ if match:
1240
+ return f"TASK-{match.group(1)}"
1241
+ return None
1242
+
1243
+
1244
+ def review_origin_task_ref(task: "Task | None", pr: PullRequest) -> "str | None":
1245
+ """The origin `TASK-<n>` a reviewer round serves, or None -- in which case
1246
+ the spawn is stamped with `--repo` alone. The review task's title wins
1247
+ over the PR body: it is the one the CR2 convention writes, and a round
1248
+ with no review task yet (spawn_anyway) still has the body to go on."""
1249
+ if task is not None:
1250
+ match = _REVIEW_TITLE_ORIGIN_RE.search(task.title or "")
1251
+ if match:
1252
+ return f"TASK-{match.group(1)}"
1253
+ return pr_origin_task_ref(pr.body)
1254
+
1255
+
1218
1256
  def plan_review_task_title(pr: PullRequest) -> str:
1219
1257
  """The CR2 title of a plan PR's review task (design §6.1)."""
1220
1258
  return f"Review plan {pr.slug} ({plan_id_of(pr) or '<plan id>'})"
@@ -5851,7 +5889,8 @@ class ReviewWatcher:
5851
5889
  directive=directive,
5852
5890
  cwd=hub,
5853
5891
  agent=self.config.agent_profile,
5854
- task_ref=task.ref if task else None,
5892
+ task_ref=review_origin_task_ref(task, pr),
5893
+ repo=pr.full_name,
5855
5894
  dry_run=self.config.dry_run,
5856
5895
  )
5857
5896
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alissa-tools-github-revloop
3
- Version: 0.31.7
3
+ Version: 0.31.9
4
4
  Summary: ALISSA-TOOLS-GITHUB-REVLOOP
5
5
  Home-page: https://alissa.app
6
6
  Author: Fahera