Source code for governance.policy

"""
Script
------
policy.py

Path
----
python/hillstar/governance/policy.py

Purpose
-------
Governance policy definitions: what constitutes a valid workflow execution
for the purpose of gating git commits.

Inputs
------
None (configuration constants)

Outputs
-------
GovernancePolicy dataclass

Assumptions
-----------
- Policy is loaded from .hillstar/governance_policy.json if present,
  otherwise defaults apply.

Parameters
----------
- max_age_seconds: Maximum age of a commit_ready marker (default 3600 = 1 hour)
- allow_force_override: Whether HILLSTAR_FORCE_COMMIT env var is respected
- require_workflow_id: Whether a workflow ID must be present in the marker
- blocked_patterns: File patterns that always require a workflow (e.g. *.py, *.json)
- exempt_patterns: File patterns exempt from enforcement (e.g. *.md docs, logs)

Failure Modes
-------------
- policy.json malformed: falls back to defaults with a warning

Author: Julen Gamboa <julen.gamboa.ds@gmail.com>

Created
-------
2026-02-08

Last Edited
-----------
2026-02-08
"""

from __future__ import annotations

import json
import os
from dataclasses import dataclass, field


[docs] @dataclass class GovernancePolicy: """Policy configuration for workflow enforcement.""" max_age_seconds: int = 3600 allow_force_override: bool = True require_workflow_id: bool = True blocked_patterns: list[str] = field(default_factory=lambda: [ "*.py", "*.json", "*.tsv", "*.sh", ]) exempt_patterns: list[str] = field(default_factory=lambda: [ "*.log", "*.md", "*.txt", ])
[docs] @classmethod def load(cls, hillstar_dir: str) -> "GovernancePolicy": """Load policy from .hillstar/governance_policy.json, or return defaults.""" policy_path = os.path.join(hillstar_dir, "governance_policy.json") if not os.path.exists(policy_path): return cls() try: with open(policy_path, encoding="utf-8") as f: data = json.load(f) return cls(**{k: v for k, v in data.items() if hasattr(cls, k)}) except Exception as e: print(f"[governance] Warning: could not load policy ({e}), using defaults") return cls()
[docs] def save(self, hillstar_dir: str) -> None: """Persist policy to .hillstar/governance_policy.json.""" os.makedirs(hillstar_dir, exist_ok=True) policy_path = os.path.join(hillstar_dir, "governance_policy.json") with open(policy_path, "w", encoding="utf-8") as f: json.dump({ "max_age_seconds": self.max_age_seconds, "allow_force_override": self.allow_force_override, "require_workflow_id": self.require_workflow_id, "blocked_patterns": self.blocked_patterns, "exempt_patterns": self.exempt_patterns, }, f, indent=2)