@@ -4,6 +4,67 @@ All notable changes to AgentFlow are documented in this file.
44
55## [ Unreleased]
66
7+ ### Quality — the auth-manager mutation threshold ratchets to 0.90
8+
9+ * ** ` serving/api/auth/manager.py ` now gates at 0.90.** CI run 34542418689 on
10+ 77825f6 scored it 98.4% (553 killed of 562). The nine survivors are the
11+ named residue in ` tests/unit/test_auth_manager_mutation.py ` . The old
12+ comment above its ` ModuleTarget ` in ` scripts/mutation_report.py ` called
13+ the structured-logging and Redis-URL survivors equivalents. T-48's
14+ behaviour tests killed all of them except the upper-cased Redis URL
15+ default, which is one of the nine. The comment now gives the measured
16+ score and names the nine equivalent survivors, pointing to the test
17+ file's docstring for their reasons. 0.90 is the bar the other serving
18+ modules hold, and it sits 8.4 points under the measured 98.4%, so the
19+ nine named equivalents cannot fail the gate while a real loss of killed
20+ mutants still does.
21+
22+ ### Fixed — mutation_local no longer scores a missing pytest temp directory as a kill
23+
24+ * ** ` scripts/mutation_local.py ` recorded every mutant that reached a
25+ ` tmp_path ` test as killed.** ` measure_module() ` handed pytest a ` --basetemp `
26+ under a scratch directory it never created, and pytest ` mkdir ` s that path
27+ without parents, so the first ` tmp_path ` fixture failed at setup and pytest
28+ exited 1. Under ` -x ` that false kill covered every mutant that survived the
29+ tests before it. ` run_mutant() ` now creates the ` --basetemp ` parent before it
30+ starts pytest, so a mutant that reaches a passing ` tmp_path ` test is scored
31+ survived, and a real failure is still a kill.
32+
33+ ### Quality — the auth-manager mutation lane pins what its survivors changed
34+
35+ * ** ` serving/api/auth/manager.py ` sat two points above its 0.80 threshold.**
36+ CI run 34464697021 on 6231b73 scored it 82.1% (430 killed of 524). Most of
37+ the 94 survivors changed something a behaviour test can see, including the
38+ logging and Redis-URL mutants that ` scripts/mutation_report.py ` calls
39+ equivalents: a log event is the operator's interface, and the Redis URL
40+ decides which server holds the rate-limit budget.
41+ ` tests/unit/test_auth_manager_mutation.py ` now pins the key-store
42+ writability probe, read-only file included; the construction wiring (the
43+ injected store and audit publisher, usage rows flowing through the real
44+ ` UsageWriter ` , the default store following ` db_path ` , the security config
45+ path at construction and on ` load() ` , the grace-period fallback and its
46+ warning, the Redis server the limiter targets); the ` load() ` log events
47+ ` api_keys_loaded ` , ` api_key_store_write_skipped_readonly ` and
48+ ` hashed_key_count_exceeds_guidance ` (strictly above the soft limit,
49+ unindexed entries only); what ` load() ` writes back, and the key file it
50+ leaves byte-for-byte alone; that a match's slot comes from the material that
51+ matched, never from the stored entry; that ` load() ` carries rate-limit
52+ windows over by bucket; scan order in ` authenticate() ` ; when
53+ ` check_rate_limit() ` trusts Redis; and ` AGENTFLOW_API_KEYS ` parsing.
54+ * ** Two simplifications remove mutants no test could kill.** The writability
55+ probe opens the key file in binary append mode: the text layer and its
56+ ` encoding="utf-8" ` did nothing for an open-and-close probe, and three
57+ mutants changed only them. ` _legacy_env_keys() ` stops passing
58+ ` allowed_entity_types ` at ` TenantKey ` 's own default (` None ` );
59+ ` rate_limit_rpm=DEFAULT_RATE_LIMIT_RPM ` stays because ` TenantKey ` 's Field
60+ default is evaluated at import, while a monkeypatch of the module global
61+ is a load-time read.
62+ * ** The residue is named, not suppressed.** Nine equivalent mutants stay
63+ alive, each on a line that also carries killable mutants, so a line-level
64+ ` # pragma: no mutate ` would silence those too; the test file's docstring
65+ lists them with their reasons. The threshold is unchanged here: it is set
66+ from the CI measurement of this tree.
67+
768### Fixed — a key configured without a key_id keeps the same id on every load
869
970A key whose configuration carries no ` key_id ` — every key from
0 commit comments