Skip to content

Commit f3e8838

Browse files
fix: add phase g release profile
1 parent d1a8417 commit f3e8838

1 file changed

Lines changed: 107 additions & 0 deletions

File tree

scripts/paper/check_paper_release_packet.py

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,31 @@
8686
r"Public\s+Score[-\s]?File\s+Boundary:\s+MoFit",
8787
r"MoFit\s+is\s+a\s+support-only\s+score-file\s+diagnostic",
8888
]
89+
PHASE_G_PDF_FORBIDDEN_PATTERNS = [
90+
r"D:\\",
91+
r"C:\\",
92+
r"Users\\",
93+
r"Documents\\",
94+
r"\bsecret\b",
95+
r"\bapi[_ -]?key\b",
96+
r"\btoken\b",
97+
r"\bHotCRP\b",
98+
r"\bCodex\b",
99+
r"\bChatGPT\b",
100+
r"\bClaude\b",
101+
r"\bsubmission\s+deadline\b",
102+
r"USENIX\s+Security\s+2027",
103+
r"NDSS\s+2027\s+fall",
104+
]
105+
PHASE_G_PDF_REQUIRED_PATTERNS = [
106+
r"Run\s+Identity\s+in\s+Diffusion\s+Membership\s+Inference",
107+
r"training\s+run\s+identity\s+(?:is\s+)?an\s+audit\s+variable",
108+
r"AUC\s+0\.648",
109+
r"AUC\s+0\.81",
110+
r"candidate-positive",
111+
r"run-sensitive",
112+
r"single\s+evaluator",
113+
]
89114
LOG_FORBIDDEN_PATTERNS = [
90115
r"Undefined",
91116
r"Citation.*undefined",
@@ -1935,6 +1960,73 @@ def validate_bibliography(paper: Path, errors: list[str]) -> None:
19351960
require(not BIB_PLACEHOLDER_RE.search(entry), f"{key} contains placeholder BibTeX text", errors)
19361961

19371962

1963+
def validate_phase_g_manifest_inputs(paper: Path, manifest: dict, errors: list[str]) -> None:
1964+
excluded = {
1965+
normalize_manifest_relpath(str(path), errors)
1966+
for path in manifest.get("anonymous_supplement_excluded", [])
1967+
if isinstance(manifest.get("anonymous_supplement_excluded", []), list)
1968+
}
1969+
requested: list[str] = []
1970+
for category in ("generated", "curated", "paper_sources"):
1971+
paths = manifest.get(category)
1972+
require(isinstance(paths, list) and paths, f"manifest.{category} must be a non-empty list", errors)
1973+
if isinstance(paths, list):
1974+
requested.extend(str(path) for path in paths)
1975+
requested.extend(["asset_manifest.json", "paper.pdf"])
1976+
1977+
for raw_rel in requested:
1978+
rel = normalize_manifest_relpath(raw_rel, errors)
1979+
if rel in excluded:
1980+
continue
1981+
path = paper / rel
1982+
require(path.exists(), f"phase-g supplement input missing: {rel}", errors)
1983+
if not path.exists() or path.suffix.lower() not in {".bib", ".csv", ".json", ".md", ".tex", ".txt"}:
1984+
continue
1985+
text = path.read_text(encoding="utf-8", errors="replace")
1986+
for pattern in (r"(?<![A-Za-z0-9_])[A-Za-z]:(?:\\\\|\\)[A-Za-z0-9_. -]", r"\\Users\\[A-Za-z0-9_. -]", r"OPENAI_API_KEY", r"sk-[A-Za-z0-9_-]{12,}"):
1987+
require(not re.search(pattern, text, flags=re.IGNORECASE), f"{rel} contains private-surface pattern: {pattern}", errors)
1988+
1989+
1990+
def validate_phase_g_pdf(paper: Path, errors: list[str]) -> None:
1991+
pdf_path = paper / "paper.pdf"
1992+
require(pdf_path.exists(), "paper.pdf is missing", errors)
1993+
if not pdf_path.exists():
1994+
return
1995+
1996+
pdfinfo = run_text(["pdfinfo", str(pdf_path)], paper)
1997+
pages_match = re.search(r"^Pages:\s+(\d+)$", pdfinfo, flags=re.MULTILINE)
1998+
require(bool(pages_match), "pdfinfo output missing Pages", errors)
1999+
if pages_match:
2000+
pages = int(pages_match.group(1))
2001+
require(1 <= pages <= 20, f"paper.pdf page count outside Phase G guardrail: {pages}", errors)
2002+
2003+
pdf_text = run_text(["pdftotext", str(pdf_path), "-"], paper)
2004+
for pattern in PHASE_G_PDF_FORBIDDEN_PATTERNS:
2005+
require(not re.search(pattern, pdf_text, flags=re.IGNORECASE), f"paper.pdf private/submission-planning hit: {pattern}", errors)
2006+
for pattern in PHASE_G_PDF_REQUIRED_PATTERNS:
2007+
require(bool(re.search(pattern, pdf_text, flags=re.IGNORECASE)), f"paper.pdf missing Phase G phrase: {pattern}", errors)
2008+
2009+
2010+
def validate_phase_g_latex_log(paper: Path, errors: list[str]) -> None:
2011+
candidates = [paper / "main.log", paper / "build" / "main.log"]
2012+
log_path = next((path for path in candidates if path.exists()), candidates[0])
2013+
require(log_path.exists(), "main.log is missing", errors)
2014+
if not log_path.exists():
2015+
return
2016+
text = log_path.read_text(encoding="utf-8", errors="replace")
2017+
for pattern in LOG_FORBIDDEN_PATTERNS:
2018+
require(not re.search(pattern, text, flags=re.IGNORECASE), f"LaTeX log forbidden hit: {pattern}", errors)
2019+
2020+
2021+
def validate_phase_g_release_packet(paper: Path, errors: list[str]) -> None:
2022+
manifest = validate_manifest(paper, errors)
2023+
if manifest:
2024+
validate_phase_g_manifest_inputs(paper, manifest, errors)
2025+
validate_phase_g_pdf(paper, errors)
2026+
validate_phase_g_latex_log(paper, errors)
2027+
validate_bibliography(paper, errors)
2028+
2029+
19382030
def validate_reference_integrity_audit(paper: Path, errors: list[str]) -> None:
19392031
refs_path = paper / "refs.bib"
19402032
audit_path = paper / REFERENCE_INTEGRITY_AUDIT
@@ -2216,6 +2308,12 @@ def validate_c14_external_review_status(
22162308

22172309
def main() -> None:
22182310
parser = argparse.ArgumentParser(description=__doc__)
2311+
parser.add_argument(
2312+
"--profile",
2313+
choices=["legacy-c14", "phase-g"],
2314+
default="legacy-c14",
2315+
help="Validation profile. legacy-c14 preserves the original C14 release gate; phase-g checks the current H1/run-identity manuscript packet.",
2316+
)
22192317
parser.add_argument(
22202318
"--c14-review-state",
22212319
choices=["pre-label", "post-label"],
@@ -2241,6 +2339,15 @@ def main() -> None:
22412339
)
22422340
errors: list[str] = []
22432341

2342+
if args.profile == "phase-g":
2343+
validate_phase_g_release_packet(paper, errors)
2344+
if errors:
2345+
for error in errors:
2346+
print(f"ERROR: {error}")
2347+
raise SystemExit(1)
2348+
print("Phase G release packet check passed.")
2349+
return
2350+
22442351
manifest = validate_manifest(paper, errors)
22452352
validate_claim_trace(paper, repo_root, errors)
22462353
validate_review_snapshot_manifest(paper, repo_root, manifest, errors)

0 commit comments

Comments
 (0)