Domain: record store with reporting layer

Issue:
After a summary report is generated, the recent-activity feed goes wrong: entries come back ordered by severity instead of by when they were recorded, so the actual latest records are buried and old entries show up as recent activity. The report's own output is correct, and nothing wrote to the store afterwards — but every later read reflects the reshuffled order until the service restarts. Expected behavior: reading from or reporting on the store must never change what later queries return; the store's read API documents handing back a snapshot in insertion order, and callers should be able to do whatever they like with what they get back without affecting the store.

Repository files:
content: # Present so pytest adds the repository root to sys.path, making `import src`
# resolve from the test suite.

path: conftest.py

content: # Pure standard library unless the environment image pre-bakes wheels.

path: requirements.lock

path: src/__init__.py

content: """Plain-text export of activity records."""


def to_lines(records):
    """Format records as 'SEVERITY category: message' lines."""
    return [
        "{} {}: {}".format(record.label().upper(), record.category, record.message)
        for record in records
    ]


def to_text(records):
    """Render records as a newline-joined document."""
    return "\n".join(to_lines(records))

path: src/export.py

content: """Pure filtering helpers over record lists."""


def by_category(records, category):
    """Records matching the given category, order preserved."""
    return [record for record in records if record.category == category]


def at_least(records, severity):
    """Records at or above the given severity, order preserved."""
    return [record for record in records if record.severity >= severity]

path: src/filters.py

content: """Data model for activity records."""

from dataclasses import dataclass

LOW = 1
MEDIUM = 2
HIGH = 3
CRITICAL = 4

SEVERITY_NAMES = {
    LOW: "low",
    MEDIUM: "medium",
    HIGH: "high",
    CRITICAL: "critical",
}


@dataclass
class Record:
    """A single activity record."""

    category: str
    severity: int
    message: str

    def label(self):
        """Human-readable severity name."""
        return SEVERITY_NAMES.get(self.severity, str(self.severity))

path: src/models.py

content: """Summary reporting over a record store."""

from collections import Counter

from .filters import at_least
from .models import CRITICAL, SEVERITY_NAMES


def summarize(store, top_n=3):
    """Build a summary of the store's contents.

    Returns a dict with the total record count, per-severity counts, the
    number of critical records, and the messages of the top_n most severe
    records.
    """
    records = store.all_records()
    records.sort(key=lambda record: record.severity, reverse=True)
    counts = Counter(record.severity for record in records)
    return {
        "total": len(records),
        "by_severity": {
            SEVERITY_NAMES[level]: counts.get(level, 0)
            for level in SEVERITY_NAMES
        },
        "critical": len(at_least(records, CRITICAL)),
        "top": [record.message for record in records[:top_n]],
    }

path: src/report.py

content: """In-memory storage for activity records."""


class RecordStore:
    """Holds activity records in the order they were added."""

    def __init__(self):
        self._records = []

    def add(self, record):
        """Append a record to the store."""
        self._records.append(record)

    def all_records(self):
        """Return a snapshot of all records in insertion order."""
        return self._records

    def recent(self, n):
        """Return the n most recently added records, oldest of them first."""
        if n <= 0:
            return []
        return self._records[-n:]

    def clear(self):
        """Remove every record."""
        self._records.clear()

    def __len__(self):
        return len(self._records)

path: src/store.py

content: """Tests for the activity store, filters, reporting, and export."""

from src.export import to_lines, to_text
from src.filters import at_least, by_category
from src.models import CRITICAL, HIGH, LOW, MEDIUM, Record
from src.report import summarize
from src.store import RecordStore


def make_store():
    store = RecordStore()
    store.add(Record("auth", MEDIUM, "password changed"))
    store.add(Record("billing", CRITICAL, "charge failed"))
    store.add(Record("auth", LOW, "login ok"))
    store.add(Record("api", HIGH, "rate limit reached"))
    return store


def test_add_and_len():
    store = make_store()
    assert len(store) == 4
    store.clear()
    assert len(store) == 0


def test_all_records_insertion_order():
    store = make_store()
    assert [record.message for record in store.all_records()] == [
        "password changed",
        "charge failed",
        "login ok",
        "rate limit reached",
    ]


def test_recent_returns_latest_in_insertion_order():
    store = make_store()
    assert [record.message for record in store.recent(2)] == [
        "login ok",
        "rate limit reached",
    ]
    assert store.recent(0) == []


def test_filters_preserve_order():
    records = make_store().all_records()
    assert [record.message for record in by_category(records, "auth")] == [
        "password changed",
        "login ok",
    ]
    assert [record.message for record in at_least(records, HIGH)] == [
        "charge failed",
        "rate limit reached",
    ]


def test_summarize_counts_and_top():
    summary = summarize(make_store(), top_n=2)
    assert summary["total"] == 4
    assert summary["critical"] == 1
    assert summary["by_severity"] == {
        "low": 1,
        "medium": 1,
        "high": 1,
        "critical": 1,
    }
    assert summary["top"] == ["charge failed", "rate limit reached"]


def test_export_formats_records():
    records = [
        Record("auth", LOW, "login ok"),
        Record("billing", CRITICAL, "charge failed"),
    ]
    assert to_lines(records) == [
        "LOW auth: login ok",
        "CRITICAL billing: charge failed",
    ]
    assert to_text(records) == "LOW auth: login ok\nCRITICAL billing: charge failed"

path: tests/public/test_public.py

Reference patch:
--- a/src/store.py
+++ b/src/store.py
@@ -13,7 +13,7 @@ class RecordStore:
 
     def all_records(self):
         """Return a snapshot of all records in insertion order."""
-        return self._records
+        return list(self._records)
 
     def recent(self, n):
         """Return the n most recently added records, oldest of them first."""


Hidden acceptance tests (diff):
diff --git a/tests/hidden/test_hidden.py b/tests/hidden/test_hidden.py
new file mode 100644
--- /dev/null
+++ b/tests/hidden/test_hidden.py
@@ -0,0 +1,50 @@
+"""Hidden evaluation tests. Never agent-visible (DEC-015).
+
+FAIL_TO_PASS: on the buggy fixture, all_records() hands out the store's
+internal list, so the report's in-place severity sort reorders the store
+itself and any caller mutation leaks back in; all three tests fail. With
+the documented snapshot behavior restored, all three pass.
+"""
+
+from src.models import CRITICAL, HIGH, LOW, MEDIUM, Record
+from src.report import summarize
+from src.store import RecordStore
+
+INSERTION_ORDER = [
+    "password changed",
+    "charge failed",
+    "login ok",
+    "rate limit reached",
+]
+
+
+def _populated():
+    store = RecordStore()
+    store.add(Record("auth", MEDIUM, "password changed"))
+    store.add(Record("billing", CRITICAL, "charge failed"))
+    store.add(Record("auth", LOW, "login ok"))
+    store.add(Record("api", HIGH, "rate limit reached"))
+    return store
+
+
+def test_recent_unchanged_after_summarize():
+    store = _populated()
+    summarize(store)
+    assert [record.message for record in store.recent(2)] == [
+        "login ok",
+        "rate limit reached",
+    ]
+
+
+def test_insertion_order_survives_reporting():
+    store = _populated()
+    summarize(store)
+    summarize(store)
+    assert [record.message for record in store.all_records()] == INSERTION_ORDER
+
+
+def test_snapshot_is_independent_of_the_store():
+    store = _populated()
+    snapshot = store.all_records()
+    snapshot.reverse()
+    assert [record.message for record in store.all_records()] == INSERTION_ORDER