188 lines
6.2 KiB
Bash
Executable file
188 lines
6.2 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
cd "$ROOT"
|
|
|
|
ts="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
|
|
|
|
mkdir -p out
|
|
|
|
git_branch=""
|
|
git_head=""
|
|
git_head_short=""
|
|
git_modified_count="0"
|
|
git_untracked_count="0"
|
|
git_dirty_count="0"
|
|
|
|
if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
git_branch="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "?")"
|
|
git_head="$(git rev-parse HEAD 2>/dev/null || echo "")"
|
|
git_head_short="$(git rev-parse --short HEAD 2>/dev/null || echo "")"
|
|
porcelain="$(git status --porcelain=v1 2>/dev/null || true)"
|
|
if [ -n "${porcelain}" ]; then
|
|
git_dirty_count="$(printf "%s\n" "${porcelain}" | wc -l | awk '{print $1}')"
|
|
git_untracked_count="$(printf "%s\n" "${porcelain}" | grep -c '^??' || true)"
|
|
git_modified_count="$(printf "%s\n" "${porcelain}" | grep -vc '^??' || true)"
|
|
fi
|
|
fi
|
|
|
|
latest_checkpoint_path=""
|
|
latest_checkpoint_sha256=""
|
|
if ls out/checkpoints/iftypeset_checkpoint_*.tar.gz >/dev/null 2>&1; then
|
|
latest_checkpoint_path="$(ls -1t out/checkpoints/iftypeset_checkpoint_*.tar.gz | head -n 1)"
|
|
latest_checkpoint_sha256="$(sha256sum "${latest_checkpoint_path}" | awk '{print $1}')"
|
|
fi
|
|
|
|
export IFTS_STATE_TS="${ts}"
|
|
export IFTS_STATE_ROOT="${ROOT}"
|
|
export IFTS_STATE_GIT_BRANCH="${git_branch}"
|
|
export IFTS_STATE_GIT_HEAD="${git_head}"
|
|
export IFTS_STATE_GIT_HEAD_SHORT="${git_head_short}"
|
|
export IFTS_STATE_GIT_DIRTY_COUNT="${git_dirty_count}"
|
|
export IFTS_STATE_GIT_MODIFIED_COUNT="${git_modified_count}"
|
|
export IFTS_STATE_GIT_UNTRACKED_COUNT="${git_untracked_count}"
|
|
export IFTS_STATE_CHECKPOINT_PATH="${latest_checkpoint_path}"
|
|
export IFTS_STATE_CHECKPOINT_SHA256="${latest_checkpoint_sha256}"
|
|
|
|
python3 - <<'PY'
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
|
|
|
|
def _read_json(path: str):
|
|
p = Path(path)
|
|
if not p.exists():
|
|
return None
|
|
try:
|
|
return json.loads(p.read_text())
|
|
except Exception:
|
|
return None
|
|
|
|
|
|
ts = os.environ.get("IFTS_STATE_TS", "")
|
|
root = os.environ.get("IFTS_STATE_ROOT", "")
|
|
git_branch = os.environ.get("IFTS_STATE_GIT_BRANCH", "")
|
|
git_head = os.environ.get("IFTS_STATE_GIT_HEAD", "")
|
|
git_head_short = os.environ.get("IFTS_STATE_GIT_HEAD_SHORT", "")
|
|
dirty_count = int(os.environ.get("IFTS_STATE_GIT_DIRTY_COUNT", "0") or "0")
|
|
modified_count = int(os.environ.get("IFTS_STATE_GIT_MODIFIED_COUNT", "0") or "0")
|
|
untracked_count = int(os.environ.get("IFTS_STATE_GIT_UNTRACKED_COUNT", "0") or "0")
|
|
checkpoint_path = os.environ.get("IFTS_STATE_CHECKPOINT_PATH", "")
|
|
checkpoint_sha256 = os.environ.get("IFTS_STATE_CHECKPOINT_SHA256", "")
|
|
|
|
coverage = _read_json("out/coverage-report.json") or {}
|
|
counts = coverage.get("counts", {}) if isinstance(coverage, dict) else {}
|
|
|
|
trust_contract_md = Path("out/trust-contract.md").exists()
|
|
trust_contract_json = Path("out/trust-contract.json").exists()
|
|
|
|
state = {
|
|
"generated_at_utc": ts,
|
|
"repo_root": root,
|
|
"git": {
|
|
"branch": git_branch or None,
|
|
"head": git_head or None,
|
|
"head_short": git_head_short or None,
|
|
"dirty_paths_count": dirty_count,
|
|
"modified_paths_count": modified_count,
|
|
"untracked_paths_count": untracked_count,
|
|
},
|
|
"latest_checkpoint": {
|
|
"path": checkpoint_path or None,
|
|
"sha256": checkpoint_sha256 or None,
|
|
},
|
|
"coverage": {
|
|
"generated_at_utc": coverage.get("generated_at_utc") if isinstance(coverage, dict) else None,
|
|
"counts": counts or None,
|
|
},
|
|
"artifacts": {
|
|
"trust_contract_md": trust_contract_md,
|
|
"trust_contract_json": trust_contract_json,
|
|
"coverage_report_json": Path("out/coverage-report.json").exists(),
|
|
"manual_checklist_md": Path("out/manual-checklist.md").exists(),
|
|
},
|
|
"canonical_docs": [
|
|
"README.md",
|
|
"STATUS.md",
|
|
"docs/06-project-overview.md",
|
|
"docs/07-session-resilience.md",
|
|
"docs/09-project-status.md",
|
|
"docs/10-project-brief.md",
|
|
"docs/12-codex-max-workload.md",
|
|
"docs/13-task-board.md",
|
|
"docs/17-rendering-pipeline.md",
|
|
"docs/CHECKPOINTS.md",
|
|
],
|
|
"resume_commands": [
|
|
"./scripts/resume.sh",
|
|
"./scripts/audit.sh",
|
|
"./scripts/state.sh",
|
|
"./scripts/ci.sh",
|
|
"./scripts/checkpoint.sh \"note\"",
|
|
],
|
|
}
|
|
|
|
Path("out/session-state.json").write_text(json.dumps(state, indent=2, sort_keys=True) + "\n")
|
|
|
|
lines = []
|
|
lines.append("# Session State (generated)\n")
|
|
lines.append(f"- generated_at_utc: `{ts}`")
|
|
lines.append(f"- repo_root: `{root}`")
|
|
if git_branch and git_head_short:
|
|
lines.append(f"- git: `{git_branch}` @ `{git_head_short}`")
|
|
elif git_branch:
|
|
lines.append(f"- git: `{git_branch}`")
|
|
else:
|
|
lines.append("- git: `(not detected)`")
|
|
lines.append(
|
|
f"- working_tree: dirty_paths={dirty_count} (modified={modified_count}, untracked={untracked_count})"
|
|
)
|
|
if checkpoint_path:
|
|
lines.append(f"- latest_checkpoint: `{checkpoint_path}`")
|
|
lines.append(f"- latest_checkpoint_sha256: `{checkpoint_sha256}`")
|
|
else:
|
|
lines.append("- latest_checkpoint: `(none yet)`")
|
|
|
|
total_rules = None
|
|
try:
|
|
total_rules = counts.get("total_rules")
|
|
except Exception:
|
|
total_rules = None
|
|
if total_rules is not None:
|
|
lines.append(f"- rules_total: `{total_rules}`")
|
|
by_enf = counts.get("by_enforcement") or {}
|
|
by_cat = counts.get("by_category") or {}
|
|
if by_enf:
|
|
lines.append(f"- enforcement_split: `{by_enf}`")
|
|
if by_cat:
|
|
lines.append(f"- categories: `{by_cat}`")
|
|
else:
|
|
lines.append("- rules_total: `(unknown; run ./scripts/ci.sh)`")
|
|
|
|
lines.append("")
|
|
lines.append("## Canonical Docs")
|
|
for d in state["canonical_docs"]:
|
|
lines.append(f"- `{d}`")
|
|
|
|
lines.append("")
|
|
lines.append("## Fast Resume")
|
|
for c in state["resume_commands"]:
|
|
lines.append(f"- `{c}`")
|
|
|
|
lines.append("")
|
|
lines.append("## Notes")
|
|
lines.append(
|
|
"- If a chat/session resets and things look “rolled back”, check `latest_checkpoint` above and extract it on a new machine."
|
|
)
|
|
lines.append(
|
|
"- This file is copied into `docs/SESSION_STATE.md` so it is included in checkpoint snapshots (since `out/` is excluded)."
|
|
)
|
|
|
|
md = "\n".join(lines) + "\n"
|
|
Path("out/session-state.md").write_text(md)
|
|
Path("docs/SESSION_STATE.md").write_text(md)
|
|
PY
|
|
|
|
echo "wrote: out/session-state.json out/session-state.md docs/SESSION_STATE.md" >&2
|