diff --git a/.gitea/workflows/check.yml b/.gitea/workflows/check.yml index 24bcbc3..5e15e55 100644 --- a/.gitea/workflows/check.yml +++ b/.gitea/workflows/check.yml @@ -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) diff --git a/docs/aamas-proceedings.md b/docs/aamas-proceedings.md index 7249500..aa730db 100644 --- a/docs/aamas-proceedings.md +++ b/docs/aamas-proceedings.md @@ -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,未来关联仍需逐篇证据。 diff --git a/paper_atlas/collectors/aamas2026.py b/paper_atlas/collectors/aamas2026.py index 5b1c2f1..02be581 100644 --- a/paper_atlas/collectors/aamas2026.py +++ b/paper_atlas/collectors/aamas2026.py @@ -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' diff --git a/tests/fixtures/aamas2026-fixture.json.gz b/tests/fixtures/aamas2026-fixture.json.gz index 3816639..8befe6f 100644 Binary files a/tests/fixtures/aamas2026-fixture.json.gz and b/tests/fixtures/aamas2026-fixture.json.gz differ diff --git a/tests/test_aamas2026.py b/tests/test_aamas2026.py index 973233b..0508b88 100644 --- a/tests/test_aamas2026.py +++ b/tests/test_aamas2026.py @@ -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']) diff --git a/tests/test_aamas_proceedings.py b/tests/test_aamas_proceedings.py index 92be220..3b32ff2 100644 --- a/tests/test_aamas_proceedings.py +++ b/tests/test_aamas_proceedings.py @@ -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):