From 2d25c2fa04407469f6a6c729631316772f0934fc Mon Sep 17 00:00:00 2001 From: bastien Date: Tue, 22 Sep 2026 16:34:34 +0200 Subject: [PATCH] chore(memory): BDR-095 amendment, BLK-021 cause established, journal + TODO G8 --- .claude/memory/blockers.md | 4 +++- .claude/memory/decisions.md | 1 + .claude/memory/journal.md | 2 ++ .claude/tasks/TODO.md | 13 ++++++++++++- 4 files changed, 18 insertions(+), 2 deletions(-) diff --git a/.claude/memory/blockers.md b/.claude/memory/blockers.md index abb944f..2cbe9c7 100644 --- a/.claude/memory/blockers.md +++ b/.claude/memory/blockers.md @@ -247,7 +247,9 @@ rules: - **Friction**: previous session on `feature/21st-cli-migration` lost its shell before tests + commit: every Bash call, `echo` included, returned 1. Its harness file `imptest2/step8d-test.sh` landed as 0 bytes. - **Real cause** (strong evidence, not reproduced on purpose): `/tmp` = tmpfs 7.4 GB mounted `usrquota`; `/tmp/claude-1000/-home-bchanot-Documents-claude/fefd277c-…/scratchpad` holds 5.9 GB of sandbox HOMEs (`pinprobe/` 2.1 GB, `pinrc/` 1.6 GB, `imp1 impg imptest sbx1 sbx2 v3.2.0 v3.6.1 v4.0.5 …`) from the impeccable pin probes. `dd` 40 MB to `/tmp/claude-1000` → "Disk quota exceeded" (EDQUOT) while `df` still shows 1.6 GB avail. Same write to `~/.cache` OK. Bash tool + `mktemp` + heredocs live in /tmp → all die together. This session: first impeccable probe failed with `Quota exceeded (os error 122)` on the installer's `/tmp/impeccable-update-*` staging, same cause. - **Solution**: this round ran everything with `TMPDIR=~/.cache/imp-probe/tmp` (probe, harness, `make test`). Durable fix = delete the dead session's scratchpad: `rm -rf /tmp/claude-1000/-home-bchanot-Documents-claude/fefd277c-e143-4d51-b589-a566641079b5` (agent's `rm -rf` on /tmp denied by the classifier → user action). Rule for probes: sandbox HOMEs that pull npm/node payloads go under `~/.cache//`, never the /tmp scratchpad, and get removed at the end of the session. -- **Status**: open until the user frees /tmp. Links [[BDR-094]], [[LRN-159]]. +- **Recurred same day**: this session's shell died the same way mid-G8 (BDR-095 amendment) while the 5.9 GB still sat there; recovered the moment the user deleted the dir. Mechanism now established, not inferred: stock `/usr/lib/systemd/system/tmp.mount` mounts /tmp with `x-systemd.graceful-option=usrquota` (no override, no fstab line on this machine) and systemd caps each user at 80% of the tmpfs → 0.8 × 7.4 GB = 5.9 GB, the exact volume observed. One quota for every session AND every sub-agent of the uid: multi-session is not the cause, the shared cap is. +- **Durable fix**: (1) launch claude with `TMPDIR=$HOME/.cache/claude-tmp` (in `~/.bashrc` `dtach_claude()`, before `exec claude`; `mkdir -p` it) → Claude Code's scratchpad, tool outputs, `mktemp` and npm staging all leave the tmpfs; children inherit. (2) `~/.config/user-tmpfiles.d/claude-tmp.conf` with `e %h/.cache/claude-tmp - - - 3d` + the user `systemd-tmpfiles-clean.timer` so dead-session dirs age out. (3) `make doctor` "Scratchpad" section warns while TMPDIR sits on a quota'd tmpfs. Probe rule unchanged: HOMEs with npm payloads under `~/.cache//`. +- **Status**: cause established; open until the launcher exports TMPDIR (user's .bashrc, hand-managed). Links [[BDR-094]], [[BDR-095]], [[LRN-159]]. ## BLK-022 — `hooks/guard-bash.sh` withheld by the safety classifier; executable spec shipped instead — 2026-09-22 - **Friction**: layer C item G2 (PreToolUse Bash guard: whole-command scan incl. nested `bash -c`, `docker compose run … lftp`, scripts the command runs; exit 2 + reason + `logger` trace; fail-closed without jq). The response carrying the hook body was stopped by a safety classifier mid-write; content withheld, instruction not to regenerate it. diff --git a/.claude/memory/decisions.md b/.claude/memory/decisions.md index 13bdc1a..252dbf9 100644 --- a/.claude/memory/decisions.md +++ b/.claude/memory/decisions.md @@ -1204,3 +1204,4 @@ Branch feature/user-writing-web-rules, UNMERGED (human gate). - **Alternatives rejected**: `ask` tier → doc says it prompts under auto, LRN-155 probe says inert, unresolved → nothing entrusted to ask. Keep docker volume drops in soft_deny (BDR-092) → "à tout prix" beats in-turn convenience; user runs them by hand. Post-commit hook alone for push → `git merge` fires post-merge, not post-commit (T18f caught it) → lib pushes the target explicitly AND post-merge hook emitted. Force-push allowance after amend → static deny stays; a rejected push warns and the user decides. Stop-hook `decision: block` on unpushed work → BDR-083 refused control-flow hooks; systemMessage only. - **Status**: accepted, on feature/destructive-guardrails. `gitflow-test.sh` T18 7/7 + T19 3/3, unpushed-guard 9/9, `make test` green minus 2 pre-existing T16a, shellcheck clean, doctor 0 errors. NOT DONE: `hooks/guard-bash.sh` ([[BLK-022]]). Existing projects need `gitflow install-hook` re-run for the push hooks. - **Reference**: `settings.json`, `lib/gitflow.sh`, `.githooks/{post-commit,post-merge}`, `hooks/unpushed-guard.sh`, `lib/tests/{guard-bash,unpushed-guard}.test.sh`, `CLAUDE.global.md`, `templates/settings/SETTINGS.md`, `agents/{verifier,plan-challenger,analyzer,security-auditor}.md`. Extends [[BDR-090]] [[BDR-092]] (ask inert, soft_deny doctrine); links [[LRN-114]] (T19 drift gate), [[LRN-155]], [[BDR-083]]. +- **Amendment 2026-09-22 (user go: "je valide les deux")**: no per-project `install-hook` step. (a) GLOBAL: `make link` runs `gitflow global-hooks` → generates `githooks/` from the emitters + `git config --global core.hooksPath ~/.claude/githooks` (symlinked into the repo) → every repo on the machine is protected + auto-pushed, gitflow-initialized or not (faunosteo class). Git precedence: a repo's local `core.hooksPath` wins. (b) RECONCILE: `hooks/session-start.sh` calls `gitflow reconcile-hooks` once per session → rewrites a lagging `.githooks/` (LRN-114 automated), banner line + commit reminder; pre-commit whitelist extended to `.githooks/**` so that refresh commits on develop. (c) Opt-outs per repo (foreign clone): `git config gitflow.protect false`, `gitflow.autopush false` — human-only, static deny on `git config gitflow.*` and on the `GIT_CONFIG_GLOBAL=`/`GIT_CONFIG=` env bypass. (d) Hermetic tests: `make test` + the two suites committing on `main` export `GIT_CONFIG_GLOBAL=/dev/null`, else the machine's global hooks fire in throwaway repos. (e) doctor: global setting + `githooks/` == emitted. Tests T18h, T19d, T20, T21. Rejected: `init.templateDir` (new repos only, ignored once hooksPath is set); dropping the per-repo `.githooks/` (portability to a machine without claude-config). Status: verified once /tmp was freed — gitflow 127/129 (2 pre-existing T16a), review-guards G5 flagged this repo's own stale `.githooks/` (the LRN-114 gate doing its job; refreshed via install-hook), shellcheck clean. `make link` (global `core.hooksPath`) refused to the agent by the classifier twice → user runs it. Doctor gained a "Scratchpad" check ([[BLK-021]] mechanism). diff --git a/.claude/memory/journal.md b/.claude/memory/journal.md index 5881e65..f642e8e 100644 --- a/.claude/memory/journal.md +++ b/.claude/memory/journal.md @@ -488,4 +488,6 @@ rules: - Both lots (21st CLI migration + impeccable repair) merged into develop on user go, `gitflow finish` → 33e0899, pushed to origin. Feature branch deleted by the lib. Machine-owned `skills/impeccable`, `skills/graphify`, `agents/impeccable-*.md` verified still on disk after the merge (LRN-154 class). - Incident 21/09 analysed from `/mnt/cloudpex/RECOVERY` + surviving transcripts: process identified = atlast reviewer sub-agent's `lftp mirror --delete` trace on a `file://` path, uid 1000, Gitea ran as bchanot ([[LRN-160]]). This machine still had: `lxd` group, rw NAS mount uid=1000, no restic, no managed settings, agent-writable settings.json. Layers A/B handed to the user. - Layer C on feature/destructive-guardrails ([[BDR-095]]): static deny for transfer/destructive tools, hard_deny "destructive tool against a local path, brief ≠ user authority", gitflow pushes at start/merge + post-commit/post-merge hooks, unpushed-guard hook, doctrine + agents. T18 caught that `git merge` skips post-commit. Guard hook body withheld by the safety classifier → spec-only ([[BLK-022]]). Branch UNMERGED. +- Hooks everywhere, user go ([[BDR-095]] amendment): global `core.hooksPath` via `make link` + generated `githooks/`, session-start `reconcile-hooks`, `gitflow.protect`/`autopush` opt-outs, hermetic `GIT_CONFIG_GLOBAL=/dev/null` in tests, doctor check, T18h/T19d/T20/T21. Written via Read/Edit only: the Bash tool died on the /tmp quota ([[BLK-021]], same failure as 21/09) before `make link`, `make test` and the commit. Second safety-classifier stop in the session (content withheld, not regenerated). +- /tmp freed by the user → shell back. G8 verified (gitflow 127/129, review-guards G5 caught the repo's stale `.githooks/`, refreshed). Quota mechanism found: systemd's stock `tmp.mount` carries `x-systemd.graceful-option=usrquota` and each user is capped at 80% of the tmpfs (5.9 GB of 7.4 GB = the exact volume that killed both shells); no override on this machine. Durable fix = `TMPDIR=$HOME/.cache/claude-tmp` in the `dtach_claude()` launcher + a tmpfiles age rule; doctor "Scratchpad" check added. `make link` denied to the agent → user. diff --git a/.claude/tasks/TODO.md b/.claude/tasks/TODO.md index fe4eb98..336d060 100644 --- a/.claude/tasks/TODO.md +++ b/.claude/tasks/TODO.md @@ -39,7 +39,18 @@ settings) are the user's; this branch = layer C (config repo) + auto-push. "trace by reading, never by running" clause. - [x] G7 docs: `templates/settings/SETTINGS.md` (hook tier, ask caveat), CHANGELOG, BDR-095, LRN-160, journal. `make test` + shellcheck. - Existing projects: re-run `gitflow install-hook` to get post-commit. +- [x] G8 hooks everywhere, no per-project step (user go 2026-09-22): global + `core.hooksPath ~/.claude/githooks` set by `make link` from a generated + `githooks/`; `gitflow reconcile-hooks` at session start refreshes a + lagging `.githooks/`; opt-outs `gitflow.protect` / `gitflow.autopush`; + pre-commit exempts `.githooks/**`; doctor check; hermetic + `GIT_CONFIG_GLOBAL=/dev/null` in `make test` + 2 suites; deny on the + env bypass forms; T18h T19d T20 T21. Verified after the /tmp cleanup: + gitflow 127/129 (2 pre-existing T16a), review-guards G5 caught this + repo's stale `.githooks/` (refreshed via install-hook), shellcheck + clean, doctor "Scratchpad" check added. OPEN for the user: `make link` + (sets the global `core.hooksPath`; denied to the agent), and launch + claude with `TMPDIR=$HOME/.cache/claude-tmp` in `dtach_claude()`. Out of scope here (user's side): restic append-only, lxd group, NAS mount, managed-settings.json + sandbox, per-project accounts, docker rootless.