Files
DECNET/tests/profiler/behave_shell/test_cognitive_cognitive_load.py
anti f2b3393669 chore: relicense to AGPL-3.0-or-later and add SPDX headers
Replaces LICENSE (GPLv3 -> AGPLv3) and prepends
`SPDX-License-Identifier: AGPL-3.0-or-later` to every source file
across decnet/, decnet_web/, tests/, scripts/, and tools/.

Rationale: closes the GPLv3 ASP loophole so any party operating a
modified DECNET as a network service must offer their modified
source. Personal copyright (Samuel Paschuan) + inbound=outbound
contributions make a future unilateral relicense infeasible.

- LICENSE: full AGPL-3.0 text (gnu.org/licenses/agpl-3.0.txt)
- COPYRIGHT: project copyright notice
- tools/add_spdx_headers.py: idempotent header injector
  (shebang- and PEP 263-aware)

Touches 1565 source files (.py, .ts, .tsx, .js, .jsx, .css, .sh).
No behavior change; comments only.
2026-05-22 21:04:16 -04:00

90 lines
3.6 KiB
Python

# SPDX-License-Identifier: AGPL-3.0-or-later
"""Step D.1: ``cognitive.cognitive_load``.
Composite of three [0, 1]-clipped sub-signals (chunking variance, error
rate, pace variability) → bucketed against COGNITIVE_LOAD_LOW_MAX /
COGNITIVE_LOAD_MEDIUM_MAX. Tests pin each component at its extremes and
confirm the bucket falls where the math says.
"""
from __future__ import annotations
from decnet.profiler.behave_shell import extract_session
from decnet.profiler.behave_shell._parse import AsciinemaEvent
def _of(observations: list, primitive: str):
obs = [o for o in observations if o.primitive == primitive]
assert len(obs) == 1, f"expected exactly one {primitive}, got {len(obs)}"
return obs[0]
def _typed(text: str, t0: float = 0.0, dt: float = 0.05) -> list[AsciinemaEvent]:
return [(t0 + i * dt, "i", c) for i, c in enumerate(text)]
def _metronomic_clean_session(n: int = 8) -> list[AsciinemaEvent]:
"""``n`` commands, perfectly even pacing, zero errors, fluent typing."""
events: list[AsciinemaEvent] = []
for i in range(n):
events.extend(_typed("ls\r", t0=i * 1.0, dt=0.05))
return events
def test_no_commands_no_emission() -> None:
events: list[AsciinemaEvent] = [(0.0, "i", "a")]
out = list(extract_session(events, sid="cl-empty"))
assert [o for o in out if o.primitive == "cognitive.cognitive_load"] == []
def test_metronomic_clean_session_emits_low() -> None:
"""Even pacing + clean output + steady typing → low load."""
out = list(extract_session(_metronomic_clean_session(8), sid="cl-low"))
obs = _of(out, "cognitive.cognitive_load")
assert obs.value == "low"
def test_high_error_rate_drives_load_up() -> None:
"""Every command errored — error_load = 1.0 alone forces load >= 0.33."""
events: list[AsciinemaEvent] = []
for i in range(8):
events.extend(_typed("foo\r", t0=i * 1.0, dt=0.05))
events.append((i * 1.0 + 0.5, "o", "bash: foo: command not found\n"))
out = list(extract_session(events, sid="cl-err"))
obs = _of(out, "cognitive.cognitive_load")
assert obs.value in ("medium", "high")
def test_all_three_components_high_emits_high() -> None:
"""Saturate every component → load ≈ 1.0 → high."""
events: list[AsciinemaEvent] = []
# Burst-then-gap pacing maximises pace-CV; mid-command jitter
# maximises chunking-CV; every command errors.
starts = [0.0, 0.1, 0.2, 30.0, 30.1, 60.0, 90.0, 90.1]
for i, s in enumerate(starts):
# Mid-command jitter: 'a' at s, 'b' 0.01s later, 'c' 2s later, '\r' 2.05s later
events.append((s, "i", "a"))
events.append((s + 0.01, "i", "b"))
events.append((s + 2.0, "i", "c"))
events.append((s + 2.05, "i", "\r"))
events.append((s + 2.10, "o", "bash: abc: command not found\n"))
out = list(extract_session(events, sid="cl-high"))
obs = _of(out, "cognitive.cognitive_load")
assert obs.value == "high"
def test_low_sample_count_reduces_confidence() -> None:
short = list(extract_session(_metronomic_clean_session(3), sid="cl-short"))
full = list(extract_session(_metronomic_clean_session(8), sid="cl-full"))
s = _of(short, "cognitive.cognitive_load")
f = _of(full, "cognitive.cognitive_load")
assert s.confidence < f.confidence
def test_pii_no_command_bodies_in_observation() -> None:
events: list[AsciinemaEvent] = []
for i in range(6):
events.extend(_typed("supersecret\r", t0=i * 1.0, dt=0.05))
out = list(extract_session(events, sid="cl-pii"))
obs = _of(out, "cognitive.cognitive_load")
assert "supersecret" not in obs.model_dump_json()