diff --git a/reme/steps/file_io/stat.py b/reme/steps/file_io/stat.py index f82203b5..5c87c794 100644 --- a/reme/steps/file_io/stat.py +++ b/reme/steps/file_io/stat.py @@ -1,11 +1,8 @@ """``file_stat`` — peek at file metadata under the workspace without copying it. -Cheap inspection alternative to ``file_download``. The answer carries the -path, type, size and mtime, and the answer is all an MCP caller receives: -MCP returns only ``response.answer``. ``mime`` and, for markdown files, -the parsed frontmatter stay in ``metadata`` with the rest of the envelope, -so they reach direct ``Response`` consumers but never an MCP caller — an -agent that needs the frontmatter must read the file, not stat it. +Cheap inspection alternative to ``file_download``. The answer carries path, +type, size and mtime; MCP returns only this answer. MIME type and Markdown +frontmatter remain in ``metadata`` for direct ``Response`` consumers. Returns a uniform envelope: diff --git a/tests/unit/test_crud_steps.py b/tests/unit/test_crud_steps.py index 5d3a3184..3ee620b5 100644 --- a/tests/unit/test_crud_steps.py +++ b/tests/unit/test_crud_steps.py @@ -113,7 +113,7 @@ def _seed_md(workspace_dir: Path, rel: str, body: str) -> Path: def test_stat_indexed_file(): - """stat returns size, mime, and frontmatter for an indexed .md file.""" + """stat reports size and mtime in its answer, plus metadata for an indexed file.""" async def run(): with tempfile.TemporaryDirectory() as tmp, temp_chdir(tmp): @@ -123,7 +123,10 @@ def test_stat_indexed_file(): payload = _metadata(step) assert payload["exists"] is True assert payload["type"] == "file" - assert "size" in payload and payload["size"] > 0 + assert payload["size"] == (Path(tmp) / "topics/n.md").stat().st_size + assert step.context.response.answer == ( + f"stat: topics/n.md (file, {payload['size']} bytes, mtime {payload['mtime']})" + ) assert payload["mime"].startswith("text/") assert payload["frontmatter"] == {"name": "T"} await store.close() @@ -144,41 +147,13 @@ def test_stat_directory_fallback(): payload = _metadata(step) assert payload["exists"] is True assert payload["type"] == "dir" + assert step.context.response.answer == f"stat: topics (dir, mtime {payload['mtime']})" await store.close() print("✓ test_stat_directory_fallback passed") asyncio.run(run()) -def test_stat_answer_reports_size_and_mtime(): - """The answer carries size and mtime, not just the path and type. - - The ``stat`` job description advertises "size, mtime, exists, is_dir, - is_file", but only ``answer`` reaches an MCP caller — mtime used to live - solely in metadata, so an agent could not tell how fresh a file was. - """ - - async def run(): - with tempfile.TemporaryDirectory() as tmp, temp_chdir(tmp): - store = await _make_store({"topics/n.md": "---\nname: T\n---\nbody"}) - step = crud_stat.StatStep(file_store=store) - await step(path="topics/n.md") - answer = step.context.response.answer - mtime = _metadata(step)["mtime"] - assert answer.startswith("stat: topics/n.md (file, ") - assert "bytes, mtime " in answer - assert answer.endswith(f"{mtime})") - - (Path(tmp) / "topics").mkdir(parents=True, exist_ok=True) - step = crud_stat.StatStep(file_store=store) - await step(path="topics") - answer = step.context.response.answer - assert answer == f"stat: topics (dir, mtime {_metadata(step)['mtime']})" - await store.close() - - asyncio.run(run()) - - def test_stat_directory_wins_over_same_name_index(): """stat prefers an existing directory over its same-name ``.md`` index.