alpha-engine-lib 0.43.0__tar.gz → 0.44.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.
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/PKG-INFO +1 -1
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/pyproject.toml +1 -1
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/__init__.py +1 -1
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/artifact_freshness.py +157 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib.egg-info/PKG-INFO +1 -1
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_artifact_freshness.py +113 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/README.md +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/setup.cfg +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/agent_schemas.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/alerts.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/anthropic_payload.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/arcticdb.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/collector_results.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/cost.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/dates.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/decision_capture.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/ec2_spot.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/email_sender.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/eval_artifacts.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/locks.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/logging.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/model_pricing.yaml +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/pillars.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/pipeline_status/__init__.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/pipeline_status/read.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/pipeline_status/registry.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/pipeline_status/templates.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/preflight.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/rag/__init__.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/rag/db.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/rag/embeddings.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/rag/migrations/0001_content_tsv.sql +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/rag/rerank.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/rag/retrieval.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/rag/schema.sql +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/reconcile.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/secrets.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/sources/__init__.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/sources/protocols.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/ssm_dispatcher.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/ssm_log_capture.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/telegram.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/trading_calendar.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/transparency.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/transparency_inventory.yaml +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/universe.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib.egg-info/SOURCES.txt +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib.egg-info/dependency_links.txt +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib.egg-info/requires.txt +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib.egg-info/top_level.txt +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_agent_schemas.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_alerts.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_anthropic_payload.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_arcticdb.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_collector_results.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_cost.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_dates.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_decision_capture.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_ec2_spot.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_email_sender.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_eval_artifacts.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_locks.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_logging.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_pillars.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_pipeline_status_read.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_pipeline_status_registry.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_pipeline_status_templates.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_preflight.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_rag.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_rag_rerank.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_rag_retrieval_hybrid.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_reconcile.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_secrets.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_sources_protocols.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_ssm_dispatcher.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_ssm_log_capture.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_telegram.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_trading_calendar.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_transparency.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_universe.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_version_bump_workflow.py +0 -0
- {alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/tests/test_version_pin.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: alpha-engine-lib
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.44.0
|
|
4
4
|
Summary: Shared utilities for the Alpha Engine modules: preflight, logging, ArcticDB, dates, decision capture, cost telemetry, Anthropic payload chokepoint, artifact freshness, RAG, agent schemas, SSM secrets, Telegram + SNS alerts, EC2 spot resilience, SSM log-capture, SSM dispatcher, Step-Functions execution-state projection, and S3-conditional-PUT writer locks. Full surface documented in README.
|
|
5
5
|
Author: Brian McMahon
|
|
6
6
|
License: Proprietary
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "alpha-engine-lib"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.44.0"
|
|
8
8
|
description = "Shared utilities for the Alpha Engine modules: preflight, logging, ArcticDB, dates, decision capture, cost telemetry, Anthropic payload chokepoint, artifact freshness, RAG, agent schemas, SSM secrets, Telegram + SNS alerts, EC2 spot resilience, SSM log-capture, SSM dispatcher, Step-Functions execution-state projection, and S3-conditional-PUT writer locks. Full surface documented in README."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
# EC2 still runs Python 3.9 on the always-on micro instance (boto3 drops
|
{alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/artifact_freshness.py
RENAMED
|
@@ -71,6 +71,7 @@ ships the freshness-monitor Lambda that wires the two together.
|
|
|
71
71
|
|
|
72
72
|
from __future__ import annotations
|
|
73
73
|
|
|
74
|
+
from collections.abc import Iterable
|
|
74
75
|
from dataclasses import dataclass, field
|
|
75
76
|
from datetime import date, datetime, timedelta, timezone
|
|
76
77
|
from typing import Any, Final, Literal
|
|
@@ -596,3 +597,159 @@ def _cycle_length_seconds(spec: ArtifactSpec) -> float:
|
|
|
596
597
|
assert spec.interval_minutes is not None
|
|
597
598
|
return spec.interval_minutes * 60
|
|
598
599
|
raise ValueError(f"unknown cadence {spec.cadence!r}")
|
|
600
|
+
|
|
601
|
+
|
|
602
|
+
# ── Per-cycle completion rollup ───────────────────────────────────────────────
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
CycleState = Literal["complete", "incomplete", "indeterminate"]
|
|
606
|
+
|
|
607
|
+
|
|
608
|
+
@dataclass
|
|
609
|
+
class CycleCompletion:
|
|
610
|
+
"""Per-cycle completion verdict — the artifact-union judgment.
|
|
611
|
+
|
|
612
|
+
Aggregates the per-artifact :class:`CheckResult` rows for one
|
|
613
|
+
execution cycle into a single verdict over the *required* set
|
|
614
|
+
(the ``severity="critical"`` rows). Answers the question the
|
|
615
|
+
raw orchestrator status cannot on a recovery-stitched run: *did
|
|
616
|
+
this cycle actually deliver every load-bearing artifact?*
|
|
617
|
+
|
|
618
|
+
Recovery substitution is already folded in upstream — a
|
|
619
|
+
canonical-missing artifact rescued by its ``recovery_key_template``
|
|
620
|
+
arrives here as ``state="fresh"``. So this rollup judges the
|
|
621
|
+
execution UNION without re-HEADing anything.
|
|
622
|
+
|
|
623
|
+
Attributes:
|
|
624
|
+
state: ``"complete"`` ⇒ every required artifact is present +
|
|
625
|
+
valid (``fresh``, or suppressed by ``grace_period``).
|
|
626
|
+
``"incomplete"`` ⇒ at least one required artifact is
|
|
627
|
+
``missing`` / ``stale`` (a real delivery gap).
|
|
628
|
+
``"indeterminate"`` ⇒ no real gap, but at least one probe
|
|
629
|
+
``probe_failed`` (the monitor itself is broken, so the
|
|
630
|
+
cycle can't be confirmed). A real gap outranks an
|
|
631
|
+
indeterminate probe.
|
|
632
|
+
complete: ``True`` iff ``state == "complete"``.
|
|
633
|
+
cycle_label: The cycle's window label (e.g. ``"2026-W22"``),
|
|
634
|
+
for reporting. Informational — the caller passes it.
|
|
635
|
+
n_required: Count of ``severity="critical"`` artifacts judged.
|
|
636
|
+
n_satisfied: Count present + valid (``fresh`` + ``grace_period``).
|
|
637
|
+
missing / stale / probe_failed / grace_period: ``artifact_id``
|
|
638
|
+
localization lists — which artifacts landed in each state.
|
|
639
|
+
reason: Human-readable summary; routed to the report surface.
|
|
640
|
+
"""
|
|
641
|
+
|
|
642
|
+
state: CycleState
|
|
643
|
+
complete: bool
|
|
644
|
+
cycle_label: str | None = None
|
|
645
|
+
n_required: int = 0
|
|
646
|
+
n_satisfied: int = 0
|
|
647
|
+
missing: list[str] = field(default_factory=list)
|
|
648
|
+
stale: list[str] = field(default_factory=list)
|
|
649
|
+
probe_failed: list[str] = field(default_factory=list)
|
|
650
|
+
grace_period: list[str] = field(default_factory=list)
|
|
651
|
+
reason: str = ""
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
def cycle_completion(
|
|
655
|
+
spec_results: Iterable[tuple[ArtifactSpec, CheckResult]],
|
|
656
|
+
*,
|
|
657
|
+
cycle_label: str | None = None,
|
|
658
|
+
) -> CycleCompletion:
|
|
659
|
+
"""Roll per-artifact freshness results up into one cycle verdict.
|
|
660
|
+
|
|
661
|
+
``cycle_completion(C) = ∀ required artifact a: present(a@C) ∧ valid(a@C)``
|
|
662
|
+
over the execution UNION, where the required set is the
|
|
663
|
+
``severity="critical"`` rows. Non-critical (``warning``) artifacts
|
|
664
|
+
are excluded — they inform per-artifact alerting but never gate the
|
|
665
|
+
cycle verdict.
|
|
666
|
+
|
|
667
|
+
Pure: consumes already-computed :class:`CheckResult` rows (as
|
|
668
|
+
``(spec, result)`` pairs so there's no positional-pairing hazard)
|
|
669
|
+
and performs no I/O. Recovery substitution and the calendar-holiday
|
|
670
|
+
short-circuit are already reflected in each ``result.state`` by
|
|
671
|
+
:func:`check_freshness`, so a holiday cycle or a recovery-rescued
|
|
672
|
+
artifact both count as satisfied here.
|
|
673
|
+
|
|
674
|
+
State precedence: a real delivery gap (``missing`` / ``stale``)
|
|
675
|
+
outranks a broken probe (``probe_failed``) — a confirmed miss is
|
|
676
|
+
more actionable than an unconfirmable one. ``grace_period`` counts
|
|
677
|
+
as satisfied (the producer is newly onboarded; suppressed by design)
|
|
678
|
+
but is surfaced in its own list so the caller can see it.
|
|
679
|
+
|
|
680
|
+
An empty required set returns ``state="complete"`` (vacuous truth) —
|
|
681
|
+
a cycle with no critical artifacts cannot be incomplete.
|
|
682
|
+
"""
|
|
683
|
+
required = [(s, r) for s, r in spec_results if s.severity == "critical"]
|
|
684
|
+
|
|
685
|
+
missing: list[str] = []
|
|
686
|
+
stale: list[str] = []
|
|
687
|
+
probe_failed: list[str] = []
|
|
688
|
+
grace_period: list[str] = []
|
|
689
|
+
satisfied = 0
|
|
690
|
+
|
|
691
|
+
for spec, res in required:
|
|
692
|
+
if res.state == "fresh":
|
|
693
|
+
satisfied += 1
|
|
694
|
+
elif res.state == "grace_period":
|
|
695
|
+
satisfied += 1
|
|
696
|
+
grace_period.append(spec.artifact_id)
|
|
697
|
+
elif res.state == "stale":
|
|
698
|
+
stale.append(spec.artifact_id)
|
|
699
|
+
elif res.state == "missing":
|
|
700
|
+
missing.append(spec.artifact_id)
|
|
701
|
+
elif res.state == "probe_failed":
|
|
702
|
+
probe_failed.append(spec.artifact_id)
|
|
703
|
+
|
|
704
|
+
n_required = len(required)
|
|
705
|
+
|
|
706
|
+
if missing or stale:
|
|
707
|
+
gaps = []
|
|
708
|
+
if missing:
|
|
709
|
+
gaps.append(f"missing={missing}")
|
|
710
|
+
if stale:
|
|
711
|
+
gaps.append(f"stale={stale}")
|
|
712
|
+
return CycleCompletion(
|
|
713
|
+
state="incomplete",
|
|
714
|
+
complete=False,
|
|
715
|
+
cycle_label=cycle_label,
|
|
716
|
+
n_required=n_required,
|
|
717
|
+
n_satisfied=satisfied,
|
|
718
|
+
missing=missing,
|
|
719
|
+
stale=stale,
|
|
720
|
+
probe_failed=probe_failed,
|
|
721
|
+
grace_period=grace_period,
|
|
722
|
+
reason=(
|
|
723
|
+
f"cycle incomplete: {satisfied}/{n_required} critical artifacts "
|
|
724
|
+
f"present+valid; " + "; ".join(gaps)
|
|
725
|
+
),
|
|
726
|
+
)
|
|
727
|
+
|
|
728
|
+
if probe_failed:
|
|
729
|
+
return CycleCompletion(
|
|
730
|
+
state="indeterminate",
|
|
731
|
+
complete=False,
|
|
732
|
+
cycle_label=cycle_label,
|
|
733
|
+
n_required=n_required,
|
|
734
|
+
n_satisfied=satisfied,
|
|
735
|
+
probe_failed=probe_failed,
|
|
736
|
+
grace_period=grace_period,
|
|
737
|
+
reason=(
|
|
738
|
+
f"cycle indeterminate: monitor probe failed for {probe_failed} — "
|
|
739
|
+
f"cannot confirm cycle ({satisfied}/{n_required} confirmed fresh)"
|
|
740
|
+
),
|
|
741
|
+
)
|
|
742
|
+
|
|
743
|
+
grace_note = f" ({len(grace_period)} in grace period)" if grace_period else ""
|
|
744
|
+
return CycleCompletion(
|
|
745
|
+
state="complete",
|
|
746
|
+
complete=True,
|
|
747
|
+
cycle_label=cycle_label,
|
|
748
|
+
n_required=n_required,
|
|
749
|
+
n_satisfied=satisfied,
|
|
750
|
+
grace_period=grace_period,
|
|
751
|
+
reason=(
|
|
752
|
+
f"cycle complete: all {n_required} critical artifacts present+valid"
|
|
753
|
+
+ grace_note
|
|
754
|
+
),
|
|
755
|
+
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: alpha-engine-lib
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.44.0
|
|
4
4
|
Summary: Shared utilities for the Alpha Engine modules: preflight, logging, ArcticDB, dates, decision capture, cost telemetry, Anthropic payload chokepoint, artifact freshness, RAG, agent schemas, SSM secrets, Telegram + SNS alerts, EC2 spot resilience, SSM log-capture, SSM dispatcher, Step-Functions execution-state projection, and S3-conditional-PUT writer locks. Full surface documented in README.
|
|
5
5
|
Author: Brian McMahon
|
|
6
6
|
License: Proprietary
|
|
@@ -41,7 +41,9 @@ from alpha_engine_lib.artifact_freshness import (
|
|
|
41
41
|
ArtifactSpec,
|
|
42
42
|
CADENCE_SYMBOLS,
|
|
43
43
|
CheckResult,
|
|
44
|
+
CycleCompletion,
|
|
44
45
|
check_freshness,
|
|
46
|
+
cycle_completion,
|
|
45
47
|
resolve_current_cycle,
|
|
46
48
|
resolve_dedup_key,
|
|
47
49
|
)
|
|
@@ -510,3 +512,114 @@ def test_cadence_symbols_match_documented_set():
|
|
|
510
512
|
assert CADENCE_SYMBOLS == frozenset(
|
|
511
513
|
{"saturday_sf", "weekday_sf", "eod_sf", "continuous"}
|
|
512
514
|
)
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
# ── Per-cycle completion rollup (Phase 1b) ──────────────────────────────────
|
|
518
|
+
|
|
519
|
+
|
|
520
|
+
def _critical(artifact_id: str) -> ArtifactSpec:
|
|
521
|
+
return _spec(artifact_id=artifact_id, severity="critical")
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
def _warning(artifact_id: str) -> ArtifactSpec:
|
|
525
|
+
return _spec(artifact_id=artifact_id, severity="warning")
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
def _res(state: str) -> CheckResult:
|
|
529
|
+
return CheckResult(state=state, reason=f"test {state}")
|
|
530
|
+
|
|
531
|
+
|
|
532
|
+
class TestCycleCompletion:
|
|
533
|
+
def test_all_critical_fresh_is_complete(self):
|
|
534
|
+
pairs = [
|
|
535
|
+
(_critical("a"), _res("fresh")),
|
|
536
|
+
(_critical("b"), _res("fresh")),
|
|
537
|
+
(_critical("c"), _res("fresh")),
|
|
538
|
+
]
|
|
539
|
+
v = cycle_completion(pairs, cycle_label="2026-W22")
|
|
540
|
+
assert isinstance(v, CycleCompletion)
|
|
541
|
+
assert v.state == "complete"
|
|
542
|
+
assert v.complete is True
|
|
543
|
+
assert v.n_required == 3
|
|
544
|
+
assert v.n_satisfied == 3
|
|
545
|
+
assert v.cycle_label == "2026-W22"
|
|
546
|
+
|
|
547
|
+
def test_one_missing_is_incomplete(self):
|
|
548
|
+
v = cycle_completion([
|
|
549
|
+
(_critical("a"), _res("fresh")),
|
|
550
|
+
(_critical("b"), _res("missing")),
|
|
551
|
+
])
|
|
552
|
+
assert v.state == "incomplete"
|
|
553
|
+
assert v.complete is False
|
|
554
|
+
assert v.missing == ["b"]
|
|
555
|
+
assert v.n_satisfied == 1
|
|
556
|
+
|
|
557
|
+
def test_one_stale_is_incomplete(self):
|
|
558
|
+
v = cycle_completion([
|
|
559
|
+
(_critical("a"), _res("fresh")),
|
|
560
|
+
(_critical("b"), _res("stale")),
|
|
561
|
+
])
|
|
562
|
+
assert v.state == "incomplete"
|
|
563
|
+
assert v.stale == ["b"]
|
|
564
|
+
|
|
565
|
+
def test_probe_failed_only_is_indeterminate(self):
|
|
566
|
+
v = cycle_completion([
|
|
567
|
+
(_critical("a"), _res("fresh")),
|
|
568
|
+
(_critical("b"), _res("probe_failed")),
|
|
569
|
+
])
|
|
570
|
+
assert v.state == "indeterminate"
|
|
571
|
+
assert v.complete is False
|
|
572
|
+
assert v.probe_failed == ["b"]
|
|
573
|
+
|
|
574
|
+
def test_real_gap_outranks_probe_failure(self):
|
|
575
|
+
"""A confirmed miss is more actionable than an unconfirmable probe."""
|
|
576
|
+
v = cycle_completion([
|
|
577
|
+
(_critical("a"), _res("missing")),
|
|
578
|
+
(_critical("b"), _res("probe_failed")),
|
|
579
|
+
])
|
|
580
|
+
assert v.state == "incomplete"
|
|
581
|
+
assert v.missing == ["a"]
|
|
582
|
+
assert v.probe_failed == ["b"] # still localized, but doesn't set the verdict
|
|
583
|
+
|
|
584
|
+
def test_grace_period_counts_as_satisfied(self):
|
|
585
|
+
v = cycle_completion([
|
|
586
|
+
(_critical("a"), _res("fresh")),
|
|
587
|
+
(_critical("b"), _res("grace_period")),
|
|
588
|
+
])
|
|
589
|
+
assert v.state == "complete"
|
|
590
|
+
assert v.complete is True
|
|
591
|
+
assert v.n_satisfied == 2
|
|
592
|
+
assert v.grace_period == ["b"]
|
|
593
|
+
|
|
594
|
+
def test_warning_severity_excluded_from_required_set(self):
|
|
595
|
+
"""A missing WARNING artifact must not fail the cycle — only
|
|
596
|
+
critical rows gate the completion verdict."""
|
|
597
|
+
v = cycle_completion([
|
|
598
|
+
(_critical("a"), _res("fresh")),
|
|
599
|
+
(_warning("b"), _res("missing")),
|
|
600
|
+
])
|
|
601
|
+
assert v.state == "complete"
|
|
602
|
+
assert v.n_required == 1
|
|
603
|
+
assert v.missing == []
|
|
604
|
+
|
|
605
|
+
def test_empty_required_set_is_vacuously_complete(self):
|
|
606
|
+
v = cycle_completion([(_warning("a"), _res("missing"))])
|
|
607
|
+
assert v.state == "complete"
|
|
608
|
+
assert v.complete is True
|
|
609
|
+
assert v.n_required == 0
|
|
610
|
+
|
|
611
|
+
def test_mixed_states_incomplete_localizes_all_gaps(self):
|
|
612
|
+
v = cycle_completion([
|
|
613
|
+
(_critical("a"), _res("fresh")),
|
|
614
|
+
(_critical("b"), _res("grace_period")),
|
|
615
|
+
(_critical("c"), _res("missing")),
|
|
616
|
+
(_critical("d"), _res("stale")),
|
|
617
|
+
(_critical("e"), _res("probe_failed")),
|
|
618
|
+
])
|
|
619
|
+
assert v.state == "incomplete"
|
|
620
|
+
assert v.n_required == 5
|
|
621
|
+
assert v.n_satisfied == 2 # fresh + grace_period
|
|
622
|
+
assert v.missing == ["c"]
|
|
623
|
+
assert v.stale == ["d"]
|
|
624
|
+
assert v.probe_failed == ["e"]
|
|
625
|
+
assert v.grace_period == ["b"]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/anthropic_payload.py
RENAMED
|
File without changes
|
|
File without changes
|
{alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/collector_results.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/decision_capture.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/pipeline_status/__init__.py
RENAMED
|
File without changes
|
{alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/pipeline_status/read.py
RENAMED
|
File without changes
|
{alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/pipeline_status/registry.py
RENAMED
|
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
|
{alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/sources/__init__.py
RENAMED
|
File without changes
|
{alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/sources/protocols.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/trading_calendar.py
RENAMED
|
File without changes
|
|
File without changes
|
{alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib/transparency_inventory.yaml
RENAMED
|
File without changes
|
|
File without changes
|
{alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib.egg-info/requires.txt
RENAMED
|
File without changes
|
{alpha_engine_lib-0.43.0 → alpha_engine_lib-0.44.0}/src/alpha_engine_lib.egg-info/top_level.txt
RENAMED
|
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
|
|
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
|
|
File without changes
|