fix(hooks): ingest only the active transcript
This commit is contained in:
parent
a337cafeaf
commit
d52d5120de
|
|
@ -1832,7 +1832,9 @@ def main():
|
|||
|
||||
# mine
|
||||
p_mine = sub.add_parser("mine", help="Mine files into the palace")
|
||||
p_mine.add_argument("dir", help="Directory to mine")
|
||||
p_mine.add_argument(
|
||||
"dir", help="Directory to mine, or one conversation file with --mode convos"
|
||||
)
|
||||
p_mine.add_argument(
|
||||
"--backend",
|
||||
default=None,
|
||||
|
|
|
|||
|
|
@ -418,9 +418,16 @@ def scan_convos(convo_dir: str) -> list:
|
|||
``sys.stderr`` with a `` SKIP: <relative-path> (symlink)`` line so the
|
||||
caller can tell why an apparent conversation directory yielded no files.
|
||||
"""
|
||||
convo_path = Path(convo_dir).expanduser().resolve()
|
||||
# A direct conversation file is a valid source. For a file, feed only
|
||||
# its basename through the existing directory validation loop.
|
||||
requested_path = Path(convo_dir).expanduser()
|
||||
single_file = requested_path.is_file()
|
||||
convo_path = (requested_path.parent if single_file else requested_path).resolve()
|
||||
scan_entries = (
|
||||
[(str(convo_path), [], [requested_path.name])] if single_file else os.walk(convo_path)
|
||||
)
|
||||
files = []
|
||||
for root, dirs, filenames in os.walk(convo_path):
|
||||
for root, dirs, filenames in scan_entries:
|
||||
dirs[:] = [d for d in dirs if d not in CONVO_SKIP_DIRS]
|
||||
for filename in filenames:
|
||||
if filename.endswith(".meta.json"):
|
||||
|
|
|
|||
|
|
@ -1090,12 +1090,12 @@ def _ingest_transcript(transcript_path: str):
|
|||
_submit_daemon_job(
|
||||
"mine",
|
||||
{
|
||||
"source": str(path.parent),
|
||||
"source": str(path),
|
||||
"mode": "convos",
|
||||
"wing": "sessions",
|
||||
"agent": "mempalace",
|
||||
},
|
||||
dedupe_key=_daemon_mine_dedupe_key(str(path.parent), "convos"),
|
||||
dedupe_key=_daemon_mine_dedupe_key(str(path), "convos"),
|
||||
wait=False,
|
||||
)
|
||||
_log(f"Transcript ingest submitted to daemon: {path.name}")
|
||||
|
|
@ -1113,7 +1113,7 @@ def _ingest_transcript(transcript_path: str):
|
|||
"-m",
|
||||
"mempalace",
|
||||
"mine",
|
||||
str(path.parent),
|
||||
str(path),
|
||||
"--mode",
|
||||
"convos",
|
||||
"--wing",
|
||||
|
|
|
|||
|
|
@ -834,3 +834,41 @@ def test_register_file_sentinel_includes_source_mtime():
|
|||
assert abs(mined[str(tiny_file)] - os.path.getmtime(tiny_file)) < 0.001
|
||||
finally:
|
||||
shutil.rmtree(tmpdir, ignore_errors=True)
|
||||
|
||||
|
||||
def test_mine_convos_dry_run_single_file_does_not_scan_siblings(
|
||||
tmp_path,
|
||||
capsys,
|
||||
):
|
||||
selected = tmp_path / "selected.txt"
|
||||
sibling = tmp_path / "sibling.txt"
|
||||
|
||||
selected.write_text(
|
||||
"> Which transcript should be mined?\n"
|
||||
"SELECTED_ONLY_MARKER belongs to the active transcript.\n\n"
|
||||
"> Should sibling files be included?\n"
|
||||
"No. Only the selected transcript should be scanned.\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
sibling.write_text(
|
||||
"> Should this sibling be mined?\n"
|
||||
"SIBLING_SHOULD_NOT_BE_MINED by the single-file invocation.\n\n"
|
||||
"> Is that important?\n"
|
||||
"Yes. It keeps hook-triggered mining narrowly scoped.\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
palace_path = tmp_path / "palace"
|
||||
|
||||
mine_convos(
|
||||
str(selected),
|
||||
str(palace_path),
|
||||
wing="sessions",
|
||||
dry_run=True,
|
||||
)
|
||||
output = capsys.readouterr().out
|
||||
|
||||
assert "Files: 1" in output
|
||||
assert "[DRY RUN] selected.txt" in output
|
||||
assert "sibling.txt" not in output
|
||||
assert not palace_path.exists()
|
||||
|
|
|
|||
|
|
@ -713,3 +713,21 @@ class TestExtractAuthoredAt:
|
|||
f = tmp_path / "session.jsonl"
|
||||
f.write_text('{"timestamp": 1}\n{"timestamp": false}\n')
|
||||
assert _extract_authored_at(f) is None
|
||||
|
||||
|
||||
def test_scan_convos_accepts_one_file_without_scanning_siblings(
|
||||
tmp_path,
|
||||
):
|
||||
selected = tmp_path / "selected.jsonl"
|
||||
sibling = tmp_path / "sibling.jsonl"
|
||||
|
||||
selected.write_text(
|
||||
'{"type": "user"}\n',
|
||||
encoding="utf-8",
|
||||
)
|
||||
sibling.write_text(
|
||||
'{"type": "user"}\n',
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
assert scan_convos(str(selected)) == [selected.resolve()]
|
||||
|
|
|
|||
|
|
@ -1378,7 +1378,7 @@ def test_ingest_transcript_daemon_opt_in_submits_job(tmp_path):
|
|||
mock_popen.assert_not_called()
|
||||
mock_submit.assert_called_once()
|
||||
payload = mock_submit.call_args.args[1]
|
||||
assert payload["source"] == str(tmp_path)
|
||||
assert payload["source"] == str(transcript.resolve())
|
||||
assert payload["mode"] == "convos"
|
||||
assert payload["wing"] == "sessions"
|
||||
|
||||
|
|
@ -1398,7 +1398,7 @@ def test_ingest_transcript_skips_when_target_running(tmp_path):
|
|||
"-m",
|
||||
"mempalace",
|
||||
"mine",
|
||||
str(transcript.parent),
|
||||
str(transcript.resolve()),
|
||||
"--mode",
|
||||
"convos",
|
||||
"--wing",
|
||||
|
|
@ -1763,7 +1763,7 @@ def test_precompact_with_timeout(tmp_path):
|
|||
assert result == {}
|
||||
|
||||
|
||||
def test_precompact_mines_transcript_dir(tmp_path, monkeypatch):
|
||||
def test_precompact_mines_only_active_transcript(tmp_path, monkeypatch):
|
||||
"""Precompact ingests the active transcript via _ingest_transcript.
|
||||
|
||||
With no MEMPAL_DIR, _mine_sync is a no-op; the transcript ingest is
|
||||
|
|
@ -1787,8 +1787,8 @@ def test_precompact_mines_transcript_dir(tmp_path, monkeypatch):
|
|||
mock_run.assert_not_called()
|
||||
mock_popen.assert_called_once()
|
||||
cmd = mock_popen.call_args[0][0]
|
||||
# Mines the transcript's parent dir as convos, into wing "sessions".
|
||||
assert str(tmp_path) in cmd
|
||||
# Mines only the active transcript as convos, into wing "sessions".
|
||||
assert str(transcript.resolve()) in cmd
|
||||
assert cmd[cmd.index("--mode") + 1] == "convos"
|
||||
assert cmd[cmd.index("--wing") + 1] == "sessions"
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue