From b9ecb0d3a898ccb4687e18adaa299452d2ea6cbd Mon Sep 17 00:00:00 2001 From: Matthew Stenquist <73511861+internetdialup@users.noreply.github.com> Date: Wed, 1 Jul 2026 01:53:29 -0500 Subject: [PATCH] feat: add generic Bamboo Drift Detector and Governor scripts Introduces automated compliance auditing and age-based memory tiering checks to the core repository. --- scripts/bamboo_drift_detector.py | 114 +++++++++++++++++ scripts/bamboo_governor.py | 210 +++++++++++++++++++++++++++++++ 2 files changed, 324 insertions(+) create mode 100644 scripts/bamboo_drift_detector.py create mode 100644 scripts/bamboo_governor.py diff --git a/scripts/bamboo_drift_detector.py b/scripts/bamboo_drift_detector.py new file mode 100644 index 0000000..8b23457 --- /dev/null +++ b/scripts/bamboo_drift_detector.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 +"""bamboo_drift_detector.py — Repository Compliance & Context Drift Detector. + +Scans the repository's markdown documentation to verify adherence to +the Bamboo Doctrine formatting rules (Created headers, versioned footers, +and contamination link requirements). +""" +from __future__ import annotations + +import re +import sys +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parent.parent + +def scan_markdown_compliance() -> tuple[list[str], list[str], list[str]]: + non_compliant_headers = [] + non_compliant_footers = [] + contamination_violations = [] + + # Audit all markdown files in the repository + paths_to_scan = sorted(list(REPO_ROOT.glob("**/*.md"))) + + for p in paths_to_scan: + # Ignore common non-project subdirectories + if any(part in p.parts for part in [".git", "node_modules", "venv", ".gemini", "bamboo-test"]): + continue + + try: + content = p.read_text(encoding="utf-8") + except Exception: + continue + + lines = [line.strip() for line in content.splitlines() if line.strip()] + if not lines: + continue + + # 1. Check Header (first 10 non-empty lines) for Created date + header_text = "\n".join(lines[:10]) + if not re.search(r"(created|created\s*date)", header_text, re.IGNORECASE): + non_compliant_headers.append(str(p.relative_to(REPO_ROOT))) + + # 2. Check Footer (last 10 non-empty lines) for versioned Last Updated + footer_text = "\n".join(lines[-10:]) + if not re.search(r"(last updated|updated|version.*v[0-9])", footer_text, re.IGNORECASE): + non_compliant_footers.append(str(p.relative_to(REPO_ROOT))) + + # 3. Check 2026-06-13 Contamination Mandate + if "2026-06-13" in content: + if "RECALIBRATION_LOG.md" not in content and "FORENSIC-LEDGER-V2.md" not in content: + contamination_violations.append(str(p.relative_to(REPO_ROOT))) + + return non_compliant_headers, non_compliant_footers, contamination_violations + +def main(): + print("🎋 Running Bamboo Drift Detector...") + + nc_headers, nc_footers, contamination_violations = scan_markdown_compliance() + + all_docs = [p for p in REPO_ROOT.glob("**/*.md") if not any(part in p.parts for part in [".git", "node_modules", "venv", ".gemini", "bamboo-test"])] + total_audited_docs = len(all_docs) + + # Calculate compliance scores + header_compliance = 1.0 - (len(nc_headers) / max(total_audited_docs, 1)) + footer_compliance = 1.0 - (len(nc_footers) / max(total_audited_docs, 1)) + contamination_compliance = 1.0 - (len(contamination_violations) / max(total_audited_docs, 1)) + + # Overall Bamboo Drift Score (100 - average compliance %) + overall_drift_score = round(100.0 * (1.0 - (header_compliance + footer_compliance + contamination_compliance) / 3.0), 2) + + # Build lists + nc_headers_str = "\n".join([f"- [{p.split('/')[-1]}](file://{REPO_ROOT / p})" for p in nc_headers[:20]]) or "*None found.*" + nc_footers_str = "\n".join([f"- [{p.split('/')[-1]}](file://{REPO_ROOT / p})" for p in nc_footers[:20]]) or "*None found.*" + contamination_str = "\n".join([f"- [{p.split('/')[-1]}](file://{REPO_ROOT / p})" for p in contamination_violations]) or "*None found.*" + + # Generate MD Report + report_path = REPO_ROOT / "docs" / "bamboo" / "BAMBOO_DRIFT_REPORT.md" + report_content = f"""# 🎋 Bamboo Drift Compliance & Integrity Report + +**Created:** 2026-07-01 + +## 📊 Compliance Scorecard +* **Overall Repository Drift Score:** `{overall_drift_score}%` (lower is better, 0% is perfect compliance) +* **Doc Header Compliance:** `{header_compliance:.2%}` +* **Doc Footer Compliance:** `{footer_compliance:.2%}` +* **Contamination Notice Audits:** `{contamination_compliance:.2%}` + +--- + +## ⚠️ Non-Compliant Documents: Headers +*These files do not contain a 'Created Date' block near the top:* +{nc_headers_str} +{f"*...and {len(nc_headers) - 20} more files.*" if len(nc_headers) > 20 else ""} + +## ⚠️ Non-Compliant Documents: Footers +*These files do not contain a versioned update footer at the bottom:* +{nc_footers_str} +{f"*...and {len(nc_footers) - 20} more files.*" if len(nc_footers) > 20 else ""} + +## ⚠️ Contamination Notice Violations +*These files refer to '2026-06-13' without the required RECALIBRATION_LOG/FORENSIC-LEDGER-V2 reconciliation link:* +{contamination_str} + +--- + +**Last Updated:** 2026-07-01-v0.8.0 +""" + report_path.parent.mkdir(parents=True, exist_ok=True) + report_path.write_text(report_content, encoding="utf-8") + print(f"✅ Saved Bamboo Drift Compliance Report to: {report_path}") + print(f"🎋 Drift Score: {overall_drift_score}%") + +if __name__ == "__main__": + main() diff --git a/scripts/bamboo_governor.py b/scripts/bamboo_governor.py new file mode 100644 index 0000000..b0d5e8f --- /dev/null +++ b/scripts/bamboo_governor.py @@ -0,0 +1,210 @@ +#!/usr/bin/env python3 +"""bamboo_governor.py — Repository Documentation & Bamboo Memory Tiering Governor. + +Enforces canonical folder structure, injects standard header metadata, +appends memory tier (Hot, Warm, Medium, Cool, Cold, Ice) changelogs, +and exports a single-entry structured manifest. +""" +from __future__ import annotations + +from datetime import date +import json +import os +import re +import subprocess +import sys +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parent.parent + +def get_git_timestamps(filepath: Path) -> tuple[str, str]: + try: + cmd = ["git", "log", "--follow", "--format=%as", "--", str(filepath)] + dates = subprocess.check_output(cmd, stderr=subprocess.DEVNULL).decode().strip().splitlines() + if not dates: + today = date.today().isoformat() + return today, today + return dates[-1], dates[0] + except Exception: + today = date.today().isoformat() + return today, today + +def get_latest_tag() -> str: + try: + return subprocess.check_output( + ["git", "describe", "--tags", "--abbrev=0"], + stderr=subprocess.DEVNULL + ).decode().strip() + except Exception: + return "v0.8.0" + +def infer_memory_tier(filepath: Path, created_date_str: str | None = None) -> str: + path_str = str(filepath.relative_to(REPO_ROOT)).lower() + + # Check for age-based ice tiering (6 months = 180 days) + if created_date_str: + try: + created_dt = date.fromisoformat(created_date_str) + age_days = (date.today() - created_dt).days + if age_days >= 180: + return "Ice" + except Exception: + pass + + if "docs/bamboo" in path_str or "docs/engineering" in path_str: + return "Hot" + elif "docs/architecture" in path_str or "why.md" in path_str: + return "Warm" + elif "docs/product" in path_str: + return "Medium" + elif "docs/notes" in path_str or "docs/research" in path_str or "docs/decision-records" in path_str: + return "Cool" + else: + return "Cold" + +def reformat_and_inject(filepath: Path, latest_tag: str) -> dict | None: + try: + content = filepath.read_text(encoding="utf-8") + except Exception as e: + print(f"⚠️ Failed to read {filepath}: {e}") + return None + + if not content.strip(): + return None + + # Strip existing changelog if present + if "### 📜 Document Changelog" in content: + content = content.split("### 📜 Document Changelog")[0].rstrip() + elif "## 📜 Document Changelog" in content: + content = content.split("## 📜 Document Changelog")[0].rstrip() + + # Strip trailing horizontal rule and spacing + lines = content.splitlines() + while lines and (lines[-1].strip() == "" or lines[-1].strip() == "---"): + lines.pop() + content = "\n".join(lines) + + # Strip existing top-level metadata block + lines = content.splitlines() + if "Document Metadata:" in content or "**Document Metadata:**" in content: + new_lines = [] + in_metadata = False + skipped_metadata = False + for line in lines: + if line.strip() == "---" and not skipped_metadata: + idx = lines.index(line) + if idx + 1 < len(lines) and ("Document Metadata:" in lines[idx + 1] or "**Document Metadata:**" in lines[idx + 1]): + in_metadata = True + continue + elif in_metadata: + in_metadata = False + skipped_metadata = True + continue + if in_metadata: + continue + new_lines.append(line) + lines = new_lines + + # Find and update the title line + title_idx = -1 + title_text = filepath.stem.replace("-", " ").title() + for idx, line in enumerate(lines): + if line.startswith("# "): + title_idx = idx + title_text = line[2:].strip() + break + + created, updated = get_git_timestamps(filepath) + version = latest_tag.split("-")[0].replace("v", "") + memory_tier = infer_memory_tier(filepath, created) + + # Clean the title of existing versions + title_clean = re.sub(r'\s*-\s*v\d+(\.\d+)*$', '', title_text).strip() + if title_idx != -1: + lines[title_idx] = f"# {title_clean} - v{version}" + + # Rebuild top-level Metadata + metadata_block = [ + "---", + "**Document Metadata:**", + f"- **Created:** {created}", + f"- **Last Updated:** {updated}", + f"- **Document Version:** v{version}", + f"- **Operational Freeze Tag:** `{latest_tag}`", + "---" + ] + + # Rebuild bottom Changelog + one_sentence_summary = "Aligned structural doctrine with standard memory tiering." + if memory_tier == "Hot": + one_sentence_summary = "Standardized operational guidelines and execution runbooks." + elif memory_tier == "Medium": + one_sentence_summary = "Aligned product requirements and specifications." + elif memory_tier == "Cool": + one_sentence_summary = "Updated research metrics and observations." + elif memory_tier == "Cold": + one_sentence_summary = "Archived historical retro and post-mortem logs." + elif memory_tier == "Ice": + one_sentence_summary = "Archived historical conversational notes and frozen metrics." + + changelog_block = [ + "---", + "### 📜 Document Changelog", + "", + "| Version | Date | Freeze Tag | Memory Tier | Summary of Change |", + "| :--- | :--- | :--- | :--- | :--- |", + f"| v{version} | {updated} | `{latest_tag}` | **{memory_tier}** | {one_sentence_summary} |" + ] + + header = lines[:title_idx + 1] + body = lines[title_idx + 1:] + + updated_lines = header + [""] + metadata_block + [""] + body + [""] + changelog_block + try: + filepath.write_text("\n".join(updated_lines) + "\n", encoding="utf-8") + print(f"✅ Governed: {filepath.relative_to(REPO_ROOT)} [{memory_tier}]") + return { + "path": str(filepath.relative_to(REPO_ROOT)), + "title": title_clean, + "version": f"v{version}", + "freeze_tag": latest_tag, + "memory_tier": memory_tier, + "created": created, + "updated": updated, + "summary": one_sentence_summary + } + except Exception as e: + print(f"❌ Failed to write {filepath}: {e}") + return None + +def main() -> int: + latest_tag = get_latest_tag() + docs_dir = REPO_ROOT / "docs" + docs_dir.mkdir(parents=True, exist_ok=True) + + manifest_entries = [] + + # Governs all markdown files in the repository + for path in sorted(REPO_ROOT.rglob("*.md")): + if any(part in path.parts for part in [".git", "node_modules", "venv", ".gemini", "bamboo-test"]): + continue + entry = reformat_and_inject(path, latest_tag) + if entry: + manifest_entries.append(entry) + + # Export BAMBOO_MANIFEST.json + bamboo_dir = docs_dir / "bamboo" + bamboo_dir.mkdir(parents=True, exist_ok=True) + manifest_path = bamboo_dir / "BAMBOO_MANIFEST.json" + manifest_data = { + "release_freeze_tag": latest_tag, + "document_count": len(manifest_entries), + "documents": manifest_entries + } + manifest_path.write_text(json.dumps(manifest_data, indent=2), encoding="utf-8") + print(f"🚀 Bamboo Manifest exported successfully to {manifest_path.relative_to(REPO_ROOT)}") + + return 0 + +if __name__ == "__main__": + sys.exit(main())