Phases

Phase 2 — History and Drift Detection

The drift engine and posture-history design, with 141 tests.

Status: ✅ Complete
Delivered: March 2026
Builds on: Phase 1


What Was Built

Phase 2 added persistence and temporal awareness — the ability to track how your security posture changes over time.

Scan History

Every scan is now automatically persisted to ~/.stacksentry/history.db. The ScanHistory class handles serialisation of ScanResult objects to JSON blobs stored in SQLite rows, with the target URL, timestamp, grade, and score indexed for fast retrieval.

history = ScanHistory()
history.save(scan_result)
 
# Retrieve all scans for a target
previous = history.get_latest(target_url)

Drift Detection

The DriftEngine compares two ScanResult objects and produces a DriftReport containing:

  • Regressions — checks that were PASS and are now FAIL or WARN
  • Improvements — checks that were FAIL or WARN and are now PASS
  • New failures — checks that are FAIL but were not present before
  • Grade delta — numeric change in pass rate percentage
  • Trend — IMPROVING, REGRESSING, or STABLE
# See what changed since last scan
stacksentry -t https://your-app.com --compare-last

What-If Simulation

The simulate_with_fixes method creates a temporary copy of a scan result, marks specified check IDs as PASS, and recomputes all metrics:

stacksentry -t https://your-app.com \
  --simulate HOST-SSH-001,WS-HSTS-001,WS-SEC-001

The elegance of this approach is that no special simulation mode exists — the same scoring and grading logic runs on both real and simulated data. If the scoring model is accurate, the projection is too.


Why Drift Detection Matters

A security score without history is a snapshot. A security score with history is a trend. The difference matters enormously for small teams — you can show that fixes are working, that a deployment introduced a regression, or that the posture is stable over time.

The --compare-last flag was added to the CLI because the most common real-world workflow is:

  1. Scan, fix a few things, scan again
  2. “Did my fixes work? Did anything else break?” Without drift detection, answering that question requires manually comparing two reports. With drift detection, it is one line.

Phase 2 Test Suite

tests/test_drift.py  — 55 tests

The drift module has the highest test count of any module because it must correctly handle all combinations of status transitions and first-scan edge cases. Key test cases:

  • PASS → FAIL (regression detected)
  • FAIL → PASS (improvement detected)
  • WARN → PASS (improvement detected)
  • First scan (no previous — no drift reported)
  • Check present in previous, absent in current
  • Check absent in previous, present in current (new failure)
  • Grade delta positive and negative
  • Trend correctly classified in all three states Total at Phase 2 completion: 141 tests, all passing.