Skip to content

Pibmo Recall has no single binary. It is a set of Python entry points run straight from the repo checkout — nothing is installed into ~/.claude or ~/.pibmo, and updating is git pull (the scheduler unit points at the checkout, so the next hourly tick runs the new code). This page documents every command and flag, its default, whether it is required, and shows real output for each. The examples assume the checkout at ~/code/pibmo-recall and shorten home paths to /Users/alice; the outputs themselves are reproduced from real runs against a scratch dataset root.

For the concepts these commands operate on — dataset roots, machines, faithful mirrors, appends and forks, process-time projects — see Concepts.

Synopsis

bash install.sh [--force]
python3 scripts/sync/sync-transcripts.py --root ROOT [options]
python3 scripts/sync/doctor.py --root ROOT [options]
python3 scripts/sync/scheduler.py (--print | --write | --check) --root ROOT --machine NAME [options]
python3 scripts/sync/rootcheck.py (--print-default | --check-local-root PATH | --describe PATH)
/usr/bin/python3 scripts/process/process_root.py --root ROOT [options]
python3 scripts/process/propose_consolidation.py --root ROOT [--apply]
python3 scripts/sync/import_legacy.py --root ROOT --legacy PATH [options]
python3 scripts/probe/session_names.py [--root DIR] [--check | --json]

Every Python command answers -h/--help. There is no --version flag; the version of a command is the git commit of the checkout it runs from.

Interpreters. The sync layer (sync-transcripts.py, doctor.py, scheduler.py, rootcheck.py, import_legacy.py) is pure Python stdlib — any python3 >= 3.9 works. The processing layer (process_root.py) needs PyYAML; on macOS run it with /usr/bin/python3, which ships it.

The dataset root

Commands that touch the archive resolve the root in this order, first hit wins: the --root flag, then the PIBMO_ROOT environment variable. There is no config file to consult — the root is the only machine-local configuration and it travels on the scheduler unit / cron line. If neither resolves, the command fails with an error naming both options and exits 1.

The platform-default local root contains a space (~/Library/Application Support/pibmo/recall on macOS), so the examples quote it:

$ ROOT="$HOME/Library/Application Support/pibmo/recall"

The sync additionally requires the machine to be registered: it refuses to run against a root whose machines.json does not map this machine’s hardware UUID to a friendly name. Registration is install.sh’s job — there is no auto-created machine folder and no silent fallback.

Exit codes

CodeMeaning
0Success. Also returned by no-op cases: a sync skipped by the single-writer lock, scheduler.py --check with current units, doctor.py with warnings but no failure, and rootcheck.py --describe in every case.
1Operational error: no resolvable root, an unregistered machine, a missing legacy archive, a corrupt projects.json. The message is printed to stderr, prefixed Error:. Also: doctor.py with any FAIL row, scheduler.py --check with a stale unit, rootcheck.py --check-local-root refusing a path, and session_names.py --check detecting drift.
2Usage error from argument parsing: an unknown flag or a missing required flag. Also install.sh with an unknown argument, and session_names.py when its --root does not exist.

Write conventions

  • Append-only is enforced at write time. A transcript is never overwritten, shrunk, or deleted. The engine classifies every changed file and the verbose output uses that vocabulary: JSONL files are new, extend, identical, fork (a divergent rewrite, kept as a sibling {uuid}.fork-{sha8}.jsonl), or skip-regression (a shrunken source, not copied); binary files and SQLite databases are new or new-version (content-addressed versions, logical-hash-gated for SQLite).
  • One writer per root. The sync and the legacy importer take the same lock. A second concurrent sync prints [2026-07-22 15:00] sync skipped (already running) and exits 0; an unwritable lock directory is a loud error, never a silent skip.
  • Credentials never enter the archive. The sync excludes ~/.gemini/oauth_creds.json, google_accounts.json and Antigravity’s OAuth token, ~/.codex/auth.json, and caches/binaries/ephemera (extensions/, browser_recordings/, bin/, .tmp/, tmp/ for Codex, cache/) at write time.
  • Each machine writes only its own subtree. Raw data lands under {root}/{machine}/...; the top-level shared config (machines.json, projects.json, profiles.json) is read but never rerouted, and it never crosses during consolidation — the destination’s copies are authoritative.
  • Every run leaves provenance. Sync runs, per-file observations, and JSONL schema evolution are recorded in {root}/{machine}/pibmo.db. The doctor’s heartbeat check reads exactly this.

install.sh

Per-machine setup: propose a local dataset root, register the machine, seed the shared config, and write the native scheduler unit. Interactive by default; every prompt can be pre-filled for headless installs. It never edits your crontab and writes nothing outside the dataset root and the unit file.

bash install.sh [--force]
FlagTypeDefaultRequiredMeaning
--forceflagoffnoAccept a cloud-synced or remote local root that rootcheck.py would refuse, eyes open.

Any other argument prints Usage: bash install.sh [--force] and exits 2.

Prompts, in order, with their environment pre-fills:

PromptPre-fillDefault
Local dataset rootPIBMO_ROOTthe platform default from rootcheck.py --print-default
Friendly machine namePIBMO_MACHINEhostname -s, lowercased
Multi-machine setup?implied y when either pre-fill below is setn
Consolidated root (optional)PIBMO_CONSOLIDATEblank (skip)
Secondary mirror (optional)PIBMO_SECONDARYblank (skip)

On a single computer the install is the first two prompts and Enter — consolidation never comes up. A cloud-synced local root is refused (see rootcheck.py); a cloud-synced consolidated root is permitted eyes-open with a note naming the vendor client as the replication tool.

What it writes: this machine’s entry in {root}/machines.json (idempotent — re-running with a new name updates the entry), the {root}/{machine}/logs/ skeleton, projects.json and profiles.json seeded from the repo’s *.example.json templates (an existing file is never overwritten), and the scheduler unit via scheduler.py --write, whose activation commands it prints. Registration output, reproduced:

machines.json: registered BE79A1C2... as 'mymac'
projects.json: seeded from projects.example.json
profiles.json: seeded from profiles.example.json

It ends by printing the manual first-sync command, the doctor command, and the crontab fallback line for those who prefer plain cron (never schedule both):

0 * * * * /usr/bin/python3 "/Users/alice/code/pibmo-recall/scripts/sync/sync-transcripts.py" --root "/Users/alice/Library/Application Support/pibmo/recall" >> "/Users/alice/Library/Application Support/pibmo/recall/mymac/logs/cron.log" 2>&1

Archiving scope is deliberately narrow: the installing user’s ~/.claude (plus ~/.gemini, ~/.codex) only. A machine-wide multi-user sweep is a separate, explicit opt-in — it is never switched on silently.

sync-transcripts.py

The scheduled entry point: archive this machine’s Claude Code, Gemini (CLI + Antigravity), and Codex session data into the dataset root, faithfully and append-only. Safe to run by hand at any time; concurrent runs exit immediately via the lock.

python3 scripts/sync/sync-transcripts.py --root ROOT [--secondary DIR]
[--consolidate DIR] [-v] [--dry-run]
FlagTypeDefaultRequiredMeaning
--rootpath$PIBMO_ROOTyes*Dataset root.
--secondarypath$PIBMO_SECONDARYnoAccumulate-only DR mirror of the whole root (a local second disk).
--consolidatepath$PIBMO_CONSOLIDATEnoConsolidated root to push this machine’s subtree into, accumulate-only transport.
-v, --verboseflagoffnoPrint every non-identical action.
--dry-runflagoffnoClassify and report without writing anything.

* Required as either the flag or the environment variable.

A run executes stages in order: mirror ~/.claude/projects/, ~/.gemini/, and ~/.codex/ through the preservation engine (with the credential and ephemera exclusions above); auto-append newly seen encoded paths to {root}/projects.json (rows default to process: false — for the converter’s benefit, never used for routing); write {root}/{machine}/machine.json; record the schema manifest and run heartbeat in pibmo.db; then, last, the optional --secondary and --consolidate mirrors — so a failed transport (cloud offline) can lag replication but never cost an archive write.

Previewing, then a first real run:

$ python3 ~/code/pibmo-recall/scripts/sync/sync-transcripts.py --root "$ROOT" --dry-run
=== Pibmo Recall sync [dry run] — 2026-07-22 15:00 ===
machine: mymac
root: /Users/alice/Library/Application Support/pibmo/recall
[dry] would add new project to projects.json: -Users-alice-code-myapp
Done. 3 file action(s): {"claude": {"new": 1}, "gemini": {"new-version": 1}, "codex": {"new": 1}}
$ python3 ~/code/pibmo-recall/scripts/sync/sync-transcripts.py --root "$ROOT" -v
=== Pibmo Recall sync — 2026-07-22 15:00 ===
machine: mymac
root: /Users/alice/Library/Application Support/pibmo/recall
Claude:
new -Users-alice-code-myapp/3f2c9d41-6a77-4e02-9b1c-8d5e0a2f4c66.jsonl
Gemini:
new-version tmp/myapp/logs.json
Codex:
new sessions/rollout-demo.jsonl
adding new project to projects.json: -Users-alice-code-myapp
Done. 3 file action(s): {"claude": {"new": 1}, "gemini": {"new-version": 1}, "codex": {"new": 1}}

A scheduled tick without -v logs two lines; a session that only grew is an extend:

[2026-07-22 16:00] sync started
[2026-07-22 16:00] sync complete

A divergent rewrite of a transcript — compaction is the common cause — is never merged and never lost: the previous lineage survives as a sibling fork file. Reproduced:

$ python3 ~/code/pibmo-recall/scripts/sync/sync-transcripts.py --root "$ROOT" -v
=== Pibmo Recall sync — 2026-07-22 17:00 ===
machine: mymac
root: /Users/alice/Library/Application Support/pibmo/recall
Claude:
fork -Users-alice-code-myapp/3f2c9d41-6a77-4e02-9b1c-8d5e0a2f4c66.jsonl
Gemini:
Codex:
schema change detected in claude
Done. 1 file action(s): {"claude": {"fork": 1}, "gemini": {}, "codex": {}}
$ ls "$ROOT/mymac/claude/projects/-Users-alice-code-myapp/"
3f2c9d41-6a77-4e02-9b1c-8d5e0a2f4c66.fork-cea3cb99.jsonl
3f2c9d41-6a77-4e02-9b1c-8d5e0a2f4c66.jsonl

(The schema change detected line is stage 6 noticing a JSONL event type it had not recorded before — the manifest in pibmo.db tracks the tools’ schema evolution as a side effect of reading the lines anyway.)

Missing root and unregistered machine:

$ python3 ~/code/pibmo-recall/scripts/sync/sync-transcripts.py
Error: no dataset root. Pass --root /path/to/dataset (or set PIBMO_ROOT). The installer prints the exact cron line.
$ python3 ~/code/pibmo-recall/scripts/sync/sync-transcripts.py --root /tmp/not-a-root
Error: /tmp/not-a-root/machines.json not found.
Run install.sh first to register this machine and seed the shared config.

Both exit 1.

doctor.py

Is this machine actually archiving? Checks registration, root reachability, a write canary, the scheduler unit (existence, byte-match against a fresh render, and whether its embedded interpreter and script still exist — the moved-repo class), the sync heartbeat in pibmo.db, log recency, and — when a consolidated root is named — per-machine replication lag there. Every failure prints its exact fix. Read-only except one write-and-delete canary under {root}/{machine}/logs/; the raw archive is never touched.

python3 scripts/sync/doctor.py --root ROOT [--machine NAME] [--python PY]
[--repo PATH] [--secondary DIR]
[--consolidate DIR] [--max-age-hours N]
[--platform P]
FlagTypeDefaultRequiredMeaning
--rootpath$PIBMO_ROOTyes*Local dataset root.
--machinestringresolved via hardware UUIDnoFriendly machine name.
--pythonpath/usr/bin/python3noInterpreter expected in the unit.
--repopathderived from the script’s locationnoRepo checkout expected in the unit.
--secondarypath""no--secondary value expected in the unit.
--consolidatepath""noConsolidated root to measure replication against (also expected in the unit).
--max-age-hoursfloat26noHeartbeat age that still counts as healthy (hourly schedule + a slept-through laptop day).
--platformenumautonoOne of auto, darwin, linux, wsl.

* Required as either the flag or the environment variable.

The unit check renders a fresh unit from the flags above and compares byte-for-byte, so pass the same --python/--repo/--secondary/ --consolidate values the installer used — a mismatch is reported as a stale unit, which is exactly what it means. Exit 0 when everything passes (warnings allowed), 1 on any FAIL.

A healthy machine shortly after install (the log warning clears after the first scheduled tick):

$ python3 ~/code/pibmo-recall/scripts/sync/doctor.py --root "$ROOT" --machine mymac
Pibmo Recall doctor -- machine 'mymac', root /Users/alice/Library/Application Support/pibmo/recall
OK root /Users/alice/Library/Application Support/pibmo/recall/machines.json readable (1 machine(s) registered)
OK identity BE79A1C2...
OK registered 'mymac'
OK writable canary ok under mymac/logs/
OK unit 1 unit file(s) current
OK unit-cmd embedded interpreter and script exist
OK heartbeat last completed sync 2026-07-22T14:00:03.512208+00:00 (0.4h ago)
WARN log no /Users/alice/Library/Application Support/pibmo/recall/mymac/logs/cron.log yet
fix: expected after the first scheduled run; manual -v runs print to the terminal instead
Summary: 7 ok, 1 warn, 0 fail

A failure carries its remediation inline — here the unit file is missing:

FAIL unit not installed: /Users/alice/Library/LaunchAgents/com.pibmo.recall.sync.plist
fix: re-run install.sh (or scheduler.py --write), then:
Activate (idempotent; bootout first when re-writing an active unit):
launchctl bootout gui/$UID com.pibmo.recall.sync 2>/dev/null || true
launchctl bootstrap gui/$UID "/Users/alice/Library/LaunchAgents/com.pibmo.recall.sync.plist"
If a sync destination crosses a vendor cloud mount (e.g. a
consolidated root under ~/Library/CloudStorage), the first launchd
run may raise ONE macOS consent prompt for python3 -- grant it once.
(using the crontab fallback instead? verify: crontab -l | grep sync-transcripts)
...
Summary: 5 ok, 1 warn, 1 fail

Exit 1. Two failure signatures get dedicated diagnoses: a fresh log next to a stale heartbeat means the scheduler fires but every run dies early (the fix says to read the log), and with --consolidate the doctor also lists every peer machine’s replicated heartbeat at the destination, flagging any that has gone dark past 48 hours. On WSL the unit check degrades to a warning — Windows Task Scheduler is not verifiable from inside the distro, so the heartbeat is the ground truth.

scheduler.py

Generate, install, and verify the native scheduler unit for the hourly sync: a launchd LaunchAgent on macOS, a systemd user service + timer on Linux. Units are deterministic — same inputs, byte-identical files — so --check can verify the installed unit against a fresh render, exactly like the doctor does.

python3 scripts/sync/scheduler.py (--print | --write | --check)
--root ROOT --machine NAME [--python PY]
[--repo PATH] [--secondary DIR]
[--consolidate DIR] [--platform P]
FlagTypeDefaultRequiredMeaning
--print / --write / --checkflagyes (exactly one)Render to stdout / write and print activation commands / exit 1 unless installed units byte-match a fresh render.
--rootpathyesDataset root to embed in the unit.
--machinestringyesThis machine’s friendly name (log path).
--pythonpath/usr/bin/python3noInterpreter to embed.
--repopathderived from the script’s locationnoRepo checkout to embed.
--secondarypath""noOptional --secondary destination to embed.
--consolidatepath""noOptional consolidated root to embed.
--platformenumautonoOne of auto, darwin, linux, wsl.

The macOS unit, rendered. StartCalendarInterval fires at minute 0 and launchd coalesces ticks missed while the Mac slept into one run on wake; RunAtLoad adds a catch-up run at login (extra runs are no-ops):

$ python3 ~/code/pibmo-recall/scripts/sync/scheduler.py --print --root "$ROOT" --machine mymac
# ==> /Users/alice/Library/LaunchAgents/com.pibmo.recall.sync.plist
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.pibmo.recall.sync</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/python3</string>
<string>/Users/alice/code/pibmo-recall/scripts/sync/sync-transcripts.py</string>
<string>--root</string>
<string>/Users/alice/Library/Application Support/pibmo/recall</string>
</array>
<key>StartCalendarInterval</key>
<dict>
<key>Minute</key>
<integer>0</integer>
</dict>
<key>RunAtLoad</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/alice/Library/Application Support/pibmo/recall/mymac/logs/cron.log</string>
<key>StandardErrorPath</key>
<string>/Users/alice/Library/Application Support/pibmo/recall/mymac/logs/cron.log</string>
</dict>
</plist>

On Linux the same command renders a oneshot service plus an hourly timer (Persistent=true catches up missed ticks; RandomizedDelaySec=120 spreads fleet load) into ~/.config/systemd/user/pibmo-recall-sync.{service,timer}.

--write writes the file(s) and prints the activation commands; --check verifies:

$ python3 ~/code/pibmo-recall/scripts/sync/scheduler.py --check --root "$ROOT" --machine mymac
OK: 1 unit file(s) current
$ python3 ~/code/pibmo-recall/scripts/sync/scheduler.py --check --root "$ROOT" --machine mymac \
--python /opt/homebrew/bin/python3
STALE: /Users/alice/Library/LaunchAgents/com.pibmo.recall.sync.plist: differs from a fresh render

The first exits 0, the second 1. On WSL no file is written in any mode — the Windows Task Scheduler cannot be configured from inside the distro, so the command to schedule on the Windows side is printed instead (exit 0).

rootcheck.py

Dataset-root placement rules for the installer. Preservation is local-first: the machine-local root must never sit on a cloud-synced or remote path, so this tool classifies paths and the installer refuses accordingly. Detection is heuristic by design (custom cloud-folder locations are not all detectable), which is why the refusal pairs with install.sh --force rather than pretending certainty. Symlinks are resolved before classification.

python3 scripts/sync/rootcheck.py (--print-default | --check-local-root PATH | --describe PATH)
[--platform P]
ModeOutputExit
--print-defaultThe platform-default local root.0
--check-local-root PATHNothing when PATH looks local; the refusal with reasons and risks when it looks cloud-synced or remote.0 local, 1 refused
--describe PATHOne line: local, or cloud-or-remote: <reasons>. Used to name a vendor-mount consolidation target, permitted eyes-open.always 0
$ python3 ~/code/pibmo-recall/scripts/sync/rootcheck.py --print-default
/Users/alice/Library/Application Support/pibmo/recall
$ python3 ~/code/pibmo-recall/scripts/sync/rootcheck.py --describe '~/Dropbox/Datasets/pibmo'
cloud-or-remote: path component 'Dropbox' looks cloud-synced (dropbox)
$ python3 ~/code/pibmo-recall/scripts/sync/rootcheck.py --check-local-root '~/Dropbox/Datasets/pibmo'
REFUSED as the local dataset root: ~/Dropbox/Datasets/pibmo
- path component 'Dropbox' looks cloud-synced (dropbox)
Risks of a cloud-synced local root (the 2026-06/07 fleet outage class):
- schedulers can be permission-fenced off the path (macOS TCC denies
launchd and cron access to ~/Library/CloudStorage) -> total, silent outage
- the cloud client can evict files to dataless placeholder stubs the
sync cannot read
- client rewrites and conflict copies violate append-only preservation
- when the client is not running, nothing is preserved at all
The local root must be a plain local path -- a hard rule of the
preservation design. A cloud-synced folder is welcome as the CONSOLIDATED
root instead -- that is replication's job, not preservation's.
Detection is heuristic: re-run with --force to override eyes-open.

The last exits 1. On Linux/WSL it additionally checks /proc/mounts for network filesystems (NFS, CIFS, sshfs, rclone, and friends) and, on WSL, refuses /mnt/* — a Windows drive crosses drvfs/9P and may be cloud-synced on the Windows side where WSL cannot tell.

process_root.py

The deterministic processing layer: project the faithful archive into merged per-project Markdown under {root}/processed/{key}/{profile}/. Groups encoded paths into logical projects via {root}/projects.json at process time (consolidate_into chains resolved transitively — consolidation happens here, never in the sync), dedupes the same session synced from several machines by superset, and stamps every emitted file with source_machine / source_encoded_path frontmatter so provenance survives the merge. No LLM is involved anywhere.

/usr/bin/python3 scripts/process/process_root.py --root ROOT [--key KEY ...]
[--projects FILE] [--profiles-config FILE]
[--respect-process-flag] [-v]
FlagTypeDefaultRequiredMeaning
--rootpath$PIBMO_ROOTyes*Dataset root.
--keystringallnoOnly process this logical project; repeatable. Matches the post-consolidation key or the raw encoded folder name.
--projectspath{root}/projects.jsonnoOverride the projects map. A missing file falls back to raw encoded names; a corrupt file is an error, not a silent fallback.
--profiles-configpath{root}/profiles.jsonnoOverride the profiles config, merged over the built-in faithful and dialogue defaults (denylist semantics).
--respect-process-flagflagoffnoSkip rows marked process: false (legacy semantic; the default processes everything, since auto-appended rows all carry process: false).
-v, --verboseflagoffnoPrint the resolved machines, keys, and profiles before converting.

* Required as either the flag or the environment variable.

$ /usr/bin/python3 ~/code/pibmo-recall/scripts/process/process_root.py --root "$ROOT" -v
root: /Users/alice/Library/Application Support/pibmo/recall
machines: mymac
projects: 1 logical key(s) from 1 raw folder(s)
profiles: faithful, dialogue
keys=1 profiles=2 wrote=6 unchanged=0 superset_dupes=0
$ find "$ROOT/processed" -type f | sed "s|$ROOT/||"
processed/-Users-alice-code-myapp/faithful/2026-07-21_3f2c9d41.fork-cea3cb99.md
processed/-Users-alice-code-myapp/faithful/2026-07-21_3f2c9d41.md
processed/-Users-alice-code-myapp/faithful/index.yaml
processed/-Users-alice-code-myapp/dialogue/2026-07-21_3f2c9d41.fork-cea3cb99.md
processed/-Users-alice-code-myapp/dialogue/2026-07-21_3f2c9d41.md
processed/-Users-alice-code-myapp/dialogue/index.yaml

Re-running over an unchanged archive reports wrote=0 unchanged=6 — the converter is idempotent. Note the fork transcript is emitted alongside the main lineage, and each profile folder carries an index.yaml of its sessions.

propose_consolidation.py

Authoring helper for projects.json: propose consolidate_into groupings from git remotes, so ~100 auto-appended rows need no hand-editing. For each row it recovers the project’s true working directory from the archive itself (the first cwd field in any transcript — encoded names cannot be reversed reliably), reads that repo’s origin URL when it exists locally, and groups rows sharing a remote. It only proposes: nothing is written without --apply, an existing different consolidate_into is never overwritten (warned instead), and the converter never depends on this tool.

python3 scripts/process/propose_consolidation.py --root ROOT [--apply] [-v]
FlagTypeDefaultRequiredMeaning
--rootpath$PIBMO_ROOTyes*Dataset root.
--applyflagoffnoWrite the proposals into {root}/projects.json. Default is a dry run.
-v, --verboseflagoffnoExplain every skipped row.

* Required as either the flag or the environment variable.

$ python3 ~/code/pibmo-recall/scripts/process/propose_consolidation.py --root "$ROOT" -v
skip -Users-alice-code-myapp (/Users/alice/code/myapp is not a local git repo with an origin)
No new consolidation proposals.

When there are proposals, each prints as <key> -> consolidate_into: <target> (the canonical row of a group is the curated one when present, else the shortest key), the dry run ends with Dry run — re-run with --apply to write these into <path>, and --apply confirms with Applied N proposal(s) to <path>.

import_legacy.py

One-time import of a pre-reset keyed archive into the faithful single-root layout. Resolves each legacy project folder back to its encoded path (via the reverse map in projects.json and the cwd fields inside the transcripts), routes every file through the same preservation engine as the sync, and takes the same root lock — an import cannot race the hourly tick. Anything it cannot place is listed as UNRESOLVED rather than silently dropped.

python3 scripts/sync/import_legacy.py --root ROOT --legacy PATH
[--machine NAME ...] [-v] [--dry-run]
FlagTypeDefaultRequiredMeaning
--rootpathyesNew dataset root (must already be installed).
--legacypathyesThe renamed pre-reset archive. Must be disjoint from --root.
--machinestringallnoOnly import this legacy machine folder; repeatable.
-v, --verboseflagoffnoPrint every file action.
--dry-runflagoffnoClassify and report without writing.
$ python3 ~/code/pibmo-recall/scripts/sync/import_legacy.py --root "$ROOT" \
--legacy ~/Datasets/pibmo-legacy --dry-run -v
Importing legacy machine 'oldmac'...
new legacy:oldmac/-Users-alice-code-myapp/7be14a90-2c33-47d1-b5aa-90e2f1c7d301.jsonl → -Users-alice-code-myapp/7be14a90-2c33-47d1-b5aa-90e2f1c7d301.jsonl
{"new": 1}
=== Import summary ===
{
"oldmac": {
"new": 1
}
}

Importing a file that already exists in the root classifies exactly like the sync would (identical, extend, fork) — the append-only rules hold for imports too.

session_names.py

Read-only probe over a Claude Code data directory: extract every session’s display name (custom /rename titles and auto titles), and audit the name-bearing JSONL structures against the documented schema baseline so format drift is caught before it bites the converter. It writes nothing, anywhere.

python3 scripts/probe/session_names.py [--root DIR] [--project NAME]
[--json | --check]
FlagTypeDefaultRequiredMeaning
--rootpath~/.claudenoClaude Code data dir to scan. May also point at an archive’s per-machine dir, e.g. {root}/{machine}/claude.
--projectstringallnoLimit to a single encoded project dir name.
--jsonflagoffnoEmit a machine-readable snapshot (for periodic diffing).
--checkflagoffnoAudit only: print drift anomalies and exit non-zero if any.
$ python3 ~/code/pibmo-recall/scripts/probe/session_names.py --root "$ROOT/mymac/claude" --check
OK: name-bearing structures match the 2026-05-29 baseline (2 transcript(s), 0 pointer(s)).

Exit 0. On drift it prints DRIFT: N anomaly(ies) vs baseline <date>: with one ! line per anomaly and exits 1; a missing --root exits 2. The default human report additionally lists live session pointers, resolved --resume names per project, and the earliest Claude Code version each event type was seen in.