Preserve explicit withdrawal and byte-exact AAMAS fixture evidence
Metadata integrity checks / metadata (push) Failing after 2m4s

This commit is contained in:
2026-11-10 01:00:58 +08:00
parent 37380e1d3b
commit 930df2a4d5
6 changed files with 42 additions and 12 deletions
+1
View File
@@ -60,6 +60,7 @@ jobs:
subprocess.run([python,'-I','-B','-m','paper_atlas','collect','aamas-proceedings','--year','2026','--snapshots',str(aamas2026_snapshots),'--out',str(root/'installed-aamas2026')],cwd=outside,check=True)
aamas2026_report = json.loads((root/'installed-aamas2026/aamas-proceedings-2026.json').read_text())
assert aamas2026_report['records'] == 639 and len(aamas2026_report['held_publications']) == 1
assert aamas2026_report['status_counts'] == {'accepted': 638, 'withdrawn': 1}
assert not aamas2026_report['database_written'] and not aamas2026_report['pdfs_downloaded']
subprocess.run([python,'-I','-B','-m','paper_atlas','collect','iclr-archive','--help'],cwd=outside,check=True)
subprocess.run([python,'-I','-B','-m','paper_atlas','collect','acl','--help'],cwd=outside,check=True)
+1 -1
View File
@@ -9,7 +9,7 @@
排除 3 个 keynote;research 的 p238 明确标注 `Retracted June 8, 2026`,完整题名、作者、栏目、日期、原文和来源回执保存在采集报告 `held_publications` 中,**不会输出 accepted 或 withdrawn 投稿记录**。当前决定模型没有独立 retracted 状态,后续须单独建模和核验;原始证据不能删除。任何新增或改变的撤稿标记都会停止采集,等待人工核查。
其余 639 个条目按六个 track 输出:`proceedings_unspecified` 522、`aaai_transfer` 19、`blue_sky` 17、`doctoral` 34、`demo` 44、`jaamas` 3。全文/扩展摘要是发表形式,不是研究类型;research 栏目不能证明每篇原始 main/special 投稿轨道。JAAMAS 保留期刊展示语义,不制造另一次独立审稿录用。
另一个 extended abstract p3178 明确标注 `Withdrawn June 8, 2026`,输出 `withdrawn` 和日期 `2026-06-08`,不补造其此前的接受决定或 PDF 链接。因此 639 个输出条目是 **638 accepted、1 withdrawn**,按六个 track 分组:`proceedings_unspecified` 522、`aaai_transfer` 19、`blue_sky` 17、`doctoral` 34、`demo` 44、`jaamas` 3。全文/扩展摘要是发表形式,不是研究类型;research 栏目不能证明每篇原始 main/special 投稿轨道。JAAMAS 保留期刊展示语义,不制造另一次独立审稿录用。
[AAAI 转投详细政策](https://cyprusconferences.org/aamas2026/call-for-papers-aaai-track/) 要求基于 AAAI-26 rejected 稿件;[总览](https://cyprusconferences.org/aamas2026/calls/) 却写 AAAI-25,须保留冲突。采集器保留 AAAI 栏目归属,不据此生成原 AAAI 投稿 ID、年份或逐篇拒稿决定。目录不是原始决定 note,未来关联仍需逐篇证据。
+25 -8
View File
@@ -26,6 +26,7 @@ SECTIONS = {
}
PDF = re.compile(r'\.\./pdfs/([A-Z]{4}[0-9]{4})\.pdf')
RETRACTION_TITLE = 'Unifying Model-Free Efficiency and Model-Based Representations via Latent Dynamics'
WITHDRAWAL_TITLE = 'Cognitive Heterogeneity and Behavioral Biases in Multi-Stage Supply Chains: Evidence from LLM-Based Agent Simulations'
def parse_authors(paragraph):
@@ -98,7 +99,16 @@ def parse_contents(html, evidence):
'decision_date_precision': 'not_supplied',
'missing_abstract_reason': 'Publisher HTML supplies no abstract; research PDF not downloaded.',
'publication_evidence': 'Published proceedings entry, not an original decision note.'}
if retractions:
withdrawn = retractions == ['Withdrawn June 8, 2026']
if withdrawn:
if (section, page, title, href) != ('1', 3178, WITHDRAWAL_TITLE, ''):
raise ValueError('Unreviewed withdrawal notice')
metadata.update({'publication_state': 'withdrawn',
'withdrawal_date': '2026-06-08',
'raw_publication_state': retractions[0],
'decision_date_precision': 'day',
'publication_evidence': 'Explicit dated publisher withdrawal notice, not an original review decision note.'})
elif retractions:
if (section, page, title, href, retractions) != ('R', 238, RETRACTION_TITLE, '', ['Retracted June 8, 2026']):
raise ValueError('Unreviewed retraction or publication status markup')
held.append({'source': 'aamas_proceedings', 'external_id': '2026/p238',
@@ -109,11 +119,12 @@ def parse_contents(html, evidence):
'raw_publication_state': retractions[0],
'hold_reason': 'Explicit retraction; not an accepted/withdrawn submission assertion. Dedicated decision modeling and identity review required.'})
continue
match = PDF.fullmatch(href)
if not match or match[1] in seen_pdf_ids:
raise ValueError('Invalid or repeated publisher PDF ID')
seen_pdf_ids.add(match[1])
metadata.update({'publisher_document_id': match[1], 'paper_url': ROOT+'pdfs/'+match[1]+'.pdf'})
if not withdrawn:
match = PDF.fullmatch(href)
if not match or match[1] in seen_pdf_ids:
raise ValueError('Invalid or repeated publisher PDF ID')
seen_pdf_ids.add(match[1])
metadata.update({'publisher_document_id': match[1], 'paper_url': ROOT+'pdfs/'+match[1]+'.pdf'})
if section == 'K':
excluded.append({'publisher_page': page, 'title': title, 'authors': authors,
'metadata': metadata, 'reason': 'Publisher keynote, not a research submission.'})
@@ -127,15 +138,19 @@ def parse_contents(html, evidence):
records.append({'source': 'aamas_proceedings', 'external_id': '2026/p'+str(page),
'url': URL+'#'+section, 'title': title, 'abstract': None,
'authors': authors, 'identifiers': {}, 'venue': 'AAMAS', 'venue_aliases': [],
'year': 2026, 'track': track, 'status': 'accepted',
'year': 2026, 'track': track, 'status': 'withdrawn' if withdrawn else 'accepted',
'observed_at': evidence['observed_at'],
'raw_decision': 'listed in official AAMAS 2026 published proceedings '+heading,
'raw_decision': retractions[0] if withdrawn else 'listed in official AAMAS 2026 published proceedings '+heading,
'metadata': metadata,
'tags': [{'namespace': 'aamas_proceedings:publication_format', 'value': publication_format}]})
if withdrawn:
records[-1]['decision_date'] = '2026-06-08'
if headers != list(SECTIONS) or dict(counts) != {k: v[3] for k, v in SECTIONS.items()}:
raise ValueError('Publisher section inventory differs from reviewed 2026 layout')
if (len(records), len(excluded), len(held)) != (639, 3, 1):
raise ValueError('Unexpected accepted/keynote/retraction partition')
if Counter(r['status'] for r in records) != {'accepted': 638, 'withdrawn': 1}:
raise ValueError('Unexpected publisher publication-state partition')
return records, excluded, held
@@ -181,6 +196,7 @@ def collect(out, *, cache=None, snapshots=None):
'track': track, 'count': len(group), 'file': str(out/p.name),
'file_sha256': hashlib.sha256(p.read_bytes()).hexdigest(), 'list_url': URL,
'status': 'partial', 'missing_abstract_count': len(group),
'status_counts': dict(Counter(r['status'] for r in group)),
'publication_format_counts': dict(Counter(r['metadata']['publication_format'] for r in group)),
'note': 'Reviewed TOC partition excluding keynote/retracted entries; identity and full submission history remain unverified.'}
p.with_suffix('.manifest.json').write_text(json.dumps(manifest, ensure_ascii=False, indent=2)+'\n')
@@ -188,6 +204,7 @@ def collect(out, *, cache=None, snapshots=None):
result = {'source': 'aamas_proceedings', 'year': 2026, 'finished_at': timestamp(),
'status': 'collected_pending_identity_review', 'records': len(records), 'files': files,
'excluded_publications': excluded, 'held_publications': held,
'status_counts': dict(Counter(r['status'] for r in records)),
'source_receipts': [evidence], 'database_written': False, 'pdfs_downloaded': False,
'note': 'Retraction evidence held separately; publication is not an original decision note, and absence is not rejection.'}
report = 'aamas-proceedings-2026.json'
Binary file not shown.
+14 -2
View File
@@ -39,8 +39,19 @@ class Aamas2026Tests(unittest.TestCase):
for r in rows:
self.assertIsNone(r['abstract'])
self.assertEqual(r['identifiers'], {})
self.assertEqual(r['status'], 'accepted')
self.assertNotIn('decision_date', r)
if r['external_id'] != '2026/p3178':
self.assertEqual(r['status'], 'accepted')
self.assertNotIn('decision_date', r)
def test_explicit_withdrawal_preserves_status_date_and_no_pdf(self):
rows, _, _ = self.parse()
row, = [r for r in rows if r['external_id'] == '2026/p3178']
self.assertEqual(row['status'], 'withdrawn')
self.assertEqual(row['decision_date'], '2026-06-08')
self.assertEqual(row['raw_decision'], 'Withdrawn June 8, 2026')
self.assertNotIn('paper_url', row['metadata'])
self.assertEqual(row['metadata']['publication_state'], 'withdrawn')
self.assertEqual(sum(r['status'] == 'accepted' for r in rows), 638)
def test_retraction_is_not_accepted_or_withdrawn_and_retains_date(self):
rows, _, held = self.parse()
@@ -85,6 +96,7 @@ class Aamas2026Tests(unittest.TestCase):
root = Path(tmp)
report = aamas.collect(2026, root/'out', snapshots=self.snapshots(root/'source'))
self.assertEqual(report['records'], 639)
self.assertEqual(report['status_counts'], {'accepted': 638, 'withdrawn': 1})
self.assertEqual(len(report['files']), 6)
self.assertEqual(len(report['held_publications']), 1)
self.assertFalse(report['database_written'])
+1 -1
View File
@@ -99,7 +99,7 @@ class AamasProceedingsTests(unittest.TestCase):
aamas.collect(2012, root/'out', snapshots=source)
self.assertFalse((root/'out/aamas-proceedings-2012.json').exists())
with self.assertRaises(ValueError):
aamas.collect(2026, root/'future', snapshots=source)
aamas.collect(2027, root/'future', snapshots=source)
self.assertFalse((root/'future').exists())
def test_ids_colliding_across_sections_do_not_produce_a_completed_collection(self):