Collapse the duplicated high-risk expiry rule; close agent mutation gaps

Mutation testing carried agent.ts to zero survivors (37 mutants, all killed
or non-compiling). Four gaps, in descending order of how much they mattered:

The high-risk session's expiry was decided in two places -- the sweep in
expire() and a second comparison inside highRiskTag(). Breaking EITHER left
the whole suite green, because each covered for the other. Worse, the copy in
highRiskTag() was unreachable: handle() sweeps before it dispatches, so no
request could ever arrive with an expired session still in the map. It read
like a check and enforced nothing. highRiskTag() now sweeps instead of
re-testing expiresAt, so the rule has one home and a test can hold it to
account.

The remaining three were missing assertions, not missing checks:

- `put`'s `!fingerprint || !mnemonic` guard could become `&&` unnoticed. Both
  fields are re-validated downstream, so nothing is permitted that was not
  before -- but a blank mnemonic then reports "fingerprint does not match the
  supplied seed", sending whoever reads it after the wrong field. Tests now
  pin the message.
- Same shape on high-risk-unlock's fingerprint format guard: disabling it
  entirely still refused, via "profile not unlocked" -- a lie about a string
  that could never be a profile. It is also the guard between a
  caller-supplied string and the audit-log path join, so it should be the one
  that answers.
- highRiskStatus's client-side `expires_at === null` decode was never
  asserted; inverted, it reports 0 for a locked profile and null for a live
  one. That number is how a caller knows what is left of the second factor.

Also hoists FROZEN/TAG/daemonAt to module scope so the new tests share the
injectable clock.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01Vtx94nXDmNgXEeThUZP4QN
This commit is contained in:
thePR0M3TH3ANandClaude Opus 5 committed 2026-08-19 16:49:33 -04:00
1 parent 17b94d3e26
commit 62e577a6e1
2 files changed
+162 -26

No files matched your search

+9 -7
View File
@@ -541,13 +541,15 @@ export class AgentDaemon {
* partition, so recording it would put it on disk by another route.
*/
private highRiskTag(fingerprint: string): string {
const session = this.highRisk.get(fingerprint);
if (!session) return "";
if (session.expiresAt <= this.now()) {
this.highRisk.delete(fingerprint);
return "";
}
return session.tag;
// Sweep rather than re-test `expiresAt` here. This used to carry its own
// copy of the expiry comparison, which was wrong twice over: it was a
// second place the rule could drift from expire(), and -- because
// handle() sweeps before it dispatches -- it was a copy no request could
// ever reach. Mutation testing found it: breaking EITHER comparison left
// the whole suite green, because each one covered for the other. One
// decision point means a test can actually hold it to account.
this.expire();
return this.highRisk.get(fingerprint)?.tag ?? "";
}
private async handle(msg: Record<string, unknown>): Promise<Record<string, unknown>> {