Accept first survey batch + add commit-hash post-processing

Spot-check of the three drafts gemini produced in the --max 3
survey smoke test: all cited file paths exist, line numbers and
function names land within 1-2 lines of actual symbols
(game.pyx:217 cdef class GameState, evaluate.py:220 batched-entropy
block, trainer.py:892 _evaluate_parallel, action_distribution at
evaluate.py:238 with cited code at line 250 inside it). Numbers
cross-checked against archives match. Conclusions preserved.

The one systemic weakness was the Last-verified commit field:
gemini left a `<short-hash>` placeholder, a literal `HEAD`, or
omitted the commit entirely depending on the call. Fixed in two
places:

1. Manually patched the three drafts before acceptance and copied
   them into docs/research/.
2. Added _current_commit_sha and _post_process_draft helpers to
   both librarian_survey.py and librarian_promote.py. The drafts
   now go through `**Last verified:**` line normalization that
   substitutes today's date and `git rev-parse --short HEAD`
   before being written to disk. Future runs converge
   deterministically.

Net: docs/research/ gains classic-port-notes.md,
deep-cfr-batched-evaluation.md, and deep-cfr-evaluation-profile.md.
12 archive entries remain unprocessed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-08 01:52:08 +09:00
co-authored by Claude Opus 4.7
parent b0b385591b
commit 5c221fb3c6
6 changed files with 209 additions and 5 deletions
+33 -3
View File
@@ -24,7 +24,6 @@ from __future__ import annotations
import argparse
import os
import re
import shutil
import subprocess
import sys
from datetime import datetime
@@ -37,6 +36,7 @@ LLM_COMMANDS = {
}
DATE_SUFFIX = re.compile(r"-\d{4}-\d{2}-\d{2}$")
LAST_VERIFIED = re.compile(r"^\*\*Last verified:\*\*[^\n]*$", re.MULTILINE)
def _repo_root() -> Path:
@@ -47,6 +47,34 @@ def _repo_root() -> Path:
raise RuntimeError("pyproject.toml을 찾을 수 없어 repository root를 판정할 수 없습니다.")
def _current_commit_sha(root: Path) -> str:
"""Resolve HEAD to a short SHA. Returns 'unknown' if git is unavailable."""
result = subprocess.run(
["git", "rev-parse", "--short", "HEAD"],
capture_output=True,
text=True,
check=False,
cwd=root,
)
if result.returncode != 0:
return "unknown"
return result.stdout.strip() or "unknown"
def _post_process_draft(content: str, commit_sha: str) -> str:
"""Normalize a draft before writing to disk.
The LLM is unreliable about running `git` itself in headless mode; it
tends to leave `<short-hash>` placeholders or literal `HEAD` strings.
Rewrite the `**Last verified:**` line in place with today's date and
the real short SHA. If the line is missing entirely, leave the draft
untouched so the omission stays visible during review.
"""
today = datetime.now().strftime("%Y-%m-%d")
new_line = f"**Last verified:** {today}, commit `{commit_sha}`"
return LAST_VERIFIED.sub(new_line, content, count=1)
def _assemble_prompt(
system_prompt: str,
rel_archive: Path,
@@ -167,7 +195,9 @@ def main() -> int:
print(result.stderr, file=sys.stderr)
return result.returncode
draft_path.write_text(result.stdout, encoding="utf-8")
commit_sha = _current_commit_sha(root)
draft_text = _post_process_draft(result.stdout, commit_sha)
draft_path.write_text(draft_text, encoding="utf-8")
print(f"Draft written to: {draft_path.relative_to(root)}")
if args.accept:
@@ -177,7 +207,7 @@ def main() -> int:
file=sys.stderr,
)
return 1
shutil.copyfile(draft_path, target)
target.write_text(draft_text, encoding="utf-8")
print(f"Copied to: {rel_target}")
print("Next: review the diff and `git add` + commit if you're satisfied.")
else:
+33 -2
View File
@@ -40,6 +40,7 @@ LLM_COMMANDS = {
}
DATE_SUFFIX = re.compile(r"-\d{4}-\d{2}-\d{2}$")
LAST_VERIFIED = re.compile(r"^\*\*Last verified:\*\*[^\n]*$", re.MULTILINE)
def _repo_root() -> Path:
@@ -78,6 +79,34 @@ def _assemble_prompt(
)
def _current_commit_sha(root: Path) -> str:
"""Resolve HEAD to a short SHA. Returns 'unknown' if git is unavailable."""
result = subprocess.run(
["git", "rev-parse", "--short", "HEAD"],
capture_output=True,
text=True,
check=False,
cwd=root,
)
if result.returncode != 0:
return "unknown"
return result.stdout.strip() or "unknown"
def _post_process_draft(content: str, commit_sha: str) -> str:
"""Normalize a draft before writing to disk.
The LLM is unreliable about running `git` itself in headless mode; it
tends to leave `<short-hash>` placeholders or literal `HEAD` strings.
Rewrite the `**Last verified:**` line in place with today's date and
the real short SHA. If the line is missing entirely, leave the draft
untouched so the omission stays visible during review.
"""
today = datetime.now().strftime("%Y-%m-%d")
new_line = f"**Last verified:** {today}, commit `{commit_sha}`"
return LAST_VERIFIED.sub(new_line, content, count=1)
def _has_counterpart(archive_stem_no_date: str, research_stems: set[str]) -> bool:
"""True if a research note already covers this archive entry.
@@ -150,6 +179,7 @@ def main() -> int:
return 1
system_prompt = (root / "scripts" / "librarian-prompt.md").read_text(encoding="utf-8")
commit_sha = _current_commit_sha(root)
timestamp = datetime.now().strftime("%Y-%m-%d_%H%M%S")
out_dir = root / "runs" / "tmp" / f"librarian-survey-{timestamp}"
@@ -186,9 +216,10 @@ def main() -> int:
skips += 1
print(f" SKIP ({output[:80]})", file=sys.stderr)
else:
(out_dir / f"{stem_out}.md").write_text(output, encoding="utf-8")
normalized = _post_process_draft(output, commit_sha)
(out_dir / f"{stem_out}.md").write_text(normalized, encoding="utf-8")
drafts += 1
print(f" draft ({len(output)} chars)", file=sys.stderr)
print(f" draft ({len(normalized)} chars)", file=sys.stderr)
print()
print(f"Survey complete: {drafts} drafts, {skips} skips, {errors} errors.")