alissa-tools-github-revloop 0.29.0__tar.gz → 0.30.1__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.
- {alissa_tools_github_revloop-0.29.0/src/main/alissa_tools_github_revloop.egg-info → alissa_tools_github_revloop-0.30.1}/PKG-INFO +1 -1
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/src/main/alissa/tools/github/revloop/__main__.py +29 -0
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/src/main/alissa/tools/github/revloop/alissa_client.py +25 -2
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/src/main/alissa/tools/github/revloop/config.py +86 -13
- alissa_tools_github_revloop-0.30.1/src/main/alissa/tools/github/revloop/fleet_vitals.py +504 -0
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/src/main/alissa/tools/github/revloop/ghclient.py +82 -0
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/src/main/alissa/tools/github/revloop/loop.py +301 -3
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/src/main/alissa/tools/github/revloop/state.py +71 -0
- alissa_tools_github_revloop-0.30.1/src/main/alissa/tools/github/revloop/version +1 -0
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/src/main/alissa/tools/github/revloop/webui/sources.py +58 -9
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/src/main/alissa/tools/github/revloop/webui/sysinfo.py +19 -0
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1/src/main/alissa_tools_github_revloop.egg-info}/PKG-INFO +1 -1
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/src/main/alissa_tools_github_revloop.egg-info/SOURCES.txt +1 -0
- alissa_tools_github_revloop-0.29.0/src/main/alissa/tools/github/revloop/version +0 -1
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/LICENSE +0 -0
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/MANIFEST.in +0 -0
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/NOTICE +0 -0
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/README.md +0 -0
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/requirements.txt +0 -0
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/setup.cfg +0 -0
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/setup.py +0 -0
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/src/main/alissa/tools/github/revloop/__init__.py +0 -0
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/src/main/alissa/tools/github/revloop/alissa.py +0 -0
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/src/main/alissa/tools/github/revloop/bows.py +0 -0
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/src/main/alissa/tools/github/revloop/loop_events.py +0 -0
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/src/main/alissa/tools/github/revloop/proc.py +0 -0
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/src/main/alissa/tools/github/revloop/prreview.py +0 -0
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/src/main/alissa/tools/github/revloop/version.py +0 -0
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/src/main/alissa/tools/github/revloop/webui/__init__.py +0 -0
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/src/main/alissa/tools/github/revloop/webui/__main__.py +0 -0
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/src/main/alissa/tools/github/revloop/webui/auth.py +0 -0
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/src/main/alissa/tools/github/revloop/webui/page.py +0 -0
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/src/main/alissa/tools/github/revloop/webui/server.py +0 -0
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/src/main/alissa_tools_github_revloop.egg-info/dependency_links.txt +0 -0
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/src/main/alissa_tools_github_revloop.egg-info/entry_points.txt +0 -0
- {alissa_tools_github_revloop-0.29.0 → alissa_tools_github_revloop-0.30.1}/src/main/alissa_tools_github_revloop.egg-info/top_level.txt +0 -0
|
@@ -13,6 +13,7 @@ from .alissa_client import AlissaClient, AlissaError
|
|
|
13
13
|
from .config import (
|
|
14
14
|
BOW_OWNERS_ENV,
|
|
15
15
|
BOWS_REFRESH_POLLS_ENV,
|
|
16
|
+
FLEET_VITALS_ENV,
|
|
16
17
|
HUB_ADD,
|
|
17
18
|
HUB_SKIP,
|
|
18
19
|
LOOP_EVENTS_ENV,
|
|
@@ -200,6 +201,15 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
200
201
|
"evidence; 0 queues immediately and relies on the directive alone",
|
|
201
202
|
)
|
|
202
203
|
|
|
204
|
+
over.add_argument(
|
|
205
|
+
"--verdict-cooldown-s",
|
|
206
|
+
type=int,
|
|
207
|
+
metavar="SECONDS",
|
|
208
|
+
help="how long after a verdict lands on a head no round may be queued "
|
|
209
|
+
"on that same head, whatever the PR's requested_reviewers snapshot "
|
|
210
|
+
"says; 0 leaves the review_requested timeline check to decide alone",
|
|
211
|
+
)
|
|
212
|
+
|
|
203
213
|
over.add_argument(
|
|
204
214
|
"--review-task-miss-ttl-polls",
|
|
205
215
|
type=int,
|
|
@@ -253,6 +263,23 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
253
263
|
action="store_false",
|
|
254
264
|
help="do not push loop telemetry even if the config enables it",
|
|
255
265
|
)
|
|
266
|
+
vitals = over.add_mutually_exclusive_group()
|
|
267
|
+
vitals.add_argument(
|
|
268
|
+
"--fleet-vitals",
|
|
269
|
+
dest="fleet_vitals_enabled",
|
|
270
|
+
action="store_true",
|
|
271
|
+
default=None,
|
|
272
|
+
help="push one fleet-vitals snapshot (heartbeat, sessions, inbox, "
|
|
273
|
+
"rate, memory) to Studio's POST /v1/loop/fleet-vitals at the end of "
|
|
274
|
+
"every completed poll pass — best-effort, never fatal. "
|
|
275
|
+
f"Overridden by ${FLEET_VITALS_ENV}",
|
|
276
|
+
)
|
|
277
|
+
vitals.add_argument(
|
|
278
|
+
"--no-fleet-vitals",
|
|
279
|
+
dest="fleet_vitals_enabled",
|
|
280
|
+
action="store_false",
|
|
281
|
+
help="do not push fleet vitals even if the config enables it",
|
|
282
|
+
)
|
|
256
283
|
over.add_argument(
|
|
257
284
|
"--alissa-endpoint",
|
|
258
285
|
dest="alissa_endpoint",
|
|
@@ -303,10 +330,12 @@ def overrides_from(args: argparse.Namespace) -> dict:
|
|
|
303
330
|
"max_concurrent_sessions": args.max_concurrent_sessions,
|
|
304
331
|
"checks_wait_seconds": args.checks_wait_seconds,
|
|
305
332
|
"checks_spawn_wait_seconds": args.checks_spawn_wait_seconds,
|
|
333
|
+
"verdict_cooldown_s": args.verdict_cooldown_s,
|
|
306
334
|
"review_task_miss_ttl_polls": args.review_task_miss_ttl_polls,
|
|
307
335
|
"task_list_self_scope": args.task_list_self_scope,
|
|
308
336
|
"task_list_bow_id": args.task_list_bow_id,
|
|
309
337
|
"loop_events_enabled": args.loop_events_enabled,
|
|
338
|
+
"fleet_vitals_enabled": args.fleet_vitals_enabled,
|
|
310
339
|
"alissa_endpoint": args.alissa_endpoint,
|
|
311
340
|
"dry_run": args.dry_run,
|
|
312
341
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
"""Alissa REST access — loop telemetry's
|
|
2
|
-
reads `repos_source: bows` needs
|
|
1
|
+
"""Alissa REST access — loop telemetry's write (issue #112), the fleet-vitals
|
|
2
|
+
write beside it (issue #126) and the two reads `repos_source: bows` needs
|
|
3
|
+
(issue #119).
|
|
3
4
|
|
|
4
5
|
This is the SECOND Alissa adapter in the package, and the split is deliberate.
|
|
5
6
|
`alissa.py` shells out to the `alissa` CLI, which is the daemon's established
|
|
@@ -62,6 +63,12 @@ ENV_TOKEN = "ALISSA_API_TOKEN"
|
|
|
62
63
|
# rather than silently posting a request the API will 400.
|
|
63
64
|
MAX_EVENTS_PER_POST = 200
|
|
64
65
|
|
|
66
|
+
# The `error` code a 403 carries when the token's user has not activated the
|
|
67
|
+
# stacked app that owns the loop ingest. Advisory (classification keys on the
|
|
68
|
+
# status), but the vitals pusher reads it to warn ONCE with the activate URL
|
|
69
|
+
# instead of once per pass.
|
|
70
|
+
NOT_ACTIVATED = "not_activated"
|
|
71
|
+
|
|
65
72
|
|
|
66
73
|
class _RefuseRedirects(urllib.request.HTTPRedirectHandler):
|
|
67
74
|
"""Refuse every redirect instead of following it (PR #113 round 1).
|
|
@@ -246,6 +253,22 @@ class AlissaClient:
|
|
|
246
253
|
payload = self._request("/v1/loop-events", {"events": events})
|
|
247
254
|
return payload if isinstance(payload, dict) else {}
|
|
248
255
|
|
|
256
|
+
def post_fleet_vitals(self, snapshot: dict) -> dict:
|
|
257
|
+
"""Replace this seat's fleet-vitals snapshot (`POST
|
|
258
|
+
/v1/loop/fleet-vitals`, issue #126).
|
|
259
|
+
|
|
260
|
+
One body, one seat, replaced on every call — there is no batching
|
|
261
|
+
and no dedupe key, because the API keeps only the LATEST snapshot per
|
|
262
|
+
user×seat (loop events are the history). Returns the API's
|
|
263
|
+
`{"seat", "receivedAt", "replaced"}` payload (empty dict when the
|
|
264
|
+
body was empty), for the caller's debug line. The API is strict —
|
|
265
|
+
unknown keys 400, lists cap at 50, the body at 64 KB — and the
|
|
266
|
+
BUILDER (`fleet_vitals`) owns fitting the snapshot to that, so this
|
|
267
|
+
method sends exactly what it is handed.
|
|
268
|
+
"""
|
|
269
|
+
payload = self._request("/v1/loop/fleet-vitals", snapshot)
|
|
270
|
+
return payload if isinstance(payload, dict) else {}
|
|
271
|
+
|
|
249
272
|
def ping(self) -> Identity:
|
|
250
273
|
"""The identity this token acts as (`GET /v1/ping`).
|
|
251
274
|
|
|
@@ -15,6 +15,9 @@ site. `loop_events_enabled` (`ALISSA_REV_LOOP_EVENTS_ENABLED`, see
|
|
|
15
15
|
`env_loop_events_enabled`) has it because a container deployment toggles
|
|
16
16
|
telemetry with one variable and no config-file edit (issue #112); the env wins
|
|
17
17
|
over both other layers so the two env-backed keys share one precedence story.
|
|
18
|
+
`fleet_vitals_enabled` (`ALISSA_REV_FLEET_VITALS_ENABLED`, see
|
|
19
|
+
`env_fleet_vitals_enabled`) is the second Studio push and rides the same rail
|
|
20
|
+
through the same boolean reader (issue #126).
|
|
18
21
|
The three `repos_source: bows` keys (issue #119) ride the same rail for the
|
|
19
22
|
same container reason: `repos_source` (`ALISSA_REVIEW_REPOS_SOURCE`),
|
|
20
23
|
`bows_refresh_polls` (`ALISSA_REVIEW_BOWS_REFRESH_POLLS`) and `bow_owners`
|
|
@@ -187,10 +190,12 @@ CONFIG_KEYS = (
|
|
|
187
190
|
"max_concurrent_sessions",
|
|
188
191
|
"checks_wait_seconds",
|
|
189
192
|
"checks_spawn_wait_seconds",
|
|
193
|
+
"verdict_cooldown_s",
|
|
190
194
|
"review_task_miss_ttl_polls",
|
|
191
195
|
"task_list_self_scope",
|
|
192
196
|
"task_list_bow_id",
|
|
193
197
|
"loop_events_enabled",
|
|
198
|
+
"fleet_vitals_enabled",
|
|
194
199
|
"alissa_endpoint",
|
|
195
200
|
"dry_run",
|
|
196
201
|
)
|
|
@@ -206,6 +211,11 @@ TASK_LIST_BOW_ENV = "ALISSA_REVIEW_TASK_BOW"
|
|
|
206
211
|
# module docstring for the shared precedence story.
|
|
207
212
|
LOOP_EVENTS_ENV = "ALISSA_REV_LOOP_EVENTS_ENABLED"
|
|
208
213
|
|
|
214
|
+
# The environment variable toggling the fleet-vitals push (issue #126): one
|
|
215
|
+
# snapshot of this daemon's live state per completed pass, to Studio's
|
|
216
|
+
# `POST /v1/loop/fleet-vitals`. Same rail and same reader as LOOP_EVENTS_ENV.
|
|
217
|
+
FLEET_VITALS_ENV = "ALISSA_REV_FLEET_VITALS_ENABLED"
|
|
218
|
+
|
|
209
219
|
# The three `repos_source: bows` rails (issue #119). Same precedence as the two
|
|
210
220
|
# above -- env > file > flag -- and the same blank-falls-through rule, because
|
|
211
221
|
# the container bakes every knob as an ENV that renders `""` when unset.
|
|
@@ -258,18 +268,21 @@ def _validate_alissa_endpoint(endpoint: str) -> str:
|
|
|
258
268
|
)
|
|
259
269
|
|
|
260
270
|
|
|
261
|
-
def
|
|
262
|
-
environ: "Mapping[str, str] | None" = None,
|
|
271
|
+
def _env_bool(
|
|
272
|
+
name: str, environ: "Mapping[str, str] | None" = None,
|
|
263
273
|
) -> "bool | None":
|
|
264
|
-
"""
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
274
|
+
"""One boolean env rail: True/False, or None when unset.
|
|
275
|
+
|
|
276
|
+
Shared by every boolean the environment can set (`LOOP_EVENTS_ENV`,
|
|
277
|
+
`FLEET_VITALS_ENV`) so the accepted spellings and the refusal are one
|
|
278
|
+
rule, not two copies that drift (issue #126). None and empty are the SAME
|
|
279
|
+
answer — an exported-but-empty variable is how a container renders
|
|
280
|
+
"unset" (the Dockerfile bakes empty ENV defaults), and it must fall
|
|
281
|
+
through to the file/CLI layers rather than read as false. A non-boolean
|
|
282
|
+
spelling raises, naming the variable: the startup phase turns a
|
|
283
|
+
ValueError into `config error` + exit 2, which is where a typo belongs.
|
|
271
284
|
"""
|
|
272
|
-
raw = (os.environ if environ is None else environ).get(
|
|
285
|
+
raw = (os.environ if environ is None else environ).get(name)
|
|
273
286
|
value = (raw or "").strip().lower()
|
|
274
287
|
if not value:
|
|
275
288
|
return None
|
|
@@ -278,11 +291,27 @@ def env_loop_events_enabled(
|
|
|
278
291
|
if value in _ENV_FALSE:
|
|
279
292
|
return False
|
|
280
293
|
raise ValueError(
|
|
281
|
-
f"{
|
|
294
|
+
f"{name} must be a boolean "
|
|
282
295
|
f"(1/0, true/false, yes/no, on/off), got {raw!r}"
|
|
283
296
|
)
|
|
284
297
|
|
|
285
298
|
|
|
299
|
+
def env_loop_events_enabled(
|
|
300
|
+
environ: "Mapping[str, str] | None" = None,
|
|
301
|
+
) -> "bool | None":
|
|
302
|
+
"""The loop-events toggle from the environment, or None when unset —
|
|
303
|
+
`_env_bool` over LOOP_EVENTS_ENV."""
|
|
304
|
+
return _env_bool(LOOP_EVENTS_ENV, environ)
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def env_fleet_vitals_enabled(
|
|
308
|
+
environ: "Mapping[str, str] | None" = None,
|
|
309
|
+
) -> "bool | None":
|
|
310
|
+
"""The fleet-vitals toggle from the environment, or None when unset —
|
|
311
|
+
`_env_bool` over FLEET_VITALS_ENV (issue #126)."""
|
|
312
|
+
return _env_bool(FLEET_VITALS_ENV, environ)
|
|
313
|
+
|
|
314
|
+
|
|
286
315
|
def env_task_list_bow_id(environ: "Mapping[str, str] | None" = None) -> "str | None":
|
|
287
316
|
"""The review BOW id from the environment, or None when it is not set.
|
|
288
317
|
|
|
@@ -511,6 +540,24 @@ DEFAULT_CHECKS_WAIT_SECONDS = 30 * 60
|
|
|
511
540
|
# still-running rollup, which is the directive-only posture.
|
|
512
541
|
DEFAULT_CHECKS_SPAWN_WAIT_SECONDS = 15 * 60
|
|
513
542
|
|
|
543
|
+
# How long, after a verdict lands on a head, NO round may be queued on that same
|
|
544
|
+
# head -- whatever the PR's `requested_reviewers` snapshot says (issue #128).
|
|
545
|
+
#
|
|
546
|
+
# GitHub consumes a review request the moment the requested identity submits a
|
|
547
|
+
# review, but not atomically with the daemon's reads: on studio #1243 a
|
|
548
|
+
# `request_changes` verdict landed at 14:35:32 and the poll ten seconds later
|
|
549
|
+
# still saw the reviewer in `requested_reviewers`, queued round 2 on the SAME
|
|
550
|
+
# head, and that phantom round bounced on the CR8 triage gate -- which made the
|
|
551
|
+
# devloop spawn a second fix session on one branch. The cooldown is the blunt
|
|
552
|
+
# guard against that propagation lag: two polls' worth at the default cadence,
|
|
553
|
+
# long enough for the request the verdict consumed to be gone from every read.
|
|
554
|
+
# The precise guard is the timeline check in loop._admit_round, which admits a
|
|
555
|
+
# same-head round only on a `review_requested` event NEWER than the verdict;
|
|
556
|
+
# the cooldown spares that check a GitHub call in exactly the window where the
|
|
557
|
+
# stale snapshot is most likely. 0 disables the cooldown and leaves the timeline
|
|
558
|
+
# check to decide alone.
|
|
559
|
+
DEFAULT_VERDICT_COOLDOWN_SECONDS = 120
|
|
560
|
+
|
|
514
561
|
# How many polls a PR with NO review task may be taken on trust before the
|
|
515
562
|
# daemon searches the task corpus for one again (issue #87).
|
|
516
563
|
#
|
|
@@ -750,6 +797,12 @@ class Config:
|
|
|
750
797
|
# timer to configure.
|
|
751
798
|
checks_spawn_wait_seconds: int = DEFAULT_CHECKS_SPAWN_WAIT_SECONDS
|
|
752
799
|
|
|
800
|
+
# How long after a verdict lands on a head no round may be queued on that
|
|
801
|
+
# same head, regardless of the PR's `requested_reviewers` snapshot; see
|
|
802
|
+
# DEFAULT_VERDICT_COOLDOWN_SECONDS. 0 is legal and means "no cooldown": the
|
|
803
|
+
# timeline check alone decides whether a same-head round is admitted.
|
|
804
|
+
verdict_cooldown_s: int = DEFAULT_VERDICT_COOLDOWN_SECONDS
|
|
805
|
+
|
|
753
806
|
# How many polls a PR with no review task is taken on trust before the
|
|
754
807
|
# corpus is searched again; see DEFAULT_REVIEW_TASK_MISS_TTL_POLLS. Floor 1
|
|
755
808
|
# -- there is no value that turns the negative cache off.
|
|
@@ -786,6 +839,15 @@ class Config:
|
|
|
786
839
|
# when on: a failed push is one WARN and the pass completes.
|
|
787
840
|
loop_events_enabled: bool = False
|
|
788
841
|
|
|
842
|
+
# Whether the loop pushes one FLEET-VITALS snapshot — heartbeat, poll
|
|
843
|
+
# durations, the reviewer-session roster, rate, memory, queue depth, the
|
|
844
|
+
# operator inbox — to Studio's `POST /v1/loop/fleet-vitals` at the end
|
|
845
|
+
# of every completed pass (issue #126), so a Factory with no console URL
|
|
846
|
+
# for this seat can still render its card. OFF by default for the same
|
|
847
|
+
# reason `loop_events_enabled` is; toggled by FLEET_VITALS_ENV above the
|
|
848
|
+
# file and the CLI; best-effort when on (one WARN per failed pass).
|
|
849
|
+
fleet_vitals_enabled: bool = False
|
|
850
|
+
|
|
789
851
|
# The Alissa/Studio API base the loop-events client posts to. One knob so
|
|
790
852
|
# a staging deployment can point telemetry somewhere else; everything else
|
|
791
853
|
# about the client (its bearer token) comes from the environment.
|
|
@@ -884,8 +946,8 @@ class Config:
|
|
|
884
946
|
"not specified on the CLI" and fall through to the file / defaults.
|
|
885
947
|
|
|
886
948
|
`environ` is the fourth layer and applies to the env-backed keys
|
|
887
|
-
(`task_list_bow_id`, `loop_events_enabled`, `
|
|
888
|
-
`bows_refresh_polls`, `bow_owners`); it wins over both of the others
|
|
949
|
+
(`task_list_bow_id`, `loop_events_enabled`, `fleet_vitals_enabled`,
|
|
950
|
+
`repos_source`, `bows_refresh_polls`, `bow_owners`); it wins over both of the others
|
|
889
951
|
(see the module docstring). Defaults to the real environment, so
|
|
890
952
|
callers that do not care pass nothing.
|
|
891
953
|
"""
|
|
@@ -941,6 +1003,11 @@ class Config:
|
|
|
941
1003
|
env_events = env_loop_events_enabled(environ)
|
|
942
1004
|
if env_events is not None:
|
|
943
1005
|
raw["loop_events_enabled"] = env_events
|
|
1006
|
+
# ...and the fleet-vitals toggle (issue #126), the same rail through
|
|
1007
|
+
# the same reader.
|
|
1008
|
+
env_vitals = env_fleet_vitals_enabled(environ)
|
|
1009
|
+
if env_vitals is not None:
|
|
1010
|
+
raw["fleet_vitals_enabled"] = env_vitals
|
|
944
1011
|
|
|
945
1012
|
# The three bows rails (issue #119), same layer and same reason: the
|
|
946
1013
|
# container hands the daemon its mode with one variable. Each is
|
|
@@ -1103,6 +1170,10 @@ class Config:
|
|
|
1103
1170
|
f"checks_spawn_wait_seconds must be >= 0, got {spawn_wait}"
|
|
1104
1171
|
)
|
|
1105
1172
|
|
|
1173
|
+
cooldown = int(raw.get("verdict_cooldown_s", cls.verdict_cooldown_s))
|
|
1174
|
+
if cooldown < 0:
|
|
1175
|
+
raise ValueError(f"verdict_cooldown_s must be >= 0, got {cooldown}")
|
|
1176
|
+
|
|
1106
1177
|
miss_ttl = int(
|
|
1107
1178
|
raw.get("review_task_miss_ttl_polls", cls.review_task_miss_ttl_polls)
|
|
1108
1179
|
)
|
|
@@ -1171,10 +1242,12 @@ class Config:
|
|
|
1171
1242
|
max_concurrent_sessions=max_sessions,
|
|
1172
1243
|
checks_wait_seconds=checks_wait,
|
|
1173
1244
|
checks_spawn_wait_seconds=spawn_wait,
|
|
1245
|
+
verdict_cooldown_s=cooldown,
|
|
1174
1246
|
review_task_miss_ttl_polls=miss_ttl,
|
|
1175
1247
|
task_list_self_scope=bool(raw.get("task_list_self_scope", False)),
|
|
1176
1248
|
task_list_bow_id=bow_id,
|
|
1177
1249
|
loop_events_enabled=bool(raw.get("loop_events_enabled", False)),
|
|
1250
|
+
fleet_vitals_enabled=bool(raw.get("fleet_vitals_enabled", False)),
|
|
1178
1251
|
alissa_endpoint=endpoint,
|
|
1179
1252
|
dry_run=bool(raw.get("dry_run", False)),
|
|
1180
1253
|
)
|