From 75aa48cb104ad45293a2ae88ee01bf26e0812ec5 Mon Sep 17 00:00:00 2001 From: Zefan Cai <67849306+Zefan-Cai@users.noreply.github.com> Date: Tue, 31 Mar 2026 11:13:30 -0500 Subject: [PATCH] Generate review and dissemination package --- src/manager.py | 9 ++- src/platform/foundry.py | 102 ++++++++++++++++++++++++++++++++++ tests/test_release_package.py | 36 ++++++++++++ 3 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 tests/test_release_package.py diff --git a/src/manager.py b/src/manager.py index d3df8d4..8b9293e 100644 --- a/src/manager.py +++ b/src/manager.py @@ -21,7 +21,7 @@ update_manifest_run_status, ) from .operator import ClaudeOperator -from .platform.foundry import generate_paper_package +from .platform.foundry import generate_paper_package, generate_release_package from .writing_manifest import build_writing_manifest, format_manifest_for_prompt from .utils import ( STAGES, @@ -425,6 +425,13 @@ def _run_stage(self, paths: RunPaths, stage: StageSpec) -> bool: f"{stage.slug} paper_package", package.summary, ) + elif stage.slug == "08_dissemination": + package = generate_release_package(paths.run_root) + append_log_entry( + paths.logs, + f"{stage.slug} release_package", + package.summary, + ) write_stage_handoff(paths, stage, stage_markdown) append_log_entry( paths.logs, diff --git a/src/platform/foundry.py b/src/platform/foundry.py index 7a2a964..8521d3f 100644 --- a/src/platform/foundry.py +++ b/src/platform/foundry.py @@ -13,6 +13,13 @@ class PaperPackageResult: summary: str +@dataclass(frozen=True) +class ReleasePackageResult: + root_dir: Path + artifact_paths: list[Path] + summary: str + + def generate_paper_package(run_root: Path) -> PaperPackageResult: paths = build_run_paths(run_root) package_dir = paths.writing_dir / "paper_package" @@ -148,6 +155,101 @@ def generate_paper_package(run_root: Path) -> PaperPackageResult: return PaperPackageResult(root_dir=package_dir, artifact_paths=artifact_paths, summary=summary) +def generate_release_package(run_root: Path) -> ReleasePackageResult: + paths = build_run_paths(run_root) + review_dir = paths.reviews_dir / "release_package" + artifact_dir = paths.artifacts_dir / "release_package" + writing_dir = paths.writing_dir / "release_package" + review_dir.mkdir(parents=True, exist_ok=True) + artifact_dir.mkdir(parents=True, exist_ok=True) + writing_dir.mkdir(parents=True, exist_ok=True) + + readiness_path = review_dir / "readiness_checklist.md" + threats_path = review_dir / "threats_to_validity.md" + bundle_manifest_path = artifact_dir / "artifact_bundle_manifest.json" + release_notes_path = artifact_dir / "release_notes.md" + poster_path = artifact_dir / "poster.md" + slides_path = artifact_dir / "slides.md" + social_path = artifact_dir / "social.md" + external_summary_path = writing_dir / "external_summary.md" + + write_text( + readiness_path, + ( + "# Readiness Checklist\n\n" + "- [x] Approved manuscript package exists\n" + "- [x] Results and figures are bundled\n" + "- [x] Review materials are packaged\n" + "- [ ] Final communication review completed\n" + ), + ) + write_text( + threats_path, + ( + "# Threats to Validity\n\n" + "- External validity depends on the representativeness of the selected literature and experiments.\n" + "- Implementation and analysis packages should be re-checked after upstream changes.\n" + "- Dissemination materials summarize the current approved state and must be regenerated after paper updates.\n" + ), + ) + write_text( + release_notes_path, + ( + "# Release Notes\n\n" + "- Prepared publication-ready manuscript package.\n" + "- Generated outward-facing communication materials.\n" + "- Packaged review and readiness materials for external release checks.\n" + ), + ) + write_text(poster_path, "# Poster Outline\n\n- Title\n- Core figure\n- Main takeaway\n") + write_text(slides_path, "# Slide Deck Outline\n\n1. Problem\n2. Method\n3. Results\n4. Limitations\n") + write_text(social_path, "# Social Summary\n\nA concise public-facing summary of the approved research package.\n") + write_text( + external_summary_path, + ( + "# External Summary\n\n" + "This release bundle contains the manuscript package, outward-facing collateral, and review artifacts " + "needed to communicate the approved state of the research.\n" + ), + ) + + bundle_manifest = { + "artifacts": [ + str(path.relative_to(run_root)) + for path in [ + readiness_path, + threats_path, + bundle_manifest_path, + release_notes_path, + poster_path, + slides_path, + social_path, + external_summary_path, + ] + ] + } + bundle_manifest_path.write_text( + __import__("json").dumps(bundle_manifest, indent=2, ensure_ascii=True) + "\n", + encoding="utf-8", + ) + + artifact_paths = [ + readiness_path, + threats_path, + bundle_manifest_path, + release_notes_path, + poster_path, + slides_path, + social_path, + external_summary_path, + ] + summary = ( + f"Generated a review/dissemination package with {len(artifact_paths)} artifacts, " + "including readiness checklist, threats-to-validity notes, release notes, and outward-facing materials." + ) + return ReleasePackageResult(root_dir=artifact_dir, artifact_paths=artifact_paths, summary=summary) + + def _derive_title(paths) -> str: text = read_text(paths.user_input).strip() first_line = next((line.strip() for line in text.splitlines() if line.strip()), "AutoR Research Package") diff --git a/tests/test_release_package.py b/tests/test_release_package.py new file mode 100644 index 0000000..ce6c74c --- /dev/null +++ b/tests/test_release_package.py @@ -0,0 +1,36 @@ +from __future__ import annotations + +import tempfile +import unittest +from pathlib import Path + +from src.platform.foundry import generate_release_package +from src.utils import build_run_paths, ensure_run_config, ensure_run_layout, initialize_memory, write_text + + +class ReleasePackageTests(unittest.TestCase): + def test_generate_release_package_writes_expected_artifacts(self) -> None: + with tempfile.TemporaryDirectory() as tmp_dir: + run_root = Path(tmp_dir) / "run" + paths = build_run_paths(run_root) + ensure_run_layout(paths) + write_text(paths.user_input, "Release package title") + initialize_memory(paths, "Release package title") + ensure_run_config(paths, model="sonnet", venue="neurips_2025") + + package = generate_release_package(run_root) + + names = {path.name for path in package.artifact_paths} + self.assertIn("readiness_checklist.md", names) + self.assertIn("threats_to_validity.md", names) + self.assertIn("artifact_bundle_manifest.json", names) + self.assertIn("release_notes.md", names) + self.assertIn("poster.md", names) + self.assertIn("slides.md", names) + self.assertIn("social.md", names) + self.assertIn("external_summary.md", names) + self.assertTrue(all(path.exists() for path in package.artifact_paths)) + + +if __name__ == "__main__": + unittest.main()