# Queue-Runner: Same-Name Different-Content File Silently Dropped (2026-07-08) ## Incident 邱律师 sent two versions of "2026年华新镇公立中小学生健康体检服务合同.docx" on the same day: - 11:05 BJT (27889 bytes): generic version without fee cap - 16:04 BJT (27482 bytes): specific version with 华新镇 in project name, ¥170,000 fee cap, different start date Only the first was reviewed. The second was silently dropped. ## Root Cause Chain (3 components) ### 1. auto_notify manifest dedup (`grep -qFx`) ```bash # In auto_notify_new_file.sh: if ! grep -qFx "$orig_name" "$QUEUE_DIR/manifest.txt" 2>/dev/null; then echo "$orig_name" >> "$QUEUE_DIR/manifest.txt" fi ``` The filename was already in manifest.txt from the first file → second file NOT appended → manifest has only ONE entry for this filename. ### 2. Runner single-pass no-backtrack The runner reads manifest top-to-bottom in one pass. By 08:00:09 UTC it had already passed the 华新镇 line (first version was in done/ → "SKIP not found"). When auto_notify wrote the second file to queue/ at 08:04, the runner was already past that line processing later files. It never goes back. ### 3. done/ presence satisfies watchdog progress check `[ -e "$QUEUE_DIR/done/$f" ]` — the first version in done/ counts as "complete" for this manifest line. ## Key Principle (Doro 2026-07-08 铁律) **判断是否为相同文件不能只看文件名。** 必须检查合同实质内容: - 顾问单位(甲方)名称 - 金额/费用上限 - 合同期限(起止日期) - 项目内容描述 - 字节大小 以上任何一项不同 → 视为新版本/不同合同,正常入队审查。 全部相同 → 视为重复发送,可跳过。 ## Fix Implemented (2026-07-09) ### 1. Content comparison script: `~/.hermes/scripts/contract_content_compare.py` ``` Usage: python3 contract_content_compare.py Exit 0 = same content (duplicate) Exit 1 = different content (new version / different contract) Exit 2 = cannot read (treat as different, err on safe side) ``` Compares: - File size (bytes) - 甲方 name (regex extraction from first 2000 chars) - All amounts (阿拉伯数字 ≥4 digits + 元/万, 人民币XXX, percentages) - All dates (YYYY年M月D日 format) - Project summary (first 500 chars normalized) ### 2. auto_notify_new_file.sh modification Replaced the simple `grep -qFx` manifest dedup with content-level comparison: ```bash if grep -qFx "$orig_name" "$QUEUE_DIR/manifest.txt" 2>/dev/null; then # Same filename exists in manifest — compare content EXISTING="" # find in done/ or queue/ COMPARE_RESULT=$(python3 contract_content_compare.py "$EXISTING" "$filepath") if [ $? -eq 0 ]; then # Content identical → true duplicate, skip log "SKIP duplicate (content identical): $orig_name" return else # Content different → new version, rename with timestamp suffix and queue RENAMED="${orig_name%.*}_v${TS}.${orig_name##*.}" cp "$filepath" "$QUEUE_DIR/$RENAMED" echo "$RENAMED" >> "$QUEUE_DIR/manifest.txt" log "SAME NAME DIFFERENT CONTENT — queued as new: $RENAMED" fi else # Brand new filename, normal flow cp "$filepath" "$QUEUE_DIR/${orig_name}" echo "$orig_name" >> "$QUEUE_DIR/manifest.txt" fi ``` ### 3. Verification Tested with the actual 华新镇 two files: ``` $ python3 contract_content_compare.py file1.docx file2.docx DIFFERENT: 两份文件内容不同(新版本/不同合同) - 字节大小不同: 27889 vs 27482 - 金额不同: A多set(), B多{'170000'} - 日期不同: A多{'2026年9月10日'}, B多{'2026年9月1日'} - 内容不同(第212字起): '...年公立中小学生健康检查...' vs '...年华新镇公立中小学生健康体检...' ``` ## Key Lesson The initial diagnosis went through multiple wrong iterations: 1. First said "queue-runner only checks filename" — wrong (tracker guard also exists) 2. Then said "tracker guard is the one that skipped" — wrong (no tracker skip in logs) 3. Then said "整个链路没有内容比对" — wrong (Doro corrected: the system should have had it) **Correct diagnosis process**: trace the actual log timestamps precisely, verify each claim with tool output, don't assume mechanisms exist or don't exist without reading the actual code. **Doro's requirement**: "不能用文件名判断是否为相同文件,需要查看合同内容(包括顾问单位名称、金额、日期等)以及字节大小,要全面判断。" This is a capability requirement, not just a bug fix — the system must understand what makes two contracts "the same" at a business level.