Skip to content

v0.2.0: full overhaul - audited defects fixed, guarded LLM extraction, verification ledger, tests + CI - #5

Merged
SakJaeLim merged 2 commits into
mainfrom
improve/v0.2.0-overhaul
Jul 27, 2026
Merged

v0.2.0: full overhaul - audited defects fixed, guarded LLM extraction, verification ledger, tests + CI#5
SakJaeLim merged 2 commits into
mainfrom
improve/v0.2.0-overhaul

Conversation

@SakJaeLim

Copy link
Copy Markdown
Owner

v0.2.0: Full overhaul — every audited defect fixed, guarded LLM extraction, verification ledger, 179 tests + CI

독립 감사(신규 클론을 Python 3.10~3.13에서 실측 + 전체 코드 리뷰)에서 발견된 결함 전체를 수정하고, "유명 오픈소스 논문 자동화 도구들의 장점을 취합한 마스터 파이프라인"이라는 원래 취지에 맞는 업그레이드를 더한 릴리스입니다. 모든 결함은 수정 전 재현했고, 수정 후 실측으로 재검증했습니다.

P0 — 사용 자체를 막던 결함 (전부 실측 재현 후 수정)

결함 수정
Python 3.10/3.11에서 CLI 전체가 SyntaxError (f-string 내 백슬래시, PEP 701) — requires-python>=3.10 선언과 모순 한 줄 수정 + CI에서 3.10~3.13 전체 컴파일 검증
review-evidence-candidates가 다른 논문의 사람 리뷰 결정을 삭제 (공유 큐 덮어쓰기) 병합 쓰기로 전환 — B 논문 큐 재생성 후에도 A 논문 결정 보존 (회귀 테스트: Excel 재저장 왕복 포함)
Excel 왕복이 검증 체계 파괴 (BOM→헤더 오류, cp949→크래시, 잉여 열→쓰기 중 크래시) 전 CSV 읽기 utf-8-sig+잉여열 흡수, 쓰기 BOM 포함(한글 정상 표시), 매트릭스 재작성 전부 원자적(temp+rename)
collect가 주제 무관 논문 수집 (arXiv 쿼리 오구성→오늘자 무관 논문, OpenAlex 전 요청 429, Crossref "Decision letter" 수집) 불리언 쿼리+relevance 정렬, mailto/API키/UA를 sources.yaml에서 실제 배선, Retry-After 백오프, 타입 필터, 소스별 서킷브레이커. 실측: 수집 71건 전부 주제 적합, 429 소멸, 중복 0
migrate-evidence'1'/'yes'verified=true로 승격 — 핵심 불변식의 유일한 구멍 리터럴 true만 보존, truthy 변형은 false로 강등+보고서 명시

거버넌스 — 시스템의 존재 이유를 완성

  • verify-evidence + 검증 원장 (matrices/verification_ledger.csv): verified=true로 가는 유일한 공인 경로. --by 이름 --attest 확약 필수, 모든 행위(verify/revoke)가 원장에 기록. guard-no-auto-verified는 이제 원장에 확약이 있는 verified 행은 통과시키고(기존: 정당한 인간 검증 후 가드가 영구 실패), 원장에 없는 verified 행은 실패시킴 — 자동화든 손 편집이든. 공격 시뮬레이션으로 실측 검증.
  • 승인 게이트 위조 방지: 부분 문자열 4개 검사 → apply 보고서의 SHA-256을 현재 원고 파일과 대조 (오래되었거나 조작된 보고서 거부)
  • 원고 백업을 가드 트리 밖(backups/manuscript/)으로 이동 — 가드 자가 발동 및 citekey 이중 집계 해소
  • sync-zotero: --dry-run --apply 조합 차단, citekey 리네임을 Evidence Matrix까지 전파(백업 동반)
  • 레거시 extract-evidence(스키마 파괴 append) 차단, 레거시 audit의 이메일 오탐(@gmail)·substring 오판정 수정

신규 기능 (마스터 파이프라인 업그레이드)

  • extract-evidence-llm — LLM 보조 근거 추출(가드됨). 모델은 후보를 제안만 하고, 모든 exact_quote는 GROBID 파싱 원문과 축자(verbatim) 대조 — 패러프레이즈·창작 인용문은 폐기·보고. 생존 후보는 휴리스틱과 동일한 사람 검토→승격→가드 apply를 거침. OpenAI 호환 엔드포인트 지원. (config/prompts/evidence_extractor.md가 죽은 설정에서 실설정으로)
  • 초안 수치 자동 대조audit-manuscript-draft --experiment-data DIR: 초안의 모든 수치를 실험 산출 파일과 자동 대조(정확 일치/반올림 일치/데이터에 없음). README가 약속했지만 코드에 없던 대표 기능의 실구현.
  • 초안 감사 강화: docx 각주 파싱(각주 인용 문장은 출처 있음 처리), cp949 자동 감지(기존: 조용히 파괴), 한국어 인용 (김철수, 2020)·[1] 인식, 영어 강주장/과장 탐지, 헤딩 앵커 장 구조 검사, config/draft_audit.yaml
  • Figure 안전성: 라인 앵커+코드펜스 인지 헤딩 매칭(## X 승인이 ### X Details 아래에 삽입되던 버그), 모호/부재 헤딩은 차단, 멱등 apply, 정직한 보고서+preview SHA, config/figures.yaml, 렌더러 부재 시 우아한 강등(--strict로 엄격 모드)
  • make-page-metadata-preview / make-outline-insertion-template — 생성자가 없어 dead end였던 두 가드 입력 파일의 생성기
  • promote-evidence --ready-only, PDF 다운로드 content-type 검사(HTML 유료벽 저장 차단), backup에 papers.sqlite 포함, 원클릭 파이프라인 ASCII화+우아한 강등
  • 릴리스 강화: 비밀 스캔 4패턴→명명된 룰셋(전체 이메일/GitHub 토큰류/개인키/AWS/JWT/설정 대입형), export 시 config 자동 새니타이즈, 실패 시 격리(QUARANTINE), 화이트리스트 누락 보완

재사용성·품질

  • 개인 연구 데이터를 코드에서 config로: 리뷰 결정→review_overrides.yaml, 코퍼스 키워드→qa_profile.yaml, figure 스펙→figures.yaml (저자 동작은 그대로 보존)
  • S2 수집을 메인 collect에 통합(API 키 헤더), 미등록·크래시하던 paperops_extra.py 제거
  • 미사용 의존성 4종 제거(pandas/rapidfuzz/bibtexparser/pypdf — 설치 실패 원인 포함), v0.2.0
  • 테스트 179개 + GitHub Actions CI(3.10/3.11/3.12/3.13 컴파일+테스트+오프라인 스모크)
  • README(en/ko) 정직화 — 미구현이던 주장은 구현으로 참이 되게, 남는 한계는 Honest limitations에 명시

검증 요약 (전부 이 브랜치에서 실측)

  • 컴파일: 3.10/3.11/3.12/3.13 전부 통과 · 테스트: 179 passed (3.10, 3.12)
  • 신규 클론 → init → collect(라이브 API) → score/screen/gap/cards → 후보 추출 → 2개 논문 리뷰 병합(+Excel 재저장) → 승격 → verify-evidence → 가드 통과 → 무단 verified 편집 → 가드 실패 확인 → 원복 → 가드 통과
  • 수치 대조 실측: 5건 중 4건 exact match, 1건 not-found 정확 판정
  • run_pipeline.py 원클릭 완주(exit 0), build_public_release.py 클린(sanitize_issues=0)

Merge 후 권장: GitHub Actions 첫 실행 확인, 로컬에서 python scripts/paperops.py doctor로 mailto/API 키 설정.

🤖 Generated with Claude Code

https://claude.ai/code/session_01ULLxKwEG7wBaj6SDFeBmsA

…action, verification ledger, tests + CI

Driven by an independent live audit (fresh-clone runs on Python 3.10-3.13 +
line-by-line review). Highlights:

P0 fixes (all live-reproduced before fixing):
- CLI was a SyntaxError on Python 3.10/3.11 (f-string backslash, PEP 701)
- review-evidence-candidates wiped other papers' human review decisions
  (shared queue overwrite -> merge)
- Excel round-trip broke validators (BOM/cp949/stray columns) -> utf-8-sig
  reads, BOM writes, atomic rewrites with row projection
- collect gathered off-topic papers: arXiv boolean queries + relevance sort,
  OpenAlex/Crossref mailto + backoff + Retry-After, Crossref type filters,
  cross-source DOI/arXiv/title dedupe, per-source circuit breaker
- migrate-evidence could flip '1'/'yes' to verified=true -> only literal
  'true' survives; demotions reported

Governance upgrades:
- verify-evidence + matrices/verification_ledger.csv: the only sanctioned
  writer of verified=true (human attestation required); guards now PASS
  attested rows and FAIL unattested ones (previously the guard failed
  forever after any legitimate human verification)
- approval-gate anti-forgery: apply reports cross-checked against current
  manuscript SHA-256
- manuscript backups moved outside the guarded tree; citekey scans skip
  backups; sync-zotero respects --dry-run and propagates citekey renames
  into the Evidence Matrix; legacy extract-evidence/audit fixed or blocked

New capabilities:
- extract-evidence-llm: LLM-proposed candidates with mandatory verbatim
  quote verification against GROBID text; same human gates as heuristics
- audit-manuscript-draft --experiment-data: automatic numeric cross-check
  (exact/rounded/not-found) against experiment output files — makes the
  README's flagship claim true in code
- draft audit: docx footnotes, cp949 fallback, Korean/English detectors,
  heading-anchored structure checks; figures: line-anchored heading match,
  honest reports, YAML specs, graceful renderer degradation
- make-page-metadata-preview / make-outline-insertion-template producers;
  promote-evidence --ready-only
- release hardening: named secret-scan ruleset, config sanitization,
  quarantine-on-failure, whitelist gaps closed

Hygiene: personal research data moved from code to config YAMLs; sources.yaml
actually wired; paperops_extra.py removed (S2 in main collect); 4 unused deps
dropped; 179 tests + GitHub Actions matrix (3.10-3.13); README en/ko truth
pass; CHANGELOG added.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ULLxKwEG7wBaj6SDFeBmsA

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1a88983098

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/paperops.py
duplicate_ids = sorted(k for k, v in count_values([r.get('candidate_id') for r in queue_rows]).items() if k and v > 1)
# MERGE, never overwrite: rows belonging to OTHER papers stay in the queue untouched.
# Regenerating paper B's queue must not delete paper A's pending human decisions.
other_rows = [r for r in existing_rows if str(r.get('paper_id') or '') != str(args.paper_id or '')]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve filtered rows from the same paper

When review-evidence-candidates is run with --min-confidence, --claim-type, or --use-in-section, this predicate discards every existing row for the current paper, including rows excluded only by the filter. Any human decisions on those rows are consequently erased and revert to pending when the full queue is regenerated. Merge by candidate ID and retain same-paper rows that were not selected by the current filtered invocation.

Useful? React with 👍 / 👎.

Comment thread scripts/paperops.py
Comment on lines +2820 to +2821
if ready_only:
warnings.append(f'--ready-only: proceeding with {validation["ready_count"]} ready rows; {validation["blocked_count"]} blocked rows reported but not applied')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Make ready-only ignore validation errors from blocked rows

--ready-only still cannot process the normal mixed preview produced by promotion-plan: validate_promotion_preview() has already added global errors for each row whose promotion_ready is false or whose promotion_blockers is nonempty. This branch merely changes the blocked-count error into a warning, so the existing errors make the subsequent if not errors false and no ready rows are written. In ready-only mode, validation failures attributable solely to blocked rows must remain row-level blockers rather than global apply errors.

Useful? React with 👍 / 👎.

Comment thread scripts/paperops.py
Comment on lines +3507 to +3511
entry is not None
and str(entry.get('action') or '').strip() == 'verify'
and str(entry.get('verified_by') or '').strip()
and str(entry.get('verified_by') or '').strip() == str(r.get('verified_by') or '').strip()
and str(entry.get('verified_at') or '').strip() == str(r.get('verified_at') or '').strip()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Validate the attested quote hash in the verification guard

After a row is verified, changing its quote/exact_quote while leaving verified_by and verified_at unchanged still makes this guard classify it as attested. verify-evidence records quote_sha256 in the ledger specifically for this purpose, but the guard never compares it with the current row, so CI's guard-no-auto-verified can pass evidence that is no longer what the human checked. Include the current quote hash in the attestation match, and similarly invalidate verification when other attested source metadata changes.

Useful? React with 👍 / 👎.

Comment on lines +682 to +685
for sent in split_sentences(para['text']):
n_sentences += 1
mentions = bool(SOURCE_MENTION.search(sent))
strong = STRONG_CLAIM.search(sent)
over = OVERCLAIM.search(sent)
numeric = NUMERIC_RESULT.search(sent)
hypo = bool(HYPOTHETICAL.search(sent))
has_source = sentence_has_source(sent, det,
para['has_footnote_ref'])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Associate DOCX footnotes with the cited sentence

For a multi-sentence DOCX paragraph containing one footnote reference, has_footnote_ref is a paragraph-wide boolean and is passed to every sentence. A citation attached to one sentence therefore suppresses strong_claim_no_source for unrelated strong claims elsewhere in that paragraph. Preserve the reference position while flattening the DOCX runs, or split/map references before invoking sentence_has_source so only the cited sentence is treated as sourced.

Useful? React with 👍 / 👎.

README.md (en) and README.ko.md were updated in the previous commit; the other
six shipped language versions still described v0.1.0. All eight are now
consistent, so a non-English reader is not handed stale instructions.

Applied to de/es/fr/ja/zh/ar, translated in each file's existing register:
- 45+ -> 50+ commands; intro now covers verbatim-checked LLM extraction and
  numeric cross-checking against experiment outputs
- verified=true described as reachable only via verify-evidence with a human
  attestation recorded in the verification ledger, cross-checked by guards
- lifecycle rows 1/5/13/14 rewritten (4 sources + boolean queries + polite
  pool + 429 backoff + dedup; extract-evidence-llm; --experiment-data;
  human-gate verification)
- comparison table, quickstart (Python 3.10+, openalex_mailto polite-pool
  note), add-ons table (+Semantic Scholar key, +OPENAI_API_KEY), typical
  session (--experiment-data, verify-evidence --attest)
- KCI anecdote now states the numeric cross-check was manual at the time and
  automatic since v0.2.0 — it is not retroactively claimed as automated
- 6 governance rules and the new honest-limitations list

Also fixes two release-build regressions from the previous commit:
- drop deleted scripts/paperops_extra.py from the export whitelist
- reword a CHANGELOG line so the hardened secret scanner no longer flags an
  illustrative email address

Verified: all 8 READMEs pass the same four content checks, all four tables in
every file match the English column shape, language selectors and figure paths
byte-identical, release build clean (sanitize_issues=0, 8 READMEs shipped),
179 tests still pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ULLxKwEG7wBaj6SDFeBmsA
@SakJaeLim
SakJaeLim merged commit 47f749f into main Jul 27, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants