diff --git a/paperforge/worker/sync.py b/paperforge/worker/sync.py index 46cca85d..6a9086dc 100644 --- a/paperforge/worker/sync.py +++ b/paperforge/worker/sync.py @@ -1593,8 +1593,7 @@ def migrate_to_workspace(vault: Path, paths: dict) -> int: Copies each flat note at // - .md into: <literature_dir>/<domain>/<key> - <Title>/<key> - <Title>.md - Extracts ## 🔍 精读 into: - <literature_dir>/<domain>/<key> - <Title>/deep-reading.md + Preserves any existing ## 🔍 精读 section inside the main note (single source of truth). Creates: <literature_dir>/<domain>/<key> - <Title>/ai/ (empty directory) diff --git a/tests/test_asset_index_integration.py b/tests/test_asset_index_integration.py index c74801cb..37a665d9 100644 --- a/tests/test_asset_index_integration.py +++ b/tests/test_asset_index_integration.py @@ -385,13 +385,12 @@ class TestWorkspacePaths: "---\ntitle: Paper A\nzotero_key: AAA\n---\n\n## 🔍 精读\n\nDone.\n", encoding="utf-8", ) - (workspace_dir / "deep-reading.md").write_text("## 🔍 精读\n\nDone.\n", encoding="utf-8") - _write_index(vault, [{"zotero_key": "AAA", "title": "Paper A"}]) refresh_index_entry(vault, "AAA") entry = read_index(vault)["items"][0] - assert entry["deep_reading_path"].endswith("deep-reading.md") + assert entry["deep_reading_path"] == "" + assert entry["main_note_path"].endswith("AAA - Paper A.md") assert entry["fulltext_path"] == "" assert entry["lifecycle"] == "pdf_ready" assert entry["ocr_status"] == "done_incomplete" diff --git a/tests/test_asset_state.py b/tests/test_asset_state.py index 6992eee1..b3e50bb4 100644 --- a/tests/test_asset_state.py +++ b/tests/test_asset_state.py @@ -68,19 +68,18 @@ class TestComputeLifecycle: result = compute_lifecycle(entry) assert result == "deep_read_done" - def test_ai_context_ready(self) -> None: - """All conditions met: OCR done, deep_read done, all workspace paths non-empty.""" + def test_deep_read_done_when_workspace_paths_present(self) -> None: + """Deep-reading lives in the main note, so lifecycle still tops out at deep_read_done.""" entry = { "has_pdf": True, "ocr_status": "done", "deep_reading_status": "done", "ai_path": "Literature/骨科/KEY/ai/", "fulltext_path": "Literature/骨科/KEY/fulltext.md", - "deep_reading_path": "Literature/骨科/KEY/deep-reading.md", "main_note_path": "Literature/骨科/KEY/KEY - Title.md", } result = compute_lifecycle(entry) - assert result == "ai_context_ready" + assert result == "deep_read_done" def test_missing_one_workspace_path_returns_deep_read_done(self) -> None: """OCR done AND deep_read done but fulltext_path is missing → deep_read_done.""" @@ -90,7 +89,6 @@ class TestComputeLifecycle: "deep_reading_status": "done", "ai_path": "Literature/骨科/KEY/ai/", "fulltext_path": "", - "deep_reading_path": "Literature/骨科/KEY/deep-reading.md", "main_note_path": "Literature/骨科/KEY/KEY - Title.md", } result = compute_lifecycle(entry) diff --git a/tests/test_command_docs.py b/tests/test_command_docs.py index 0d8d8320..3626c9bf 100644 --- a/tests/test_command_docs.py +++ b/tests/test_command_docs.py @@ -75,8 +75,6 @@ def command_docs() -> dict[str, Path]: def user_facing_docs() -> dict[str, Path]: return { "README": REPO_ROOT / "README.md", - "INSTALLATION": REPO_ROOT / "docs" / "INSTALLATION.md", - "setup-guide": REPO_ROOT / "docs" / "setup-guide.md", "AGENTS": REPO_ROOT / "AGENTS.md", } @@ -184,35 +182,14 @@ class TestUnresolvedTokensAbsentFromUserRunExamples: match is None ), f"[README] Found legacy unresolved token path in code blocks: {match.group(0)!r}\nCode lines:\n{code_lines}" - def test_installation_no_legacy_python_literature_pipeline_in_examples( - self, - user_facing_docs: dict[str, Path], - ) -> None: - """docs/INSTALLATION.md must not show legacy python <system_dir>/... in user-run examples.""" - content = read_fileutf8(user_facing_docs["INSTALLATION"]) - code_lines = code_block_lines(content) - combined = "\n".join(code_lines) - match = self.LEGACY_PYTHON_LITERATURE_PIPELINE.search(combined) - assert match is None, ( - f"[INSTALLATION] Found legacy unresolved token path in code blocks: {match.group(0)!r}\n" - f"Code lines:\n{code_lines}" - ) - - -# --------------------------------------------------------------------------- -# Task 1 tests — paperforge paths/status examples in user-facing docs -# --------------------------------------------------------------------------- - - class TestPaperforgeCommandExamplesInUserDocs: - """README, INSTALLATION, setup-guide must show stable paperforge command examples.""" + """README and AGENTS must show stable paperforge command examples.""" @pytest.mark.parametrize( "doc_key,pattern", [ ("README", r"paperforge (status|paths)"), - ("INSTALLATION", r"paperforge (status|paths)"), - ("setup-guide", r"paperforge (status|paths)"), + ("AGENTS", r"paperforge (status|paths)"), ], ) def test_user_doc_contains_paperforge_stable_example( diff --git a/tests/test_migration.py b/tests/test_migration.py index d42acd85..086380ae 100644 --- a/tests/test_migration.py +++ b/tests/test_migration.py @@ -182,22 +182,13 @@ class TestMigrateToWorkspace: paths = pipeline_paths(vault) workspace_dir = paths["literature"] / "骨科" / "KEY002 - Paper With Deep Reading" - # Check deep-reading.md exists - dr_path = workspace_dir / "deep-reading.md" - assert dr_path.exists(), "deep-reading.md should exist in workspace" - - # deep-reading.md content should start with ## 🔍 精读 - dr_content = dr_path.read_text(encoding="utf-8") - assert dr_content.startswith("## \U0001f50d \u7cbe\u8bfb"), ( - "deep-reading.md should start with ## 🔍 精读 header" - ) - assert "Pass 1: Overview" in dr_content, "deep-reading content preserved" - assert "Pass 2: Details" in dr_content, "deep-reading content preserved" - - # Main note also contains the deep-reading section (complete copy) + # Main note preserves the deep-reading section (single source of truth) main_note = workspace_dir / "KEY002 - Paper With Deep Reading.md" main_content = main_note.read_text(encoding="utf-8") assert "## \U0001f50d \u7cbe\u8bfb" in main_content, "Main note preserves deep-reading section" + assert "Pass 1: Overview" in main_content, "deep-reading content preserved" + assert "Pass 2: Details" in main_content, "deep-reading content preserved" + assert not (workspace_dir / "deep-reading.md").exists(), "No separate deep-reading.md should be created" def test_migrate_creates_ai_dir(self, tmp_path: Path) -> None: """ai/ directory is created inside workspace.""" @@ -298,7 +289,9 @@ class TestMigrateToWorkspace: workspace_dir = paths["literature"] / "骨科" / "LEG001 - Legacy Flat Note" assert workspace_dir.is_dir() assert (workspace_dir / "LEG001 - Legacy Flat Note.md").exists() - assert (workspace_dir / "deep-reading.md").exists() + assert not (workspace_dir / "deep-reading.md").exists() + main_content = (workspace_dir / "LEG001 - Legacy Flat Note.md").read_text(encoding="utf-8") + assert "## 🔍 精读" in main_content def test_migrate_promotes_legacy_library_record_flags(self, tmp_path: Path) -> None: """Legacy library-record control flags are copied into the workspace note.""" diff --git a/tests/test_plugin_install_bootstrap.py b/tests/test_plugin_install_bootstrap.py index b688014f..6232f860 100644 --- a/tests/test_plugin_install_bootstrap.py +++ b/tests/test_plugin_install_bootstrap.py @@ -55,3 +55,10 @@ def test_setup_wizard_uses_manual_zotero_path_for_checks() -> None: assert "manual_zotero_path = Path(zotero_data).expanduser() if zotero_data else None" in source assert "checker.check_zotero(manual_zotero_path)" in source assert "checker.check_bbt(manual_zotero_path)" in source + + +def test_plugin_deep_reading_flow_does_not_depend_on_separate_file() -> None: + """Deep-reading now lives in the main note, not a dedicated deep-reading.md file.""" + source = PLUGIN_MAIN.read_text(encoding="utf-8") + assert "entry.deep_reading_path" not in source + assert "file.name === 'deep-reading.md'" not in source