File size: 2,589 Bytes
2b16e51
feb1b1c
 
2b16e51
 
 
 
 
 
 
 
feb1b1c
 
2b16e51
feb1b1c
2b16e51
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66

from __future__ import annotations

import hashlib
from dataclasses import dataclass
from datetime import date
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from redstack.config.schema import RedstackConfig

__all__: tuple[str, ...] = (
    "RunContext",
    "canonical_config_hash",
)


def canonical_config_hash(config: RedstackConfig) -> str:
    """Return a deterministic sha256 over the validated configuration.

    The hash is taken over Pydantic's canonical JSON serialization with keys
    sorted, so it is stable across process restarts and insertion order. Enum
    values serialize by value (``StrEnum``), matching the determinism policy.
    This feeds the run report's ``reproducible.config_hash`` (Ports §13) and the
    offline staleness key's ``config_slice`` (Offline Pipeline Part 11).
    """
    payload = config.model_dump_json(by_alias=False)
    sorted_payload = _canonicalize_json(payload)
    return hashlib.sha256(sorted_payload.encode("utf-8")).hexdigest()


def _canonicalize_json(payload: str) -> str:
    """Re-serialize a JSON string with sorted keys and no incidental whitespace."""
    import json

    parsed: object = json.loads(payload)
    return json.dumps(parsed, sort_keys=True, separators=(",", ":"), ensure_ascii=False)


@dataclass(frozen=True, slots=True, kw_only=True)
class RunContext:
    """Immutable base carrier: resolved config + reproducibility provenance.

    Subclasses (``OfflinePipelineContext`` and the online ``OnlineRunContext``)
    add their bound ports and output roots. The base owns only what both share
    and what the run report's ``reproducible`` block requires.

    Attributes:
        config: The fully-composed, validated typed configuration. Raw ``dict``
            config never crosses a module boundary (Architecture §20).
        seed: The run seed sourced from ``config.determinism.seed`` (offline may
            also override from ``config.offline.seed``); the single root of every
            seeded substream. Online ranking has no legitimate RNG.
        as_of: The injected reference date. No module reads the OS clock for
            logic; recency math takes this value (Repository Layout §0).
        code_version: The build's code provenance (e.g. ``git describe``),
            recorded into the report so a submission is traceable to its source.
        config_hash: Deterministic sha256 of ``config`` (see
            :func:`canonical_config_hash`), pinned at construction.
    """

    config: RedstackConfig
    seed: int
    as_of: date
    code_version: str
    config_hash: str