Third Plane-1 checker on the Phase-0 shared substrate, mirroring
compliance-drift.sh / dependency-cve.sh conventions verbatim (set -euo pipefail,
sourced substrate, --canary/--dry-run/--no-api/--refresh/--targets, mode-600
reports under $REPORT_ROOT/doc-drift/<UTC-date>/, ALARM-only, finding.schema
spirit JSON, exit 0/2/3, dotgit->.git fixture trick).
Detects documentation drift deterministically (design §4 doc-drift row):
- readme-omits-component: README omits an existing major component in the tree
(top-level service dir, SAM/CDK stack, Lambda handler dir, openapi/docs spec)
- readme-stale-vs-code: README last-touch far older than newest code commit
(two-factor: >=DOC_DRIFT_STALE_DAYS AND >=DOC_DRIFT_STALE_COMMITS)
A repo with NO README is SKIPPED (compliance-drift owns readme-present; no
double-flag). Future Gemini large-context judge (§4) is an inert stub (maybe_judge),
off in canary/dry-run/offline.
Planted-drift fixture corpus + EXPECTED_DRIFT_COUNT=4, canary-asserted (exit 3 on
miss). shellcheck -x clean (only accepted SC1091 source-line info).
Does NOT touch checker_coordinator.sh, requirements.txt, or aws-posture.
Wiring/systemd is gated (PROVISIONING footer). Design refs §4, §7 Phase 3.
|
||
|---|---|---|
| .. | ||
| clean-repo | ||
| drift-omits-repo | ||
| drift-stale-repo | ||
| no-readme-repo | ||
| EXPECTED_DRIFT_COUNT | ||
| README.md | ||
doc-drift canary fixtures
Planted-drift corpus for checkers/doc-drift.sh --canary (offline, no network/token).
The checker asserts the total drift count equals EXPECTED_DRIFT_COUNT (anti-complacency
floor, design §6.4). If a check regresses (stops firing), the count drops and the canary
FAILS (exit 3).
doc-drift flags repos whose architecture moved but the README did not (design §4,
doc-drift row). It is deliberately deterministic and grounded — no fuzzy LLM judgment. The
LLM judge layer (Gemini large-context) is a later enhancement and is inert offline (see the
maybe_judge stub in the checker).
Each fixture is a real git checkout (its .git is shipped as dotgit/ so it commits into
THIS repo without becoming a nested submodule; the checker renames it back to .git/ at run
time, the same trick compliance-drift.sh / dependency-cve.sh use). Real commit history is
required because the staleness check reads git log dates.
Detected drift (the deterministic checklist)
| Check | Rule cited | What fires |
|---|---|---|
readme-omits-component |
global CLAUDE.md: "README must accurately describe architecture, services, data flow" | A README exists but omits mention of a major existing component present in the tree: a top-level service dir, a SAM/CDK stack (template.yaml / app.py / cdk.json), a Lambda handler dir, or an openapi/docs API spec. |
readme-stale-vs-code |
global CLAUDE.md: "update the README in the same commit" as functionality changes | The README's last-touched commit is far older than the newest code commit (≥ DOC_DRIFT_STALE_DAYS days) AND ≥ DOC_DRIFT_STALE_COMMITS substantial code commits landed after the README was last touched. |
A repo with no README at all is SKIPPED by doc-drift, not flagged — readme-present is
compliance-drift.sh's job, and double-flagging would be a false alarm
(memory feedback_cloudwatch_alarms).
Fixtures
| Fixture | Planted drift | Count |
|---|---|---|
clean-repo |
none — README names every component (api/, the SAM stack, handlers/, openapi/) and the README was committed alongside the code |
0 |
drift-omits-repo |
README mentions only notifier-service; omits payments-service/, the SAM template.yaml stack, and the handlers/charge Lambda dir |
3 |
drift-stale-repo |
README names its one component (no omission) but was last touched 2026-01-05 while 5 substantial code commits landed in 2026-06 — stale | 1 |
no-readme-repo |
no README — doc-drift SKIPS it (must NOT fire; compliance-drift owns this) | 0 |
Total = 4 (EXPECTED_DRIFT_COUNT). The canary pins the staleness thresholds it was
authored against (DOC_DRIFT_STALE_DAYS, DOC_DRIFT_STALE_COMMITS) internally so it is
deterministic regardless of the operator's env.
When you add/remove a check or fixture, update both the fixture and EXPECTED_DRIFT_COUNT
in the same commit (the canary edit is itself caught on the next run — design §6.4).