Slice 19 - Sync Status
Date: 2026-06-29
Scope#
Add read-only local transcript sync inspection:
codealmanac sync status
codealmanac sync status --from claude,codex --quiet 45m --wiki <name> --jsonThis slice does not run ingest, queue jobs, update the sync ledger, install automation, or write wiki prose.
Product Boundary#
sync means local transcript sweep. It is not hosted upload or cloud sync.
The status command answers:
- which supported transcript stores were scanned
- which quiet transcripts map to repos with
.almanac/ - which transcript line ranges are ready for a future ingest run
- which transcripts are skipped because they are still quiet, unchanged, already pending, unreadable, or cursor-conflicted
Architecture#
# CLI adapts flags only.
summary = app.workflows.sync.status(
RunSyncStatusRequest(
cwd=Path.cwd(),
wiki=args.wiki,
apps=parse_sync_apps(args.source_apps),
quiet=parse_quiet(args.quiet),
)
)SyncWorkflow coordinates the product status calculation. It calls
SourcesService.discover_transcripts(...), reads the per-repo sync ledger, and
evaluates cursor state.
SourcesService owns the TranscriptDiscoveryAdapter port. Concrete Codex and
Claude scanners live under integrations/sources/transcripts/ and translate
provider JSONL files into typed TranscriptCandidate models.
app.py is the composition root. It wires transcript discovery adapters into
SourcesService, matching Cosmic Python chapter 13's composition-root
guidance. The CLI does not import concrete Codex or Claude transcript scanners.
Data Shape#
TranscriptCandidate is a source observation:
- app:
claudeorcodex - session id
- transcript path
- transcript cwd
- mapped repo root
- modified time
- size
SyncLedgerEntry records the last absorbed cursor for a transcript:
- app and session id
- transcript path
- status
- last absorbed byte size
- last absorbed line
- last absorbed prefix hash
For a new transcript, sync status uses the empty transcript hash as the cursor. For a known transcript, status validates that the stored prefix still matches the current transcript bytes before reporting a ready range.
Duration Parsing#
--quiet uses humanfriendly.parse_timespan(...) instead of hand-rolled
duration parsing. The CLI converts it to datetime.timedelta before building
RunSyncStatusRequest.
Why Status Before Execution#
Full sync execution needs to avoid ingesting CodeAlmanac's own lifecycle
transcripts. Ingest and Garden now create provider transcripts through Claude
and Codex harness runs, but HarnessRunResult and RunRecord do not yet carry
the provider session id or transcript path that sync can mark as internal.
Until that feedback loop exists, sync status is safe because it is read-only.
sync execution remains gated.
Verification#
- focused transcript discovery, sync workflow, CLI, and architecture tests
- full pytest suite
- ruff
git diff --check- CLI help and JSON status smoke
- package build
- isolated dogfood with a synthetic Codex transcript mapped to a temp repo