diff --git a/.claude/memory/blockers.md b/.claude/memory/blockers.md index 8f45b50..b2322c5 100644 --- a/.claude/memory/blockers.md +++ b/.claude/memory/blockers.md @@ -42,7 +42,7 @@ rules: | BLK-020 | 2026-09-02 | notify-attention: both channels dead on one VS Code client — 2026-09-02 | resolved | | BLK-021 | 2026-09-22 | Bash tool dead mid-session ("every command exits 1"): /tmp usrquota blown by a dead session's probe HOMEs — 2… | open | | BLK-022 | 2026-09-22 | `hooks/guard-bash.sh` withheld by the safety classifier; executable spec shipped instead — 2026-09-22 | open | -| BLK-023 | 2026-09-28 | floor-guard SKIP pattern `xit(` (Jasmine) matches any `exit(` in python/JS test helpers → false ECARTS; workaround: no `exit(` in inline python, bash derives rc from output — 2026-09-28 | open | +| BLK-023 | 2026-09-28 | floor-guard SKIP pattern `xit(` (Jasmine) matches any `exit(` in python/JS test helpers → false ECARTS; workaround: no `exit(` in inline python, bash derives rc from output — 2026-09-28 | resolved | --- @@ -267,4 +267,4 @@ rules: - **Friction**: fresh verifier returned ECARTS(1) on a fully conform diff: `FLOOR SKIP lib/tests/profile-census.test.sh:116 sys.exit(1 if violations else 0)`. One re-dispatch spent on a tool artefact. - **Real cause**: `lib/floor-guard.sh` SKIP_SUBSTRINGS holds the bare fragment `'xit('` to catch Jasmine's `xit(…)`; `skip_kind()` is a plain substring match, so `sys.exit(`, `SystemExit(`, `process.exit(` all hit. - **Solution**: workaround applied — the inline python prints violations only, the bash wrapper derives the return code from the captured output (no `exit(` anywhere). Root fix pending: word-bound the pattern (`(^|[^a-zA-Z_.])xit\(`) or match `xit(` only in JS/TS test files; hotfix-sized. -- **Status**: open. Links [[BDR-105]], [[BDR-102]] (floor-guard origin), [[EVAL-034]]. +- **Status**: resolved 2026-09-28 — hotfix 0deb559 (bugfix/floor-guard-xit-boundary): the four bare Jasmine identifiers moved into `SKIP_IDENT_RE` with lookbehind `(? fais le hotfix du floor-guard +> BLK-023: lib/floor-guard.sh SKIP pattern `xit(` (meant for Jasmine's xit) matches any `exit(` / `SystemExit(` / `process.exit(` in python or JS test helpers → false FLOOR SKIP finding (ECARTS on a conform diff, 2026-09-28). Fix: make the Jasmine match word-bounded so `sys.exit(` no longer trips it; keep `xit(` detection for a real Jasmine `xit(` at line start or after a non-identifier char. Add the two regression cases to lib/tests/floor-guard.test.sh (a python `sys.exit(1)` line must NOT flag; a JS ` xit('skipped', ...)` line MUST flag). + +## CLARIFICATIONS +- Pass A: silent autofill (hotfix). Pass B: nothing visible or public is open (an internal matcher; message text unchanged). +- [challenge 2026-09-28: simplicity SOLID, correctness SOLID, robustness CONCERNS(2), all closed by named plan changes, r2] fixture echo lines in lib/tests/floor-guard.test.sh carry `# floor-guard: allow flip-test fixture` outside the echoed string (a test-path diff scan would flag the fixture itself; WAIVED on a test file is informational and authorized here); the vacuous live clause left criterion 2; `def fit(` / `function xit(` / `xit.each(` behave as before and are recorded as a `shortcut:` comment (upgrade path named there), out of hotfix scope. +- Root cause (LOCATE): `skip_kind` (lib/floor-guard.sh:188-189) is a plain substring test over SKIP_SUBSTRINGS; the bare-identifier entries `'xit('`, `'fit('`, `'xdescribe('`, `'fdescribe('` therefore match inside longer identifiers (`exit(`, `SystemExit(`, `process.exit(`, `model.fit(`, `profit(`). The dotted/decorator entries (`.skip(`, `.only(`, `it.todo(`, `@pytest.mark.skip`, `@unittest.skip`, `t.Skip(`) are unaffected. +- Fix (closed): the four bare identifiers move out of SKIP_SUBSTRINGS into one compiled regex with an identifier-boundary lookbehind, `(?&1); echo "$out" | grep -qE 'FAIL=[1-9]' && { echo "$out" | tail -12; exit 1; }; for k in SKIP SKIP_EXIT_CLEAN SKIP_XIT_FLAGS SKIP_FIT_FLAGS SKIP_FDESCRIBE_FLAGS; do echo "$out" | grep -q "PASS $k" || { echo "missing PASS $k"; exit 1; }; done; echo FLOOR_SUITE_GREEN + EXPECT: FLOOR_SUITE_GREEN + EVIDENCE: MET exit=0 marker-found :: FLOOR_SUITE_GREEN +2. Build/tests green: shellcheck on the test, bash syntax of the guard, the regex compiles, and the guard's own diff is clean (its new lines are not on a test path). [challenge: the former census clause was vacuous — the file no longer holds an `exit(` — and is dropped; SKIP_EXIT_CLEAN in criterion 1 is the regression proof] + CHECK: shellcheck lib/tests/floor-guard.test.sh && bash -n lib/floor-guard.sh && python3 -c "import re;re.compile(r'(?&1 | grep -q 'FLOOR GUARD: clean' && echo BUILD_OK + EXPECT: BUILD_OK + EVIDENCE: MET exit=0 marker-found :: BUILD_OK + +## FILE SCOPE +- lib/floor-guard.sh (SKIP_SUBSTRINGS + skip_kind), lib/tests/floor-guard.test.sh (two cases) diff --git a/.claude/tasks/plans/2026-09-28-floor-guard-xit-boundary-1648.md b/.claude/tasks/plans/2026-09-28-floor-guard-xit-boundary-1648.md new file mode 100644 index 0000000..f8d3a81 --- /dev/null +++ b/.claude/tasks/plans/2026-09-28-floor-guard-xit-boundary-1648.md @@ -0,0 +1,63 @@ +# PLAN — floor-guard-xit-boundary (hotfix, logic fix → challenged) — r2 +- r2 after 3 challengers (simplicity SOLID, correctness SOLID, robustness + CONCERNS(2)): SKIP_IDENT_RE sits right under SKIP_SUBSTRINGS; the waiver + instruction on the guard's own lines is gone (SKIP never scans + lib/floor-guard.sh: no `test`/`spec` in its path); the new fixture echo + lines in the TEST file carry `# floor-guard: allow flip-test fixture` + OUTSIDE the echoed string (the test path contains `test`, so a later scan + of that diff would flag the fixture itself); `def fit(` / `function xit(` + / `xit.each(` stay unmatched or matched as before and are recorded as a + `shortcut:` comment; fixtures extended to prove the whole alternation. +- date: 2026-09-28 | contract: contracts/2026-09-28-floor-guard-xit-boundary-1648.md +- branch: bugfix/floor-guard-xit-boundary | executor: hotfixer (sonnet) + +## Root cause +lib/floor-guard.sh:99-102 SKIP_SUBSTRINGS = ('.skip(', '.only(', 'xit(', +'xdescribe(', 'fit(', 'fdescribe(', 'it.todo(', '@pytest.mark.skip', +'@unittest.skip', 't.Skip('); :188-189 `skip_kind(text)` = `any(p in text …)`. +Plain substring: `xit(` ⊂ `exit(`, `SystemExit(`, `process.exit(`; `fit(` ⊂ +`profit(`, `model.fit(`. Only test files are scanned for SKIP (line_findings, +`if test_file:`), so the false positive hits inline python/JS helpers inside +test files — the 2026-09-28 case: `sys.exit(1 if violations else 0)` in +lib/tests/profile-census.test.sh (BLK-023). + +## The exact edit (lib/floor-guard.sh) +1. SKIP_SUBSTRINGS keeps only the dotted/decorator forms: + ('.skip(', '.only(', 'it.todo(', '@pytest.mark.skip', '@unittest.skip', 't.Skip('). +2. New `SKIP_IDENT_RE = re.compile(r'(? {});` + (leading spaces on purpose); run; `check_kind SKIP_XIT_FLAGS "$rc" 2 + "$out" 'FLOOR SKIP'`. Then two more repos in the same block proving the + rest of the alternation: `fit('focused', () => {});` → `check_kind + SKIP_FIT_FLAGS … 2 … 'FLOOR SKIP'`; `fdescribe('focused', () => {});` → + `check_kind SKIP_FDESCRIBE_FLAGS … 2 … 'FLOOR SKIP'`. +Header comment (:2-3) gains "plus boundary cases for SKIP" after "one CLEAN +fixture". + +## Not changed +Messages, exit codes, waiver syntax, other kinds, test harness helpers.