rockglacierNET / scripts /update_context_hook.py
kabiriitb's picture
Upload folder using huggingface_hub
80e070e verified
Raw History Blame Contribute Delete
6.85 kB
#!/usr/bin/env python3
"""
Stop-hook context updater for RockGlacierNet.
Runs as a plain shell command β€” no Claude Code permission system involved.
Appends a timestamped session-end entry to agent.md and updates
project_state.md with current git + training state.
Called by .claude/settings.json Stop hook.
"""
import os
import sys
import subprocess
import glob
from datetime import datetime, timezone
ROOT = "/data1/nasiruddink/rockglacier/RockGlacierNet"
AGENT = os.path.join(ROOT, "agent.md")
MEMORY = "/home/nasiruddink/.claude/projects/-data1-nasiruddink-rockglacier-RockGlacierNet/memory"
STATE = os.path.join(MEMORY, "project_state.md")
MEMIDX = os.path.join(MEMORY, "MEMORY.md")
def run(cmd):
try:
return subprocess.check_output(cmd, shell=True, cwd=ROOT,
stderr=subprocess.DEVNULL,
text=True).strip()
except Exception:
return ""
def git_state():
log = run("git log --oneline -5")
status = run("git status --short")
return log, status
def training_state():
csv = os.path.join(ROOT, "outputs/checkpoints/training_history.csv")
ckpt = os.path.join(ROOT, "outputs/checkpoints/best_model.weights.h5")
has_ckpt = os.path.exists(ckpt)
if not os.path.exists(csv):
return None, has_ckpt
try:
# Read last row without pandas dependency
with open(csv) as f:
lines = [l.strip() for l in f if l.strip()]
if len(lines) < 2:
return None, has_ckpt
header = lines[0].split(",")
last = lines[-1].split(",")
row = dict(zip(header, last))
return row, has_ckpt
except Exception:
return None, has_ckpt
def patch_counts():
counts = {}
for split in ("train", "val", "test"):
d = os.path.join(ROOT, "patches", split, "features")
if os.path.isdir(d):
counts[split] = len(glob.glob(os.path.join(d, "*.npy")))
else:
counts[split] = 0
return counts
def append_agent_md(now_str, log, status, row, has_ckpt, patches):
lines = [
"",
"---",
"",
f"## Session β€” {now_str}",
"",
"*(Auto-recorded by Stop hook β€” run `/update-context` for full intelligent summary)*",
"",
]
lines += ["**Git state:**"]
if log:
for l in log.splitlines():
lines.append(f"- {l}")
else:
lines.append("- (no commits)")
if status:
lines.append(f"- Modified: {', '.join(status.splitlines()[:5])}")
lines.append("")
lines += ["**Training state:**"]
lines.append(f"- Checkpoint: {'exists' if has_ckpt else 'NOT FOUND'} (best_model.weights.h5)")
if row:
epoch = row.get("epoch", "?")
iou = row.get("val_iou_m", row.get("iou_m", "?"))
dice = row.get("val_dice_coef", "?")
loss = row.get("val_loss", row.get("loss", "?"))
prec = row.get("val_precision_m", "?")
rec = row.get("val_recall_m", "?")
lines.append(f"- Last epoch: {epoch} | val_iou={iou} val_dice={dice} val_loss={loss}")
lines.append(f"- val_precision={prec} val_recall={rec}")
else:
lines.append("- No training history found")
lines.append("")
if any(patches.values()):
lines += ["**Patches:**"]
for sp, n in patches.items():
lines.append(f"- {sp}: {n} patches")
lines.append("")
lines.append("**Next steps:** *(fill in manually or run /update-context)*")
lines.append("")
entry = "\n".join(lines)
os.makedirs(os.path.dirname(AGENT) if os.path.dirname(AGENT) else ".", exist_ok=True)
with open(AGENT, "a") as f:
f.write(entry)
def update_memory(now_str, row, has_ckpt, log):
os.makedirs(MEMORY, exist_ok=True)
# --- MEMORY.md index ---
mem_content = f"""# RockGlacierNet Memory Index
**Last Updated**: {now_str}
- [project_state.md](project_state.md) β€” Full pipeline, model architecture, dataset stats, training metrics
"""
with open(MEMIDX, "w") as f:
f.write(mem_content)
# --- project_state.md ---
iou = dice = loss = epoch = "unknown"
if row:
epoch = row.get("epoch", "?")
iou = row.get("val_iou_m", row.get("iou_m", "?"))
dice = row.get("val_dice_coef", "?")
loss = row.get("val_loss", row.get("loss", "?"))
last_commit = log.splitlines()[0] if log else "unknown"
state_content = f"""---
name: project-state
description: Full pipeline, model architecture, dataset stats, and current training results for RockGlacierNet
metadata:
type: project
lastUpdated: "{now_str}"
---
## What this project does
Binary semantic segmentation of rock glaciers in Sikkim Himalayas.
Input: 12-band GeoTIFFs (Sentinel-2 + DEM). Output: binary glacier/background mask.
## Current Training State (auto-updated {now_str})
- Last commit: {last_commit}
- Checkpoint: {'EXISTS' if has_ckpt else 'MISSING'} β€” outputs/checkpoints/best_model.weights.h5
- Last epoch: {epoch}
- val_iou_m: {iou} | val_dice_coef: {dice} | val_loss: {loss}
## Baseline (old 128Γ—128 model, 70 epochs)
- val_iou=0.5825, val_dice=0.7618, precision=0.6233, recall=0.8990, f1=0.7362
## Current Result (new 64Γ—64 model, 58 epochs β€” REGRESSION)
- val_iou=0.3308 β€” IoU dropped 44% vs baseline
- Root cause: 64px patches too small for spatial context (glaciers are 40–60px wide)
- Fix needed: increase patch_size to 128 or use full-image variable-size training
## Active Architecture
- Model: ResidualSwinUNET_64x64 (models/swin_unet.py)
- Input: (64, 64, 12) β€” Output: (64, 64, 1) sigmoid
- Encoder: 3 pool layers, filters 32β†’64β†’128β†’256
- Bottleneck: 8Γ—8Γ—256 + 2Γ— Swin transformer blocks (64 tokens)
- Loss: Focal(Ξ±=0.75,Ξ³=2.0)Γ—2 + Dice | Optimizer: Adam lr=1e-4
## Pipeline Run Order
1. python preprocess/extract_patches.py
2. python preprocess/validate_alignment.py
3. python train.py
4. python evaluate.py
## Key Files
- preprocess/patch_config.py β€” patch_size=64, stride=32, bg_keep=0.25
- data_generator.py β€” loads .npy patches, z-score normalizes
- outputs/checkpoints/training_history.csv β€” per-epoch metrics
- agent.md β€” full session log and project index
"""
with open(STATE, "w") as f:
f.write(state_content)
def main():
now_str = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
log, status = git_state()
row, has_ckpt = training_state()
patches = patch_counts()
append_agent_md(now_str, log, status, row, has_ckpt, patches)
update_memory(now_str, row, has_ckpt, log)
print(f"Context updated β€” agent.md entry appended ({now_str}).")
if __name__ == "__main__":
main()