From ec3a56837c59792f8c544f74ad8bc9e199f79b09 Mon Sep 17 00:00:00 2001 From: Mother Seara Date: Tue, 4 Aug 2026 23:13:54 +0900 Subject: [PATCH] =?UTF-8?q?fix(index-append):=20stop=20losing=20the=20conc?= =?UTF-8?q?lusion=20on=20numbered=20lists=20=E2=80=94=20and=20never=20miss?= =?UTF-8?q?=20silently?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The knowledge-index extractor assumed the "What was closed" section opened with a `- ` bullet. A numbered list (`1. …`) matched nothing, so the entry was indexed with an empty conclusion — while the close still succeeded and the log still printed "knowledge index appended". The failure was invisible. Observed in a real ledger, not hypothetically: 3 of 11 entries were empty, all three from the same cause, and the two most recent closes were consecutive misses — i.e. the newer writing style was silently draining the index. - take the section's first substantive line and strip only the list marker (`-`/`*`/`+`, `1.`, `1)`); `**1. …**` is content and is kept verbatim - warn loudly next to the success line when extraction yields nothing, so a miss is visible at close time. Still best-effort: it never blocks the close. - regression tests for each list style + the loud-miss path. Verified they fail against the old extractor (4 of 7 assertions), so the tests actually bite. Co-Authored-By: Claude Opus 5 (1M context) --- bin/index-append | 18 ++++++++++++++++-- tests/test_gates.sh | 26 ++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/bin/index-append b/bin/index-append index ef7b125..3b89a2f 100755 --- a/bin/index-append +++ b/bin/index-append @@ -28,11 +28,21 @@ t=open(sys.argv[1],encoding='utf-8',errors='ignore').read() m=re.search(r'\*\*Verdict\*\*:\s*(.+)',t); print((m.group(1).strip() if m else '-')[:160]) PY )" +# The old regex assumed the first item was a `- ` bullet, so a numbered list (`1. …`) matched +# nothing → '-'. The close still succeeded and the log still printed "appended" — the failure +# was invisible. Take the section's first substantive line and strip only the list marker. CLOSED="$(python3 - "$SUM" <<'PY' import re,sys t=open(sys.argv[1],encoding='utf-8',errors='ignore').read() -m=re.search(r'##\s*What was closed[^\n]*\n(?:>.*\n)*-\s*(.+)',t) -print((m.group(1).strip() if m else '-')[:240]) +m=re.search(r'##\s*What was closed[^\n]*\n(.*?)(?=\n##\s|\Z)', t, re.S) +out='-' +if m: + for ln in m.group(1).split('\n'): + s=ln.strip() + if not s or s.startswith('>'): continue + s=re.sub(r'^(?:[-*+]|\d+[.)])\s+','',s).strip() # list marker only ( `**1.` is content ) + if s: out=s; break +print(out[:240]) PY )" @@ -53,3 +63,7 @@ REL="${ARC_DIR#"$SCRIPT_DIR"/../}" printf -- '- source: %s\n\n' "$REL" } >> "$INDEX" echo " 📚 knowledge index appended: ${INDEX}" +# Never fail silently: if the conclusion could not be extracted, say so next to the success line. +# Still best-effort — this must not block the close. +[ "$CLOSED" = "-" ] && echo " ⚠️ could not extract 'What was closed' → entry indexed empty. Check the _SUMMARY format and fill this row by hand: $SUM" +exit 0 diff --git a/tests/test_gates.sh b/tests/test_gates.sh index 7f4acf1..6ca1a92 100755 --- a/tests/test_gates.sh +++ b/tests/test_gates.sh @@ -83,6 +83,32 @@ printf -- '- [x] decoy. verify: `false` verify: `true`\n' > "$T3" "$BIN/verify-gate" "$T3" --revert >/dev/null 2>&1 grep -q '^- \[ \] decoy' "$T3" && echo " ✓ decoy double-verify reverted (first block wins)" || { echo " ✗ decoy passed (greedy bug)"; FAIL=1; } +# --- index-append: the conclusion must survive any list style, and a miss must be loud --- +# Regression: the extractor used to assume a `- ` bullet, so a numbered list silently indexed +# an empty conclusion while still logging success. Observed in the wild, not hypothetical. +IDX_ARC="$WS/idx_arc"; mkdir -p "$IDX_ARC"; export YEOUL_INDEX="$WS/KI.md" +idx_case() { # idx_case + rm -f "$YEOUL_INDEX" + printf '# close\n- **Closed**: 2026-01-01\n- **stop_reason**: converged\n- **Verdict**: v\n\n## What was closed\n%s\n\n## Evidence\n- none\n' \ + "$2" > "$IDX_ARC/_SUMMARY_idx_arc.md" + "$BIN/index-append" "$IDX_ARC" >/dev/null 2>&1 + grep -qF "$3" "$YEOUL_INDEX" && echo " ✓ index-append: $1" || { echo " ✗ index-append: $1 — '$3' not indexed"; FAIL=1; } +} +idx_case "bullet list" '- bullet conclusion' '**Closed**: bullet conclusion' +idx_case "numbered list" '1. numbered conclusion' '**Closed**: numbered conclusion' +idx_case "paren-numbered" '1) paren conclusion' '**Closed**: paren conclusion' +idx_case "bold-numbered kept" '**1. bold conclusion**' '**Closed**: **1. bold conclusion**' +idx_case "leading blockquote" '> note line +- after the quote' '**Closed**: after the quote' +# a genuine miss must be visible in the output, not silent +rm -f "$YEOUL_INDEX" +printf '# close\n- **Closed**: 2026-01-01\n- **stop_reason**: converged\n- **Verdict**: v\n\n## Evidence\n- none\n' \ + > "$IDX_ARC/_SUMMARY_idx_arc.md" +"$BIN/index-append" "$IDX_ARC" 2>&1 | grep -q "could not extract" \ + && echo " ✓ index-append warns loudly when extraction misses" \ + || { echo " ✗ index-append failed silently"; FAIL=1; } +"$BIN/index-append" "$IDX_ARC" >/dev/null 2>&1; assert "index-append never blocks the close" 0 $? + echo if [ "$FAIL" -eq 0 ]; then echo "✅ all gate tests passed"; else echo "⛔ gate tests FAILED"; fi exit "$FAIL"