"""The daily operating view as plain text, and what it must not become (plan Step 23). `dagens` is build-brief 6's consumer: the flags the status machine derives surface here or nowhere. At this milestone it is text -- the HTML dashboard is M6's job -- and text pinned to a golden file, because a daily view whose wording drifts is a daily view nobody can diff. Three properties carry the section, and each is here for a measured reason. **It writes nothing.** The view is read on a workspace the operator is working in, often several times a day. A view that refreshed a cache as a side effect would make reading the pipeline a write, and `sak_status.py --oppdater` is deliberately the only writer of those four keys. **"No inbound recorded" is not "gone silent".** With the mail server deferred to M4, nothing reads the inbox, so an absent inbound event is a fact about the log and not about the world (risk H9). A view that read the absence as silence would flag most of the pipeline on its first run and be switched off by the second. **The version echo lives in the skill, never in this output.** Step 9 promised the echo from `dagens` and Step 23 is where that promise is kept -- but the stamp changes with every commit, so a stamp inside the text would break this step's own golden at the next commit. The echo is on request, from the skill. Style note: this file follows tests/test_sak_status_cli.py. """ import hashlib import os import re import subprocess import sys import pytest import sak_status REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) CLI = os.path.join(REPO, "scripts", "dagens.py") KORPUS = os.path.join(REPO, "tests", "fixtures", "workspace") GOLDEN = os.path.join(REPO, "tests", "golden", "dagens-2026-09-15.txt") SKILL = os.path.join(REPO, "skills", "dagens", "SKILL.md") STEMPEL = os.path.join(REPO, "BUILD_STAMP") TODAY = "2026-09-15" #: The three the status machine flags at the frozen date, one per rule. FLAGGEDE = { "2026-09-bratthaug-industri-as-systemarkitekt": "intervju_10", "2026-09-havbris-energi-as-losningsarkitekt": "sendt_14", "2026-09-storelva-kommune-fagleder-digitalisering": "dialog_7", } #: A case with no inbound recorded and no flag. It must be labelled, and it #: must not be presented as having gone quiet. UTEN_INNKOMMENDE = "2026-09-nordlys-data-as-ai-radgiver" def kjor(*argv, **kwargs): env = dict(os.environ) env.pop("JOBBSOK_WORKSPACE", None) env.pop("CLAUDE_PLUGIN_DATA", None) env.update(kwargs.get("env", {})) proc = subprocess.run( [sys.executable, CLI] + list(argv), stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env, ) return proc.returncode, proc.stdout, proc.stderr def visning(workspace=KORPUS, today=TODAY): kode, ut, feil = kjor("--workspace", workspace, "--today", today) assert kode == 0, feil.decode("utf-8") return ut.decode("utf-8") def seksjoner(tekst): """Split the view into {heading: body}, so a claim about a section is one.""" deler = re.split(r"^## ", tekst, flags=re.M) ut = {} for del_ in deler[1:]: linjer = del_.split("\n") overskrift = re.sub(r"\s*\(\d+\)\s*$", "", linjer[0]).strip() ut[overskrift] = "\n".join(linjer[1:]) return ut def digest(root): ut = {} for dirpath, dirnames, filenames in os.walk(root): dirnames.sort() for navn in sorted(filenames): full = os.path.join(dirpath, navn) with open(full, "rb") as handle: ut[os.path.relpath(full, root)] = hashlib.sha256(handle.read()).hexdigest() return ut def test_the_text_view_matches_the_golden_for_the_fixture_workspace(golden): golden(GOLDEN, visning()) def test_the_three_flagged_cases_appear_under_the_silence_section(): deler = seksjoner(visning()) stille = [navn for navn in deler if "stille" in navn.lower()] assert len(stille) == 1, "expected exactly one silence section, found %r" % (list(deler),) kropp = deler[stille[0]] for sak_id, flagg in FLAGGEDE.items(): assert sak_id in kropp, "%s is flagged but not in the silence section" % sak_id assert flagg in kropp, "the section never names the rule %r that fired" % flagg # And nothing else: a section that listed the whole pipeline would be a # section the operator learns to skip. Membership is checked against the # corpus's own case list rather than a pattern, because a pattern loose # enough to catch a sak-id also catches the ISO dates in the same block. oppforte = [sak_id for sak_id in sak_status.alle_saker(KORPUS) if sak_id in kropp] assert sorted(oppforte) == sorted(FLAGGEDE), ( "the silence section lists %r; only the flagged three belong there" % (sorted(oppforte),) ) def test_a_case_with_no_recorded_inbound_is_labelled_and_not_called_silent(): tekst = visning() deler = seksjoner(tekst) stille = deler[[navn for navn in deler if "stille" in navn.lower()][0]] assert UTEN_INNKOMMENDE not in stille, ( "a case with nothing recorded inbound was presented as having gone " "quiet; with the mail server deferred that is a fact about the log" ) assert UTEN_INNKOMMENDE in tekst, "the case is missing from the view entirely" # Labelled, and in the status machine's own words rather than a second # paraphrase that could drift from it. rapport = sak_status.rapport(KORPUS, TODAY, UTEN_INNKOMMENDE) merknad = rapport["saker"][0]["innkommende"]["merknad"] assert merknad and merknad in tekst, ( "the view never carries the %r note" % (merknad,) ) def test_the_run_writes_nothing(workspace): forst = digest(workspace) kode, _ut, feil = kjor("--workspace", workspace, "--today", TODAY) assert kode == 0, feil.decode("utf-8") assert digest(workspace) == forst, "the daily view wrote into the workspace" def test_two_runs_are_byte_identical(): forste = visning() andre = visning() assert forste == andre # And under two hash seeds, which is what turns "no dict reaches stdout" # from an intention into a measurement. _kode, null, _ = kjor("--workspace", KORPUS, "--today", TODAY, env={"PYTHONHASHSEED": "0"}) _kode, mange, _ = kjor("--workspace", KORPUS, "--today", TODAY, env={"PYTHONHASHSEED": "1048573"}) assert null == mange, ( "output moved with PYTHONHASHSEED, so a dict or a set reached stdout" ) def test_the_skill_carries_the_version_echo_and_the_golden_does_not(): with open(SKILL, "r", encoding="utf-8") as handle: skill = handle.read() assert "BUILD_STAMP" in skill, ( "Step 9 promised the echo from dagens in two places and kept it in " "none; this is the step that keeps it" ) assert "plugin.json" in skill, "the echo prints the stamp and the version, not one" tekst = visning() assert "BUILD_STAMP" not in tekst, ( "the daily view carries the build stamp; it changes with every commit " "and would break this step's own golden at the next one" ) if os.path.isfile(STEMPEL): with open(STEMPEL, "r", encoding="utf-8") as handle: stempel = handle.read().strip() assert stempel and stempel not in tekst, ( "the stamp %r leaked into the daily view" % (stempel,) )