Spec 010 — Implementation Plan
中文版: plan.zh.md
Sync is a cross-cutting service, not a resource kind (see ADR-016). It follows the retention/credentials pattern across the four layers.
Layering
domain/sync/ pure value objects + contracts
manifest.py Manifest, SCHEMA_VERSION
models.py SyncConfig, SyncState, SyncStatus enum, ConflictInfo
serialization.py deterministic Resource <-> dict projection (pure)
errors.py SyncError family (codes for the error envelope)
application/sync/
ports.py GitPort, ManifestStore, WorkspaceLayout protocols
exporter.py vault -> workspace (files, resources, ciphertext)
importer.py workspace -> vault (mirror+reindex, reconcile, ciphertext)
service.py SyncService: run/status/config/resolve/key orchestration
worker.py SyncWorker: debounced + interval auto-sync loop
config_service.py SyncConfigService over the sync_config/sync_state rows
infrastructure/sync/
git_repo.py GitPort impl over the `git` subprocess (sole net egress)
workspace.py file mirror + manifest read/write + ciphertext dump/load
persistence.py SyncConfigModel, SyncStateModel + repos
surfaces/
http/sync_routes.py + sync_wiring.py /api/v1/sync/*
cli/sync_cmd.py `coffer sync` groupBuild order (TDD, each a committable chunk)
- domain/sync — models, manifest, deterministic serialization, errors. Unit tests: serialization determinism (sorted keys, excluded fields, round trip), status transitions.
- infrastructure/sync persistence — config/state tables + repos + migration
0017. Integration test: round-trip rows. - infrastructure/sync git_repo + workspace — git subprocess adapter and the file/manifest/ciphertext IO. Integration test against a local bare repo as
origin(real git). - application/sync exporter + importer — using a real
ResourceService+ real credential store + tmp vault dirs. Integration test: export → import round-trip reproduces resources, files, and decryptable credentials. - application/sync service + config_service — orchestrate a run, status, resolve, key export/import. Integration test: two tmp vaults + one bare repo simulate machine A and B; assert convergence, locked-credentials, conflict stop/resolve, and that the workspace never contains the master key.
- surfaces http + wiring — routes, schemas, error codes; wire in
_lifespanand startSyncWorker(gated onauto). Contract test for/api/v1/sync/*. - surfaces cli —
coffer syncgroup over the loopback client. Contract test. - frontend — Sync settings panel (config, status, run, resolve) on the existing API/query-key conventions.
- docs — architecture.md cross-cutting row, README feature line, bilingual companions; acceptance markers tie each
spec.mdscenario to a test.
Key constraints honored
- Every file ≤ 400 lines; split
service.py/importer.pyif they grow. application/must not importinfrastructure/: git/workspace/persistence are injected asGitPort/WorkspaceLayout/ repo protocols.domain/syncstays pure (no git, no sqlalchemy, no fs).- New error codes added to the HTTP error envelope
_STATUSmap. - Outbound git uses the user's ambient credentials via subprocess, not the Coffer HTTP client; no new loopback-binding exception.
response_modelon every route; mypy --strict; importlinter contract for thesyncpackage (it imports no other kind).