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:
- Scan, fix a few things, scan again
- “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.