diff --git a/.claude/settings.json b/.claude/settings.json index 6a33fe5..6f63ba3 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -22,7 +22,9 @@ "Bash(git push -f *)", "Bash(sudo *)", "Read(**/.env)", - "Read(**/.env.*)" + "Read(**/.env.local)", + "Read(**/.env.*.local)", + "Read(**/.env.production)" ] } } diff --git a/.claude/workflow.lock.json b/.claude/workflow.lock.json index 9d58184..a364def 100644 --- a/.claude/workflow.lock.json +++ b/.claude/workflow.lock.json @@ -12,10 +12,10 @@ ".claude/skills/gc/SKILL.md": "abfb7282d3000eb30b872df8313a17aa2c60a54e5951034c0bdfaa3da59bde71", ".claude/skills/handoff/SKILL.md": "1289ffc479b7761066212b576d1a22c8c134e3aeed7bff5945642acc439a3eab", ".claude/skills/parallel-work/SKILL.md": "94fbb922cf88fc005fd8cfa033c1c3559adbfde04fd759ca5f3c696f2afd9afe", - ".claude/workflow/check.py": "c3e23ff58df8e13365d69c85a5a54d1bfbc9557f08d745e426426715e09d6220", + ".claude/workflow/check.py": "f2e69f3c29d75e116ed3812bc3a33af7129a82c8d32b17d8782d8d4aed7ce29a", ".claude/workflow/gc.py": "30b0191f1171a1fe9d5f6bf10579a8eaa8a06db49da9a8437376df5bebb70af4", "docs/plans/_template.md": "9178c6a0a9807241ecd8d5624f9e07f94f6b5181e4a2290f44fd49f0c8b08d3f" }, - "kit": "c9485b3 2026-11-06", + "kit": "9a00ece 2026-11-06", "level": "full" } diff --git a/.claude/workflow/check.py b/.claude/workflow/check.py index d8c367a..635ae41 100755 --- a/.claude/workflow/check.py +++ b/.claude/workflow/check.py @@ -15,8 +15,10 @@ - Every path-scoped rule in .claude/rules/ has a strict `paths:` header whose fixed prefixes exist (a header that fails to parse makes the rule load in every session instead). - docs/decisions.md, if present: under its byte budget, lines within the length limit, each theme - heading names an existing detail file; every detail entry opens with a status line; every index - line is claimed by an entry marked `live, indexed`, and every claim names an index line. + heading names an existing detail file, each detail file under its own budget when one is set + (entries that no longer apply move to docs/decisions/archive/, which is not checked); every + detail entry opens with a status line; every index line is claimed by an entry marked + `live, indexed`, and every claim names an index line. - docs/plans/README.md, if present: its "Active plans" list agrees with each dated plan's `Status:` line (a closed plan is not listed; an open one is). """ @@ -33,6 +35,7 @@ "claude_md_max_bytes": 14336, "decisions_index_max_bytes": 20000, "decisions_line_max_chars": 250, + "decisions_theme_max_bytes": 0, # 0 = no cap; new repositories are seeded with 40960 } STATUS = re.compile( r"^\*\*Status:\*\* (live, indexed|live, not indexed|superseded by \d{4}-\d{2}-\d{2}" @@ -144,7 +147,14 @@ def check_decisions(root: Path, cfg: dict, r: Report) -> None: if not detail.exists(): r.fail("decisions", f"theme heading names {file}, which does not exist") continue - lines = detail.read_text(encoding="utf-8-sig").split("\n") + text = detail.read_text(encoding="utf-8-sig") + cap = cfg.get("decisions_theme_max_bytes") or 0 + if cap and len(text.encode()) > cap: + size = len(text.encode()) + r.fail("decisions", f"{file} is {size} bytes, over {cap}: move superseded, withdrawn " + f"and record entries to docs/decisions/archive/{file}, which " + "nothing loads or checks") + lines = text.split("\n") claims: list[tuple[int, str]] = [] for i, line in enumerate(lines): h = HEADING.match(line)