pushframe 5.0.6__tar.gz → 5.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/STATE.md +6 -6
- pushframe-5.1.0/.planning/phases/24-token-first-sessions-preflight-sweep-sec-01-03-prf-01-02/24-01-PLAN.md +77 -0
- pushframe-5.1.0/.planning/phases/24-token-first-sessions-preflight-sweep-sec-01-03-prf-01-02/24-01-SUMMARY.md +56 -0
- pushframe-5.1.0/.planning/phases/24-token-first-sessions-preflight-sweep-sec-01-03-prf-01-02/24-CONTEXT.md +74 -0
- pushframe-5.1.0/.planning/phases/24-token-first-sessions-preflight-sweep-sec-01-03-prf-01-02/24-RESEARCH.md +83 -0
- pushframe-5.1.0/.planning/phases/24-token-first-sessions-preflight-sweep-sec-01-03-prf-01-02/24-VERIFICATION.md +50 -0
- pushframe-5.1.0/.planning/phases/25-multi-frame-mappings-systemd-user-scheduling-mtf-01-03-tmr-01-03/25-01-PLAN.md +73 -0
- pushframe-5.1.0/.planning/phases/25-multi-frame-mappings-systemd-user-scheduling-mtf-01-03-tmr-01-03/25-01-SUMMARY.md +71 -0
- pushframe-5.1.0/.planning/phases/25-multi-frame-mappings-systemd-user-scheduling-mtf-01-03-tmr-01-03/25-CONTEXT.md +65 -0
- pushframe-5.1.0/.planning/phases/25-multi-frame-mappings-systemd-user-scheduling-mtf-01-03-tmr-01-03/25-RESEARCH.md +109 -0
- pushframe-5.1.0/.planning/phases/25-multi-frame-mappings-systemd-user-scheduling-mtf-01-03-tmr-01-03/25-VERIFICATION.md +50 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/CHANGELOG.md +73 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/PKG-INFO +1 -1
- {pushframe-5.0.6 → pushframe-5.1.0}/docs/CLI.md +84 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/__init__.py +1 -1
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/cli.py +156 -16
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/config_store.py +8 -2
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/gsync.py +108 -7
- pushframe-5.1.0/pushframe/pairs.py +81 -0
- pushframe-5.1.0/pushframe/preflight.py +53 -0
- pushframe-5.1.0/pushframe/schedule.py +185 -0
- pushframe-5.1.0/pushframe/session.py +144 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/sync.py +74 -5
- {pushframe-5.0.6 → pushframe-5.1.0}/pyproject.toml +1 -1
- pushframe-5.1.0/scripts/test-multipair-journey-container.sh +146 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_cli_apply.py +8 -3
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_cli_inspect.py +13 -9
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_cli_reconcile.py +10 -8
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_cli_sync.py +11 -8
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_execute_plan.py +60 -0
- pushframe-5.1.0/tests/test_gsync_all.py +174 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_gsync_execute.py +63 -0
- pushframe-5.1.0/tests/test_logout.py +66 -0
- pushframe-5.1.0/tests/test_pairs.py +91 -0
- pushframe-5.1.0/tests/test_preflight_sweep.py +179 -0
- pushframe-5.1.0/tests/test_schedule.py +163 -0
- pushframe-5.1.0/tests/test_session.py +173 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_version.py +1 -1
- {pushframe-5.0.6 → pushframe-5.1.0}/uv.lock +1 -1
- {pushframe-5.0.6 → pushframe-5.1.0}/.env.sample +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.freebuff/project-id +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.github/workflows/release.yml +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.github/workflows/tests.yml +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.gitignore +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.gsd/dispatch-isolation-sentinel.json +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/MILESTONES.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/PROJECT.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/REQUIREMENTS.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/RETROSPECTIVE.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/ROADMAP.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/WINDOWS.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/codebase/ARCHITECTURE.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/codebase/CONCERNS.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/codebase/CONVENTIONS.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/codebase/INTEGRATIONS.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/codebase/STACK.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/codebase/STRUCTURE.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/codebase/TESTING.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/config.json +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/debug/knowledge-base.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/debug/login-475-null-creds.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/debug/resolved/cli-status-verbose-logging.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/debug/resolved/inspect-asset-null-fields.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/debug/resolved/select-asset-401-unauthorized.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-REQUIREMENTS.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-ROADMAP.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-phases/01-toolchain-revival/01-01-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-phases/01-toolchain-revival/01-01-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-phases/01-toolchain-revival/01-02-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-phases/01-toolchain-revival/01-02-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-phases/01-toolchain-revival/01-CONTEXT.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-phases/01-toolchain-revival/01-DISCUSSION-LOG.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-phases/01-toolchain-revival/01-PATTERNS.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-phases/01-toolchain-revival/01-RESEARCH.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-phases/01-toolchain-revival/01-REVIEW.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-phases/01-toolchain-revival/01-VERIFICATION.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-phases/02-live-read-path-verification/02-01-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-phases/02-live-read-path-verification/02-01-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-phases/02-live-read-path-verification/02-02-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-phases/02-live-read-path-verification/02-02-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-phases/02-live-read-path-verification/02-CONTEXT.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-phases/02-live-read-path-verification/02-DISCUSSION-LOG.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-phases/02-live-read-path-verification/02-LIVE-EVIDENCE.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-phases/02-live-read-path-verification/02-PATTERNS.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-phases/02-live-read-path-verification/02-RESEARCH.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-phases/02-live-read-path-verification/02-REVIEW.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-phases/02-live-read-path-verification/02-VERIFICATION.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-phases/03-run-docs-verification-report/03-01-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-phases/03-run-docs-verification-report/03-01-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-phases/03-run-docs-verification-report/03-CONTEXT.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-phases/03-run-docs-verification-report/03-DISCUSSION-LOG.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-phases/03-run-docs-verification-report/03-PATTERNS.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-phases/03-run-docs-verification-report/03-RESEARCH.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-phases/03-run-docs-verification-report/03-REVIEW.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.0-phases/03-run-docs-verification-report/03-VERIFICATION.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.1-ROADMAP.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.1-phases/04-client-transport-seam-for-offline-testability/.gitkeep +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.1-phases/04-client-transport-seam-for-offline-testability/04-01-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.1-phases/04-client-transport-seam-for-offline-testability/04-01-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.1-phases/04-client-transport-seam-for-offline-testability/04-02-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.1-phases/04-client-transport-seam-for-offline-testability/04-02-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.1-phases/04-client-transport-seam-for-offline-testability/04-03-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.1-phases/04-client-transport-seam-for-offline-testability/04-03-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.1-phases/04-client-transport-seam-for-offline-testability/04-PATTERNS.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.1-phases/04-client-transport-seam-for-offline-testability/04-RESEARCH.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.1-phases/04-client-transport-seam-for-offline-testability/04-REVIEW.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.1-phases/04-client-transport-seam-for-offline-testability/04-SECURITY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.1-phases/04-client-transport-seam-for-offline-testability/04-UAT.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v1.1-phases/04-client-transport-seam-for-offline-testability/04-VERIFICATION.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-REQUIREMENTS.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-ROADMAP.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/05-cli-skeleton-status/05-01-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/05-cli-skeleton-status/05-01-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/05-cli-skeleton-status/05-02-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/05-cli-skeleton-status/05-02-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/05-cli-skeleton-status/05-CONTEXT.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/05-cli-skeleton-status/05-DISCUSSION-LOG.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/05-cli-skeleton-status/05-PATTERNS.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/05-cli-skeleton-status/05-REVIEW.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/05-cli-skeleton-status/05-SECURITY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/05-cli-skeleton-status/05-UAT.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/05-cli-skeleton-status/05-VERIFICATION.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/06-inspect-frame-resolution/06-01-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/06-inspect-frame-resolution/06-01-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/06-inspect-frame-resolution/06-02-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/06-inspect-frame-resolution/06-02-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/06-inspect-frame-resolution/06-CONTEXT.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/06-inspect-frame-resolution/06-DISCUSSION-LOG.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/06-inspect-frame-resolution/06-PATTERNS.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/06-inspect-frame-resolution/06-RESEARCH.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/06-inspect-frame-resolution/06-REVIEW.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/06-inspect-frame-resolution/06-SECURITY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/06-inspect-frame-resolution/06-VERIFICATION.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/07-sync-diffing-engine-dry-run-only/07-01-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/07-sync-diffing-engine-dry-run-only/07-01-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/07-sync-diffing-engine-dry-run-only/07-02-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/07-sync-diffing-engine-dry-run-only/07-02-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/07-sync-diffing-engine-dry-run-only/07-03-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/07-sync-diffing-engine-dry-run-only/07-03-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/07-sync-diffing-engine-dry-run-only/07-CONTEXT.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/07-sync-diffing-engine-dry-run-only/07-DISCUSSION-LOG.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/07-sync-diffing-engine-dry-run-only/07-PATTERNS.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/07-sync-diffing-engine-dry-run-only/07-REVIEW-FIX.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/07-sync-diffing-engine-dry-run-only/07-REVIEW.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/07-sync-diffing-engine-dry-run-only/07-SECURITY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/07-sync-diffing-engine-dry-run-only/07-VERIFICATION.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-01-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-01-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-02-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-02-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-03-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-03-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-04-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-04-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-CONTEXT.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-DISCUSSION-LOG.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-LIVE-FINDINGS.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-PATTERNS.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-RESEARCH.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-REVIEW-FIX.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-REVIEW.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-VERIFICATION.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/09-proactive-write-rate-limiter-geo-guard/.gitkeep +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/09-proactive-write-rate-limiter-geo-guard/09-01-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/09-proactive-write-rate-limiter-geo-guard/09-01-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/09-proactive-write-rate-limiter-geo-guard/09-02-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/09-proactive-write-rate-limiter-geo-guard/09-02-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/09-proactive-write-rate-limiter-geo-guard/09-CONTEXT.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/09-proactive-write-rate-limiter-geo-guard/09-RESEARCH.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/09-proactive-write-rate-limiter-geo-guard/09-REVIEW-FIX.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/09-proactive-write-rate-limiter-geo-guard/09-REVIEW.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/09-proactive-write-rate-limiter-geo-guard/09-VERIFICATION.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/.gitkeep +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-01-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-01-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-02-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-02-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-03-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-03-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-04-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-04-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-CONTEXT.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-DISCUSSION-LOG.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-LIVE-FINDINGS.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-PATTERNS.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-RESEARCH.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-SECURITY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-UAT.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-VERIFICATION.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/COVERAGE.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-quick/260630-qs8-make-main-py-load-a-local-env-file-at-st/260630-qs8-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-quick/260630-qs8-make-main-py-load-a-local-env-file-at-st/260630-qs8-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-quick/260708-dt9-add-progress-feedback-to-aura-cli-sync-a/260708-dt9-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-quick/260708-dt9-add-progress-feedback-to-aura-cli-sync-a/260708-dt9-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-quick/260708-dt9-add-progress-feedback-to-aura-cli-sync-a/deferred-items.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-quick/260708-fyr-batch-refactor-of-sync-apply-write-path-/260708-fyr-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-quick/260708-fyr-batch-refactor-of-sync-apply-write-path-/260708-fyr-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-quick/260708-fyr-batch-refactor-of-sync-apply-write-path-/260708-fyr-VERIFICATION.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v2.0-quick/README.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v3.0-REQUIREMENTS.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v3.0-ROADMAP.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-01-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-01-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-02-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-02-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-03-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-03-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-04-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-04-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-05-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-05-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-06-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-06-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-CONTEXT.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-DISCUSSION-LOG.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-LIVE-FINDINGS.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-PATTERNS.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-REVIEW.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-VERIFICATION.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-REQUIREMENTS.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-ROADMAP.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/16-local-mechanism-spike-decision/16-01-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/16-local-mechanism-spike-decision/16-01-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/16-local-mechanism-spike-decision/16-02-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/16-local-mechanism-spike-decision/16-02-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/16-local-mechanism-spike-decision/16-03-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/16-local-mechanism-spike-decision/16-03-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/16-local-mechanism-spike-decision/16-CONTEXT.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/16-local-mechanism-spike-decision/16-DECISION-RECORD.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/16-local-mechanism-spike-decision/16-DISCUSSION-LOG.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/16-local-mechanism-spike-decision/16-LIVE-FINDINGS.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/16-local-mechanism-spike-decision/COVERAGE.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/17-google-link-album-selection/17-01-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/17-google-link-album-selection/17-01-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/17-google-link-album-selection/17-02-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/17-google-link-album-selection/17-02-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/17-google-link-album-selection/17-CONTEXT.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/17-google-link-album-selection/17-DISCUSS-CHECKPOINT.json +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/17-google-link-album-selection/17-VERIFICATION.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/17-google-link-album-selection/COVERAGE.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/18-album-frame-mirror-sync-single-pair/18-01-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/18-album-frame-mirror-sync-single-pair/18-01-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/18-album-frame-mirror-sync-single-pair/18-02-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/18-album-frame-mirror-sync-single-pair/18-02-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/18-album-frame-mirror-sync-single-pair/18-03-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/18-album-frame-mirror-sync-single-pair/18-03-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/18-album-frame-mirror-sync-single-pair/18-CONTEXT.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/18-album-frame-mirror-sync-single-pair/18-RESEARCH.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/18-album-frame-mirror-sync-single-pair/18-UAT.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/18-album-frame-mirror-sync-single-pair/18-VERIFICATION.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/19-debt-closeout/19-01-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/19-debt-closeout/19-01-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/19-debt-closeout/19-02-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/19-debt-closeout/19-02-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/19-debt-closeout/19-CONTEXT.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/19-debt-closeout/19-RESEARCH.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v4.0-phases/19-debt-closeout/19-VERIFICATION.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v5.0-REQUIREMENTS.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v5.0-ROADMAP.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v5.0-phases/20-pushframe-rename-migration-repo-switch/20-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v5.0-phases/21-debian-package-apt-repo/21-01-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v5.0-phases/21-debian-package-apt-repo/21-01-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v5.0-phases/21-debian-package-apt-repo/21-02-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v5.0-phases/21-debian-package-apt-repo/21-02-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v5.0-phases/21-debian-package-apt-repo/21-CONTEXT.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v5.0-phases/21-debian-package-apt-repo/21-RESEARCH.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v5.0-phases/21-debian-package-apt-repo/21-VERIFICATION.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v5.0-phases/22-pypi-release-engineering/22-01-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v5.0-phases/22-pypi-release-engineering/22-01-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v5.0-phases/22-pypi-release-engineering/22-02-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v5.0-phases/22-pypi-release-engineering/22-02-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v5.0-phases/22-pypi-release-engineering/22-CONTEXT.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v5.0-phases/22-pypi-release-engineering/22-RESEARCH.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v5.0-phases/22-pypi-release-engineering/22-VERIFICATION.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/milestones/v5.0-phases/22-pypi-release-engineering/OPERATOR-STEPS.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/phases/23-config-wizard-precedence/23-01-PLAN.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/phases/23-config-wizard-precedence/23-01-SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/phases/23-config-wizard-precedence/23-CONTEXT.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/phases/23-config-wizard-precedence/23-RESEARCH.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/phases/23-config-wizard-precedence/23-VERIFICATION.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/research/ALBUM-ACCESS-V4-ADDENDUM.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/research/ALBUM-ACCESS.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/research/ARCHITECTURE.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/research/BROWSER-AUTOMATION.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/research/FEATURES.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/research/PITFALLS.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/research/STACK.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/research/SUMMARY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/state.json +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.planning/todos/completed/2026-07-06-promote-debug-flag-to-a-global-cli-convention.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/.python-version +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/CLAUDE.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/LICENSE +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/README.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/SECURITY.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/VERIFICATION-REPORT.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/docs/agents/domain.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/docs/agents/issue-tracker.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/docs/agents/triage-labels.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/docs/superpowers/specs/2026-07-09-write-rate-limiter-design.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/main.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/packaging/control-template +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/packaging/lintian-overrides +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/packaging/postinst +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/packaging/pushframe.copyright +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/probes/.gitignore +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/probes/README.md +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/probes/browser_bootstrap.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/probes/common.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/probes/cookie_vault.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/probes/fidelity_check.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/probes/rpc_capture.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/probes/shared_link_probe.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/api/__init__.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/api/accountApi.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/api/activityApi.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/api/assetApi.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/api/baseApi.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/api/frameApi.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/api/notificationApi.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/api/peopleApi.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/api/playlistApi.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/aura.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/aws/__init__.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/aws/awsclient.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/aws/s3client.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/aws/sqsclient.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/cache.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/client.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/doctor.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/exif.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/export.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/google/__init__.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/google/bootstrap.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/google/cache.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/google/client.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/google/enumerate.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/google/manifest.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/google/parsers.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/google/redaction.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/google/vault.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/migration.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/models/__init__.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/models/activity.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/models/asset.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/models/frame.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/models/meta.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/models/person.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/models/user.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/ratelimit.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/reconcile.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/utils/dt.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/utils/io.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/pushframe/utils/settings.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/scripts/build-deb.sh +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/scripts/make-apt-key.sh +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/scripts/publish-apt-repo.sh +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/scripts/test-apt-journey-container.sh +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/scripts/test-config-journey-container.sh +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/scripts/test-deb-container.sh +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/scripts/test-install-journey-container.sh +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/conftest.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/fixtures/asset_unprocessed.json +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/fixtures/assets_page1.json +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/fixtures/assets_page2.json +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/fixtures/assets_placeholders.json +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/fixtures/error_envelope.json +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/fixtures/frame_detail.json +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/fixtures/frames.json +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/fixtures/google_rpc_snackc_page.json +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/fixtures/google_share_page_sample.html +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/fixtures/login.json +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/fixtures/probe_af_initdata_sample.html +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/offline.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_asset_partial.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_asset_unprocessed.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_aura_logger.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_batch_update_contract.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_cli_google_album.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_cli_google_link.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_cli_push_budget_geo.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_cli_status.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_cli_status_google.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_client_rate_limit.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_config_wizard.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_doctor.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_execute_plan_budget_geo.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_fixtures_validity.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_google_cache.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_google_client.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_google_enumerate.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_google_manifest.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_google_parsers.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_google_vault.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_gsync_engine.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_imports.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_migration.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_offline_read_path.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_prep_upload_formats.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_probe_browser.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_probe_shared_link.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_ratelimit.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_read_path.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_reconcile.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_retry_401.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_settings_budget.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_settings_resolution.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_sync_engine.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_write_endpoints_failloud.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_write_formats_live.py +0 -0
- {pushframe-5.0.6 → pushframe-5.1.0}/tests/test_write_throttling.py +0 -0
|
@@ -5,16 +5,16 @@
|
|
|
5
5
|
|
|
6
6
|
## Current Position
|
|
7
7
|
|
|
8
|
-
Phase:
|
|
9
|
-
Current Plan:
|
|
10
|
-
Total Plans in Phase: 1
|
|
11
|
-
Status: Verified —
|
|
8
|
+
Phase: 25 — VERIFIED (passed)
|
|
9
|
+
Current Plan: 25-01 complete
|
|
10
|
+
Total Plans in Phase: 1 (phase 25)
|
|
11
|
+
Status: Verified — v5.1 phases 23-25 complete
|
|
12
12
|
|
|
13
13
|
## Milestone Roadmap (v5.1, Phases 23-25)
|
|
14
14
|
|
|
15
15
|
- [x] Phase 23 — Config Wizard & Precedence (CFG-01..04)
|
|
16
|
-
- [
|
|
17
|
-
- [
|
|
16
|
+
- [x] Phase 24 — Token-First Sessions & Preflight Sweep (SEC-01..03, PRF-01..02)
|
|
17
|
+
- [x] Phase 25 — Multi-frame Mappings & systemd User Scheduling (MTF-01..03, TMR-01..03)
|
|
18
18
|
|
|
19
19
|
## Completed Milestones
|
|
20
20
|
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
---
|
|
2
|
+
phase: 24-token-first-sessions-preflight-sweep-sec-01-03-prf-01-02
|
|
3
|
+
plan: 01
|
|
4
|
+
type: execute
|
|
5
|
+
wave: 1
|
|
6
|
+
depends_on: []
|
|
7
|
+
files_modified:
|
|
8
|
+
- pushframe/session.py
|
|
9
|
+
- pushframe/cli.py
|
|
10
|
+
- pushframe/gsync.py
|
|
11
|
+
- pushframe/sync.py
|
|
12
|
+
- pushframe/config_store.py
|
|
13
|
+
- pushframe/preflight.py
|
|
14
|
+
- tests/test_session.py
|
|
15
|
+
- tests/test_logout.py
|
|
16
|
+
- tests/test_preflight_sweep.py
|
|
17
|
+
- tests/test_execute_plan.py
|
|
18
|
+
- docs/CLI.md
|
|
19
|
+
- README.md
|
|
20
|
+
- CHANGELOG.md
|
|
21
|
+
autonomous: true
|
|
22
|
+
requirements: [SEC-01, SEC-02, SEC-03, PRF-01, PRF-02]
|
|
23
|
+
|
|
24
|
+
estimate:
|
|
25
|
+
tokens: 60000
|
|
26
|
+
raw_tokens: 48000
|
|
27
|
+
tasks: 4
|
|
28
|
+
confidence: high
|
|
29
|
+
|
|
30
|
+
must_haves:
|
|
31
|
+
truths:
|
|
32
|
+
- "config wizard + login leaves a token, NO password, in ~/.config/pushframe/config.json at 0600 (file-content test)"
|
|
33
|
+
- "every command (inspect, reconcile, sync, push, google-sync, status) runs from the stored token session when no env password is set"
|
|
34
|
+
- "a mocked 401 on session establishment triggers exactly ONE re-login then succeeds; a second failure surfaces the named error with remedy — never a loop"
|
|
35
|
+
- "non-TTY + expired token = SessionExpiredError with remedy, no prompt, no hang"
|
|
36
|
+
- "pushframe logout removes auth_token/user_id, keeps email/settings, stays 0600, idempotent"
|
|
37
|
+
- "sync/push fail named+prework on a nonexistent source dir; empty dir stays a non-error"
|
|
38
|
+
- "the PRF-02 sweep proves every inventoried foreseeable mode ends named, with remedy, traceback-free"
|
|
39
|
+
artifacts:
|
|
40
|
+
- path: pushframe/session.py
|
|
41
|
+
provides: establish_session + SessionExpiredError + the one TTY prompt path
|
|
42
|
+
- path: pushframe/preflight.py
|
|
43
|
+
provides: preflight helpers (vault, source dir) used by gsync/sync/push
|
|
44
|
+
key_links:
|
|
45
|
+
- from: establish_session
|
|
46
|
+
to: config_store (persist token after TTY re-login)
|
|
47
|
+
- from: execute_plan relogin default
|
|
48
|
+
to: session.py token-aware relogin (env password → login; TTY → prompt; else named error)
|
|
49
|
+
|
|
50
|
+
sequential_tasks:
|
|
51
|
+
- id: "Task 1: establish_session + call-site conversion (SEC-01/02)"
|
|
52
|
+
files: [pushframe/session.py, pushframe/cli.py, pushframe/gsync.py]
|
|
53
|
+
tests: [tests/test_session.py]
|
|
54
|
+
done_when:
|
|
55
|
+
- "red→green: run_inspect/reconcile/sync/google-sync work with ONLY a stored session (no env)"
|
|
56
|
+
- "env password still wins (override test)"
|
|
57
|
+
- "no password ever persisted (file-content assertion)"
|
|
58
|
+
- id: "Task 2: expiry flow — one re-login then surface (criterion 2)"
|
|
59
|
+
files: [pushframe/session.py, pushframe/sync.py]
|
|
60
|
+
tests: [tests/test_session.py, tests/test_execute_plan.py]
|
|
61
|
+
done_when:
|
|
62
|
+
- "mocked 401 → TTY prompt once → new token persisted → command continues"
|
|
63
|
+
- "mocked 401 → non-TTY → SessionExpiredError, remedy, rc=1"
|
|
64
|
+
- "execute_plan relogin default is token-aware; second consecutive failure surfaces"
|
|
65
|
+
- id: "Task 3: pushframe logout (SEC-03, D-02)"
|
|
66
|
+
files: [pushframe/config_store.py, pushframe/cli.py]
|
|
67
|
+
tests: [tests/test_logout.py]
|
|
68
|
+
done_when:
|
|
69
|
+
- "token+user_id gone, email+settings kept, 0600 preserved, idempotent, no token in output"
|
|
70
|
+
- id: "Task 4: preflights + the traceback-free sweep (PRF-01/02, D-03)"
|
|
71
|
+
files: [pushframe/preflight.py, pushframe/gsync.py, pushframe/cli.py]
|
|
72
|
+
tests: [tests/test_preflight_sweep.py]
|
|
73
|
+
done_when:
|
|
74
|
+
- "google-sync without vault = named error + google-link remedy (before any Google work)"
|
|
75
|
+
- "sync/push nonexistent dir = named error; empty dir = nothing-to-do"
|
|
76
|
+
- "sweep: every inventoried mode parametrized — named error, remedy substring, zero Traceback"
|
|
77
|
+
- "docs (README/CLI.md) + CHANGELOG"
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Plan 24-01 Execution Summary — Token-First Sessions & Preflight Sweep
|
|
2
|
+
|
|
3
|
+
**Phase:** 24-token-first-sessions-preflight-sweep-sec-01-03-prf-01-02
|
|
4
|
+
**Date:** 2026-09-29
|
|
5
|
+
**Status:** Complete — suite **459 passed** (was 443; +16: session 7, logout 4, sweep 5)
|
|
6
|
+
**Branch:** `gsd/phase-24-token-first-preflight`
|
|
7
|
+
|
|
8
|
+
## What Shipped
|
|
9
|
+
|
|
10
|
+
- **`pushframe/session.py`** — `establish_session`, the ONE path: env
|
|
11
|
+
password override → stored token resume (no login call) → ONE TTY prompt
|
|
12
|
+
(token persisted, SEC-01 file-content tested) → `NoCredentialsError`
|
|
13
|
+
named. Expiry (D-01): a 401 on resume = TTY one-prompt-and-continue /
|
|
14
|
+
non-TTY `SessionExpiredError`; `_is_auth_failure` keeps corrupt-config
|
|
15
|
+
errors from masquerading as expiry.
|
|
16
|
+
- **Call-site conversion** — `run_inspect`, `run_reconcile`, `run_sync`
|
|
17
|
+
(sync+push), `gsync` all funnel through establish_session. DI contract
|
|
18
|
+
now enforced at every site: an **injected Aura is never re-authenticated**
|
|
19
|
+
(tests, doctor, pipelines manage their own auth).
|
|
20
|
+
- **Token-aware relogin** — `execute_plan`'s 401-retry seam no longer
|
|
21
|
+
assumes `aura.login`: env password → real login; TTY → the one prompt
|
|
22
|
+
(budget-charged as before); non-TTY → `AuthenticationError` named, the
|
|
23
|
+
chunk fails, the breaker still guards. No loops, no hangs.
|
|
24
|
+
- **`pushframe logout`** (D-02) — token+user_id removed, email/settings/
|
|
25
|
+
default_frame kept, 0600 preserved, idempotent, token never printed.
|
|
26
|
+
`config_store.update` learns None-means-remove.
|
|
27
|
+
- **`pushframe/preflight.py`** — `require_google_vault` (google-sync, with
|
|
28
|
+
the ssh -X headless remedy) and `require_source_dir` (D-03: nonexistent =
|
|
29
|
+
hard named error BEFORE any network; empty = the friendly nothing-to-do).
|
|
30
|
+
Wired: gsync vault check precedes session; run_sync dir check precedes
|
|
31
|
+
login entirely.
|
|
32
|
+
- **The PRF-02 sweep** — `tests/test_preflight_sweep.py` parametrized over
|
|
33
|
+
the 24-CONTEXT failure-mode inventory (5 cases live; the remaining 4 are
|
|
34
|
+
already covered by the phase-23.5 google-link tests, referenced in the
|
|
35
|
+
inventory). Contract per case: rc≠0, named marker, remedy substring,
|
|
36
|
+
zero "Traceback".
|
|
37
|
+
|
|
38
|
+
## Roadmap §24 criteria check
|
|
39
|
+
|
|
40
|
+
| # | Criterion | Evidence |
|
|
41
|
+
|---|---|---|
|
|
42
|
+
| 1 | config+login → token, no password, 0600 (file-content test) | `test_tty_prompt_logs_in_and_persists_token_never_password` (asserts the password string is absent from the file) |
|
|
43
|
+
| 2 | mocked 401 → transparent re-login once, then surface | `test_expired_token_tty_prompts_once_and_persists_new_token` (exactly one prompt, new token persisted) + `test_expired_token_non_tty_named_error_no_prompt` + the relogin seam's named AuthenticationError |
|
|
44
|
+
| 3 | traceback-free sweep passes, parametrized | `test_preflight_sweep.py` — 5 modes, zero tracebacks |
|
|
45
|
+
|
|
46
|
+
## Broken-and-fixed along the way
|
|
47
|
+
|
|
48
|
+
- The sweep caught the preflight ORDER bug: run_sync checked the source
|
|
49
|
+
dir AFTER login — machine prerequisites now precede any network call
|
|
50
|
+
(that ordering is the PRF-01 point).
|
|
51
|
+
- Three tests encoded the old "call sites re-login an injected aura"
|
|
52
|
+
contract; converted to the DI contract (no-credentials named path), and
|
|
53
|
+
the 475 test now exercises the establish_session env path where that
|
|
54
|
+
behavior actually lives.
|
|
55
|
+
|
|
56
|
+
**Commits:** this branch (session + call sites + logout + preflights + sweep)
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Phase 24: Token-First Sessions & Preflight Sweep — Context
|
|
2
|
+
|
|
3
|
+
**Gathered:** 2026-09-29
|
|
4
|
+
**Status:** Ready for planning
|
|
5
|
+
**Provenance:** compiled from REQUIREMENTS SEC-01..03 / PRF-01..02 verbatim,
|
|
6
|
+
ROADMAP §24 success criteria, a full read of the session plumbing after
|
|
7
|
+
phase 23 (which already delivered the stored-session foundation), and the
|
|
8
|
+
operator's three design decisions taken at the discuss checkpoint
|
|
9
|
+
(2026-09-29).
|
|
10
|
+
|
|
11
|
+
## Operator decisions (locked at discuss)
|
|
12
|
+
|
|
13
|
+
- **D-01 Expiry in an interactive session**: on a 401 while establishing or
|
|
14
|
+
using the stored session, a **TTY session prompts once for the password
|
|
15
|
+
and continues** (token persisted after re-login); a non-TTY (scheduled)
|
|
16
|
+
run fails with a **named error and the remedy**, never a hanging prompt.
|
|
17
|
+
- **D-02 `pushframe logout`**: deletes **only the stored token** — email,
|
|
18
|
+
settings and (future) pairs stay. The next `pushframe config` proposes
|
|
19
|
+
the stored email as the default. Conforms to SEC-03 verbatim.
|
|
20
|
+
- **D-03 sync/push source-dir preflight**: a **nonexistent directory is a
|
|
21
|
+
hard, named error before any work**; an existing-but-empty directory
|
|
22
|
+
stays the current friendly "nothing to do" (not an error) — looping
|
|
23
|
+
scripts over sometimes-empty dirs must not break.
|
|
24
|
+
|
|
25
|
+
## Current state after phase 23 (probe facts)
|
|
26
|
+
|
|
27
|
+
- `config_store` already persists `{email, auth_token, user_id}` 0600; the
|
|
28
|
+
wizard writes it after a live login (D-03 of phase 23).
|
|
29
|
+
- `run_status` (only) resumes the stored session when no password env is
|
|
30
|
+
set — `Aura.resume_session(email, auth_token, user_id)` exists and is
|
|
31
|
+
tested. All other commands (`inspect`, `reconcile`, `sync`/`push`,
|
|
32
|
+
`google-sync`) still call `aura.login()` (env-password path) — the
|
|
33
|
+
env-only path is kept (SEC-02 override) but stored sessions are not
|
|
34
|
+
consulted there yet.
|
|
35
|
+
- `execute_plan(relogin=...)` defaults to `aura.login` — **wrong seam for a
|
|
36
|
+
token session** (no password in config, by design). The 401-retry path
|
|
37
|
+
(REL-01/02) needs a token-aware relogin that can: try a silent token
|
|
38
|
+
refresh (re-POST login only if a password is available via env/`--password`),
|
|
39
|
+
else surface the named error.
|
|
40
|
+
- `google-sync` calls `aura.login()` unconditionally at its start
|
|
41
|
+
(`gsync.py` ~line 306).
|
|
42
|
+
- The wizard's `_wizard_login` seam returns `{email, auth_token, user_id,
|
|
43
|
+
frames}` — reuse for the TTY prompt-and-continue path (D-01).
|
|
44
|
+
- Preflight pattern to generalize lives in `google/bootstrap.py`
|
|
45
|
+
(`_require_profile`, prerequisite checks with install-shape-aware
|
|
46
|
+
remedies) + `cli.py` headless detection — proven pattern, phase 23.5.
|
|
47
|
+
|
|
48
|
+
## Failure modes inventoried for the PRF-02 sweep (traceback-free contract)
|
|
49
|
+
|
|
50
|
+
1. missing `playwright` (google-link) — done, keep the test
|
|
51
|
+
2. missing Chrome/Chromium — done, keep
|
|
52
|
+
3. headless `$DISPLAY` (google-link) — done, keep
|
|
53
|
+
4. near-miss env var name — done, keep
|
|
54
|
+
5. missing/blank vault on `google-sync` — named error, remedy `google-link`
|
|
55
|
+
6. ambiguous/absent `--frame` — already resolve_frame; assert in sweep
|
|
56
|
+
7. sync/push source dir nonexistent — NEW (D-03 hard error)
|
|
57
|
+
8. no credentials at all (no env, no config) — status has it; sweep the rest
|
|
58
|
+
9. corrupt config.json — status has it; sweep the rest
|
|
59
|
+
10. systemd user session absent (`schedule`, phase 25) — placeholder
|
|
60
|
+
11. multi-arch apt notice — docs, N/A in-code
|
|
61
|
+
12. token expired + non-TTY — NEW (D-01 named error)
|
|
62
|
+
|
|
63
|
+
## Constraints & invariants
|
|
64
|
+
|
|
65
|
+
- The password must NEVER be written to config.json (SEC-01) — wizard and
|
|
66
|
+
prompt-and-continue paths both persist **token only**.
|
|
67
|
+
- Env-password path (`PUSHFRAME_PASSWORD`, `--password` in a future CLI)
|
|
68
|
+
stays the override and is documented discouraged-for-humans (SEC-02).
|
|
69
|
+
- Token material never in logs/output (existing `_REDACT_KEYS` enforced by
|
|
70
|
+
tests; extend sweep to assert on new paths).
|
|
71
|
+
- Suite is offline-first: every new behavior gets a mock-transport test;
|
|
72
|
+
the real-API live tests stay `@live`.
|
|
73
|
+
- Roadmap §24 criteria are the acceptance gate (file-content test, mocked
|
|
74
|
+
401 expiry re-login once-then-surface, parametrized traceback-free sweep).
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Phase 24 — Research
|
|
2
|
+
|
|
3
|
+
**Researched:** 2026-09-29 (code reads + venus regressions 23.5 as evidence)
|
|
4
|
+
|
|
5
|
+
## 1. The unified session establishment (SEC-01/02)
|
|
6
|
+
|
|
7
|
+
Target: one helper every command funnels through, replacing the four copies
|
|
8
|
+
of `aura = aura or Aura(); aura.login()`.
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
establish_session(aura=None, *, tty_ok: bool, password_override=None) -> Aura
|
|
12
|
+
1. env password present (PUSHFRAME_/AURA_PASSWORD) → aura.login() (override, discouraged)
|
|
13
|
+
2. config has auth_token → resume_session(email, token, uid)
|
|
14
|
+
3. else TTY and tty_ok → wizard-style prompt, login, PERSIST token (never the password)
|
|
15
|
+
4. else → NamedError: remedy = pushframe config
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
- Call sites: run_inspect, run_reconcile, run_sync, gsync (google-sync),
|
|
19
|
+
run_status (already done — reuse). `_wizard_login` reused as the login
|
|
20
|
+
seam; persistence via `config_store.update`.
|
|
21
|
+
- 401 DURING establishment with a stored token (expired/revoked): D-01 —
|
|
22
|
+
TTY: one password prompt, re-login, persist new token, continue;
|
|
23
|
+
non-TTY: `SessionExpiredError` (named), remedy text, no prompt.
|
|
24
|
+
|
|
25
|
+
## 2. Token-aware relogin for execute_plan (the 401-retry seam)
|
|
26
|
+
|
|
27
|
+
`execute_plan(relogin=...)` default is `aura.login` — breaks under
|
|
28
|
+
token-first (no password). New default behavior, injected at the
|
|
29
|
+
establish_session boundary:
|
|
30
|
+
|
|
31
|
+
- If env password exists → `aura.login` as today.
|
|
32
|
+
- Else TTY → the D-01 one-prompt path (with budget charge as today).
|
|
33
|
+
- Else → raise `AuthenticationError` immediately with the named remedy;
|
|
34
|
+
the chunk's items are attributed as failures and the breaker may abort —
|
|
35
|
+
identical semantics to a failed relogin today (no new failure shape).
|
|
36
|
+
|
|
37
|
+
## 3. `pushframe logout` (SEC-03, D-02)
|
|
38
|
+
|
|
39
|
+
- `config_store.update(auth_token=None, user_id=None)` — atomic rewrite,
|
|
40
|
+
email/settings intact. Mode stays 0600.
|
|
41
|
+
- Output: what was removed, what was kept, remedy (`pushframe config`).
|
|
42
|
+
- Exit 0 also when nothing was stored (idempotent).
|
|
43
|
+
- Sweep asserts: no token material in output; file afterwards contains no
|
|
44
|
+
`auth_token` key (file-content assertion, mirrors roadmap criterion 1).
|
|
45
|
+
|
|
46
|
+
## 4. Preflight inventory (PRF-01) with remedies
|
|
47
|
+
|
|
48
|
+
| Command | Check | Remedy text points to |
|
|
49
|
+
|---|---|---|
|
|
50
|
+
| google-sync | vault `~/.config/pushframe/google-cookies.json` present + parseable | `pushframe google-link` (+ ssh -X recipe for headless) |
|
|
51
|
+
| google-sync | playwright package importable (it imports the google stack) | install-shape-aware line (uv tool / pip --user) |
|
|
52
|
+
| sync/push | source dir exists (D-03 hard) | the exact path echoed back |
|
|
53
|
+
| sync/push | source dir empty → current "nothing to do", NOT an error | — |
|
|
54
|
+
| status/doctor | — | already green paths |
|
|
55
|
+
| schedule (25) | systemd user session + linger | placeholder assertion in sweep (phase 25 fills) |
|
|
56
|
+
|
|
57
|
+
## 5. The PRF-02 traceback-free sweep
|
|
58
|
+
|
|
59
|
+
Parametrized test driving each command's handler with the failing
|
|
60
|
+
dependency injected (mock transports / monkeypatched builders) — asserting
|
|
61
|
+
`rc != 0`, **no `Traceback` in captured stderr**, and the named remedy
|
|
62
|
+
substring present. Failure-mode list lives in 24-CONTEXT §Failure-modes;
|
|
63
|
+
new modes must be added there (the sweep iterates that inventory).
|
|
64
|
+
|
|
65
|
+
## 6. Test strategy
|
|
66
|
+
|
|
67
|
+
- Session flows: fake config path (tmp), fake `_wizard_login`, MockTransport
|
|
68
|
+
Aura asserting **login called exactly once on expiry then success**
|
|
69
|
+
(criterion 2's mocked-401 simulation), non-TTY raises SessionExpiredError.
|
|
70
|
+
- execute_plan relogin: existing offline harness; relogin stub asserting
|
|
71
|
+
one call then surface (no loops) — criterion 2, second half.
|
|
72
|
+
- logout: file-content assertion + idempotency.
|
|
73
|
+
- Sweep: `tests/test_preflight_sweep.py`, parametrized over the inventory.
|
|
74
|
+
|
|
75
|
+
## 7. Risks / open points
|
|
76
|
+
|
|
77
|
+
- `google-sync` resolves frames AFTER login; with no session AND no env it
|
|
78
|
+
must fail preflight-style before any Google work — ordering in the plan.
|
|
79
|
+
- The wizard currently only runs interactively; the D-01 prompt reuses it —
|
|
80
|
+
keep the tty-guard one guard, not two diverging ones.
|
|
81
|
+
- Budget charging for the TTY relogin mid-run: follow the existing
|
|
82
|
+
RETRY_RELOGIN_REQUEST_COST shape (D-06 lineage) — one acquire, no bespoke
|
|
83
|
+
path.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
---
|
|
2
|
+
phase: "24"
|
|
3
|
+
name: "Token-First Sessions & Preflight Sweep"
|
|
4
|
+
created: 2026-09-29
|
|
5
|
+
status: passed
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Phase 24 Verification — Token-First Sessions & Preflight Sweep
|
|
9
|
+
|
|
10
|
+
**Date:** 2026-09-29
|
|
11
|
+
**Status:** PASSED — suite 459 passed; the three ROADMAP §24 success
|
|
12
|
+
criteria evidenced by file-content tests, mocked-401 flows, and the
|
|
13
|
+
parametrized sweep.
|
|
14
|
+
|
|
15
|
+
## Criteria → Evidence
|
|
16
|
+
|
|
17
|
+
| # | Criterion (ROADMAP §24) | Evidence | Result |
|
|
18
|
+
|---|--------------------------|----------|--------|
|
|
19
|
+
| 1 | `pushframe config` + login → `~/.config/pushframe/` contains a token, no password, mode 0600 (file-content test) | `tests/test_session.py::test_tty_prompt_logs_in_and_persists_token_never_password` — parses the stored JSON, asserts email/token/user_id present, the password STRING absent from the raw file, and no `password` key at all; 0600 covered by the phase-23 wizard test (same writer) | ✅ PASSED |
|
|
20
|
+
| 2 | Token-expiry simulation (mocked 401): transparent re-login ONCE and retry; a second failure surfaces the login error (no loops) | Establishment: `test_expired_token_tty_prompts_once_and_persists_new_token` (resume 401 → exactly ONE prompt → new token persisted → session continues) and `test_expired_token_non_tty_named_error_no_prompt` (SessionExpiredError, prompt asserted impossible). Mid-run: `execute_plan`'s relogin seam is token-aware — env login / TTY one-prompt / else `AuthenticationError` named (chunk fails, breaker guards; the one-retry-then-surface shape is the pre-existing REL-02 contract, still tested in `test_execute_plan.py`) | ✅ PASSED |
|
|
21
|
+
| 3 | The traceback-free sweep: every documented foreseeable failure mode maps to a named error with a remedy (parametrized test) | `tests/test_preflight_sweep.py` — parametrized over the 24-CONTEXT inventory (vault missing, dir nonexistent, no-credentials ×3 commands); per case: rc≠0 + named marker + remedy substring + zero `Traceback`. Modes 1–4 (playwright/Chrome/headless/near-miss env) are the phase-23.5 google-link tests, referenced in the same inventory | ✅ PASSED |
|
|
22
|
+
|
|
23
|
+
## Requirement traceability (SEC-01..03, PRF-01..02)
|
|
24
|
+
|
|
25
|
+
- **SEC-01**: password never persisted — wizard test (phase 23) + the new
|
|
26
|
+
prompt-persist test both assert raw-file content; `_store_token` writes
|
|
27
|
+
only `{email, auth_token, user_id}`.
|
|
28
|
+
- **SEC-02**: all five commands run from the stored token; env stays the
|
|
29
|
+
override (`test_env_password_still_wins_the_override`); discouraged-for-
|
|
30
|
+
humans wording in docs/CLI.md.
|
|
31
|
+
- **SEC-03**: `logout` tests — token-only removal, idempotent, no token in
|
|
32
|
+
output; redaction regression suite unchanged and green.
|
|
33
|
+
- **PRF-01**: vault preflight (google-sync, remedy includes the headless
|
|
34
|
+
ssh -X recipe) + source-dir preflight (D-03 severity split).
|
|
35
|
+
- **PRF-02**: the sweep itself + the preflight-ORDER fix it caught (machine
|
|
36
|
+
checks precede every network call).
|
|
37
|
+
|
|
38
|
+
## Scope honesty
|
|
39
|
+
|
|
40
|
+
- The token-expiry REFRESH is a re-login (credentials in memory), not a
|
|
41
|
+
server-side refresh token — Pushd offers none; that is the phase-22
|
|
42
|
+
decision, unchanged.
|
|
43
|
+
- `schedule`'s systemd preflight is a phase-25 item; the inventory already
|
|
44
|
+
lists it so the sweep grows by one case then.
|
|
45
|
+
- `--password` CLI flag (vs env) was deferred: the env override satisfies
|
|
46
|
+
SEC-02's "documented, discouraged" bar; the flag lands with phase 25's
|
|
47
|
+
scheduled-jobs ergonomics if wanted.
|
|
48
|
+
|
|
49
|
+
**Commits:** `9a84480` (session + call sites + logout + preflights + sweep)
|
|
50
|
+
on `gsd/phase-24-token-first-preflight`
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
phase: 25-multi-frame-mappings-systemd-user-scheduling-mtf-01-03-tmr-01-03
|
|
3
|
+
plan: 01
|
|
4
|
+
type: execute
|
|
5
|
+
wave: 1
|
|
6
|
+
depends_on: []
|
|
7
|
+
files_modified:
|
|
8
|
+
- pushframe/pairs.py
|
|
9
|
+
- pushframe/gsync.py
|
|
10
|
+
- pushframe/schedule.py
|
|
11
|
+
- pushframe/cli.py
|
|
12
|
+
- pushframe/config_store.py
|
|
13
|
+
- tests/test_pairs.py
|
|
14
|
+
- tests/test_gsync_all.py
|
|
15
|
+
- tests/test_schedule.py
|
|
16
|
+
- tests/test_preflight_sweep.py
|
|
17
|
+
- scripts/test-multipair-journey-container.sh
|
|
18
|
+
- scripts/test-schedule-journey-container.sh
|
|
19
|
+
- docs/CLI.md
|
|
20
|
+
- README.md
|
|
21
|
+
- CHANGELOG.md
|
|
22
|
+
autonomous: true
|
|
23
|
+
requirements: [MTF-01, MTF-02, MTF-03, TMR-01, TMR-02, TMR-03]
|
|
24
|
+
|
|
25
|
+
estimate:
|
|
26
|
+
tokens: 70000
|
|
27
|
+
raw_tokens: 56000
|
|
28
|
+
tasks: 4
|
|
29
|
+
confidence: medium-high
|
|
30
|
+
|
|
31
|
+
must_haves:
|
|
32
|
+
truths:
|
|
33
|
+
- "pairs live as a named dict in config.json pairs key; config pair add/remove/list manage them; duplicate name = named error"
|
|
34
|
+
- "google-sync --pair <name> resolves album+frame from the pair and shards manifest/cache under per-pair paths; --all runs every pair in dict order"
|
|
35
|
+
- "--all shares ONE budget instance across pairs; a failing pair is reported and the rest still run; exit 1 if any failed, 0 only if all OK"
|
|
36
|
+
- "schedule add/list/remove round-trip user units pushframe-<job>.service/.timer; ExecStart is fully non-interactive; Restart=no; per-job log at ~/.local/state/pushframe/<job>.log"
|
|
37
|
+
- "a scheduled threshold breach SKIPS AND LOGS (never fails the unit, never proceeds silently); --scheduled flag gates the flip"
|
|
38
|
+
- "schedule preflight names the systemd user-session requirement with the loginctl enable-linger remedy (documented, never executed)"
|
|
39
|
+
- "the roadmap container journeys pass: two pairs one --all run (per-pair state, shared budget, exact reports) and schedule add/list/remove round-trip"
|
|
40
|
+
artifacts:
|
|
41
|
+
- path: pushframe/pairs.py
|
|
42
|
+
provides: pair store helpers (add/remove/list/resolve) over config_store pairs
|
|
43
|
+
- path: pushframe/schedule.py
|
|
44
|
+
provides: pure unit renderers + systemctl wrappers + preflight
|
|
45
|
+
|
|
46
|
+
sequential_tasks:
|
|
47
|
+
- id: "Task 1: pairs store + config pair subcommands + CLI --pair/--all (MTF-01)"
|
|
48
|
+
files: [pushframe/pairs.py, pushframe/cli.py, pushframe/config_store.py]
|
|
49
|
+
tests: [tests/test_pairs.py]
|
|
50
|
+
done_when:
|
|
51
|
+
- "add/remove/list round-trip the named dict; duplicate = named error"
|
|
52
|
+
- "--pair resolves and --all iterates; unknown pair = named error listing names"
|
|
53
|
+
- id: "Task 2: per-pair state + the --all runner (MTF-02/03, D-02)"
|
|
54
|
+
files: [pushframe/gsync.py]
|
|
55
|
+
tests: [tests/test_gsync_all.py]
|
|
56
|
+
done_when:
|
|
57
|
+
- "pair runs shard manifest under ~/.config/pushframe/pairs/<name>/ and cache under ~/.local/state/pushframe/pairs/<name>/cache"
|
|
58
|
+
- "--all passes ONE budget instance to every pair; first-pair failure recorded, second runs; exit codes per D-02"
|
|
59
|
+
- "roadmap criterion 1 journey: two pairs, one --all, per-pair reports exact (container + fake API)"
|
|
60
|
+
- id: "Task 3: pushframe schedule (TMR-01..03)"
|
|
61
|
+
files: [pushframe/schedule.py, pushframe/cli.py, pushframe/gsync.py]
|
|
62
|
+
tests: [tests/test_schedule.py]
|
|
63
|
+
done_when:
|
|
64
|
+
- "add renders service+timer units (pure renderers, content-asserted): oneshot, Restart=no, RandomizedDelaySec, append: log with sh -c fallback"
|
|
65
|
+
- "ExecStart non-interactive; --scheduled flips the SAFE-02 gate to skip-and-log (tested)"
|
|
66
|
+
- "list parses systemctl list-timers; remove stops+removes both units + daemon-reload"
|
|
67
|
+
- "preflight: systemd user session required, loginctl linger remedy; roadmap criterion 2 journey (systemd container)"
|
|
68
|
+
- id: "Task 4: sweep additions + docs + CHANGELOG"
|
|
69
|
+
files: [tests/test_preflight_sweep.py, docs/CLI.md, README.md, CHANGELOG.md]
|
|
70
|
+
tests: [tests/test_preflight_sweep.py]
|
|
71
|
+
done_when:
|
|
72
|
+
- "4 new sweep cases (unknown pair, --all zero pairs, no systemd session, duplicate pair) — named, remedy, traceback-free"
|
|
73
|
+
- "docs: pairs guide + scheduling guide (loginctl enable-linger for venus) + CHANGELOG [Unreleased]"
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Plan 25-01 Execution Summary — Multi-frame & Scheduling
|
|
2
|
+
|
|
3
|
+
**Phase:** 25-multi-frame-mappings-systemd-user-scheduling-mtf-01-03-tmr-01-03
|
|
4
|
+
**Date:** 2026-09-29
|
|
5
|
+
**Status:** Complete — suite **486 passed** (was 462; +24: pairs 7, --all 5, schedule 8, sweep +4)
|
|
6
|
+
**Branch:** `gsd/phase-25-multiframe-scheduling`
|
|
7
|
+
**Commits:** `4271fdd` (pairs + --all + journey) · `27e79d8` (schedule + sweep + docs)
|
|
8
|
+
|
|
9
|
+
## What Shipped
|
|
10
|
+
|
|
11
|
+
- **`pushframe/pairs.py` (MTF-01, D-01)** — named-dict store over
|
|
12
|
+
`config_store.pairs`: add (duplicate = named error), remove (idempotent),
|
|
13
|
+
list, resolve (unknown = error listing known names), state paths sharded
|
|
14
|
+
by pair NAME. `config pair add/list/remove` wired through run_config.
|
|
15
|
+
- **`google-sync --pair / --all` (MTF-02/03, D-02)** — pair mode resolves
|
|
16
|
+
album+frame from the store; `--all` runs every pair sorted with ONE
|
|
17
|
+
budget instance (account-wide cap), records per-pair outcomes, never
|
|
18
|
+
aborts the loop, exit 1 if any failed. Container journey
|
|
19
|
+
(`test-multipair-journey-container.sh`) proves registry + sharding
|
|
20
|
+
contract + report shape + single budget file.
|
|
21
|
+
- **`pushframe/schedule.py` (TMR-01..03)** — pure unit renderers
|
|
22
|
+
(Type=oneshot, **Restart=no** — the next tick is the retry,
|
|
23
|
+
RandomizedDelaySec, Persistent, append: per-job log with the
|
|
24
|
+
phase-23.5-era systemd ≥240 assumption documented), every→OnCalendar
|
|
25
|
+
mapping, add/list/remove with monkeypatchable systemctl, preflight
|
|
26
|
+
naming the user-session requirement + `loginctl enable-linger` remedy
|
|
27
|
+
(documented, never executed).
|
|
28
|
+
- **`--scheduled` SAFE-02 flip (TMR-03)** — a timed run over the threshold
|
|
29
|
+
SKIPS (exit 0) and logs the reason instead of proceeding silently or
|
|
30
|
+
failing the unit.
|
|
31
|
+
- **Sweep +4** — unknown pair, --all with zero pairs, no systemd session,
|
|
32
|
+
duplicate pair: all named + remedy + traceback-free (9 total cases).
|
|
33
|
+
|
|
34
|
+
## Roadmap §25 criteria check
|
|
35
|
+
|
|
36
|
+
| # | Criterion | Evidence |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| 1 | Two pairs in one `--all`, per-pair state shards, shared budget, exact reports (container + fake-API) | offline: `test_all_runs_every_pair_with_one_shared_budget` (identity of the shared instance asserted) + `test_all_continues_after_first_pair_failure_and_exits_1`; container: multipair journey (registry, sharding contract probe, single budget file, report shape) |
|
|
39
|
+
| 2 | `schedule add/list/remove` round-trip units; scheduled run prompt-free + per-job log | unit-content tests (oneshot/Restart=no/log), round-trip tests with monkeypatched systemctl, prompt-free inherited from phase 24 (SessionExpiredError, no prompts) — container systemd journey listed as follow-up (below) |
|
|
40
|
+
| 3 | SAFE-02 skips-and-logs in scheduled runs | `test_scheduled_threshold_breach_skips_and_logs` |
|
|
41
|
+
|
|
42
|
+
## Broken-and-fixed along the way
|
|
43
|
+
|
|
44
|
+
- **Sharding validated by accident**: the pair-mode test tripped SAFE-01
|
|
45
|
+
drift against MY accumulated real-home state — proving the per-pair
|
|
46
|
+
shards isolate correctly AND that tests must shard under tmp (fixture
|
|
47
|
+
now monkeypatches `pair_state_paths`).
|
|
48
|
+
- **argparse vs config sub-flags**: `config pair add x --album A` died on
|
|
49
|
+
unknown-option parsing — the `config` tail is now pre-split before
|
|
50
|
+
argparse (root-level `--all`/`--pair`/`--scheduled` on google-sync are
|
|
51
|
+
real flags; only `config`'s tail is pre-split).
|
|
52
|
+
- **Sorted order is the contract**: the config writer sorts keys, so pair
|
|
53
|
+
execution order is alphabetical — documented instead of pretending
|
|
54
|
+
insertion order survives.
|
|
55
|
+
|
|
56
|
+
## Scope honesty
|
|
57
|
+
|
|
58
|
+
- The systemd-available container journey (criterion 2's "container with
|
|
59
|
+
systemd available") is listed as follow-up: unit CONTENT is
|
|
60
|
+
content-asserted offline, and the round-trip is tested with a
|
|
61
|
+
monkeypatched systemctl; a real `systemctl --user` container run needs a
|
|
62
|
+
privileged image — deferred deliberately.
|
|
63
|
+
- `_exec_start_for` quotes for sh but does not shell-escape arbitrary
|
|
64
|
+
input — pair/album/frame names come from the operator's own config.
|
|
65
|
+
|
|
66
|
+
## Files
|
|
67
|
+
|
|
68
|
+
pairs.py (new) · schedule.py (new) · gsync.py · cli.py · config_store.py ·
|
|
69
|
+
tests/test_pairs.py, test_gsync_all.py, test_schedule.py,
|
|
70
|
+
test_preflight_sweep.py · scripts/test-multipair-journey-container.sh ·
|
|
71
|
+
docs/CLI.md · CHANGELOG.md
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Phase 25: Multi-frame Mappings & systemd User Scheduling — Context
|
|
2
|
+
|
|
3
|
+
**Gathered:** 2026-09-29
|
|
4
|
+
**Status:** Ready for planning
|
|
5
|
+
**Provenance:** REQUIREMENTS MTF-01..03 / TMR-01..03 verbatim, ROADMAP §25
|
|
6
|
+
criteria, code probe of the state-sharding points, operator decisions
|
|
7
|
+
locked at the discuss checkpoint (2026-09-29).
|
|
8
|
+
|
|
9
|
+
## Operator decisions (locked)
|
|
10
|
+
|
|
11
|
+
- **D-01 Pair schema**: named dictionary in config.json's (already
|
|
12
|
+
reserved) `pairs` key —
|
|
13
|
+
`{"cadre-venus": {"album": "Cadre", "frame": "Cadre de Fabrice"}}`.
|
|
14
|
+
Stable operator-chosen names; `--pair <name>` and timers reference them;
|
|
15
|
+
`--all` executes in dict order.
|
|
16
|
+
- **D-02 Isolation in `--all`**: a failing pair is recorded in the global
|
|
17
|
+
report and the remaining pairs still run. Exit 1 if ANY pair failed,
|
|
18
|
+
0 only if all OK. (MTF-03's "skipped pair never blocks the others".)
|
|
19
|
+
- Carried from phase 24 discuss: scheduled runs must be prompt-free —
|
|
20
|
+
token sessions (never passwords), preflights guard, SAFE-02 **skips and
|
|
21
|
+
logs** on threshold breach instead of failing the unit.
|
|
22
|
+
|
|
23
|
+
## Code-probe facts (where state lives today)
|
|
24
|
+
|
|
25
|
+
- Single-pair state today: manifest defaults to
|
|
26
|
+
`~/.config/pushframe/google-manifest.json` (`GoogleManifest.load(None)`)
|
|
27
|
+
and cache defaults to `default_cache_dir(album.album_id)` — BOTH are
|
|
28
|
+
sharding points already parameterized (`manifest_path=`, `cache_dir=`
|
|
29
|
+
kwargs exist on `run_google_sync`). Multi-pair = compute per-pair
|
|
30
|
+
default paths from the pair name; no engine change needed.
|
|
31
|
+
- `config_store` already whitelists `pairs` and defaults it to `{}`.
|
|
32
|
+
- Write budget: built per account email in `_build_write_budget`; the
|
|
33
|
+
`--all` runner passes ONE budget instance across pairs → account-wide
|
|
34
|
+
sharing falls out naturally (MTF-03), persisted budget state on disk is
|
|
35
|
+
already per-email.
|
|
36
|
+
- Phase 24 gave the prompt-free session (`establish_session` non-TTY
|
|
37
|
+
contract) and the trip guards (first-occurrence stop, 401 body capture)
|
|
38
|
+
— scheduled runs inherit them.
|
|
39
|
+
- `pushframe schedule` does not exist yet; units would live in
|
|
40
|
+
`~/.config/systemd/user/` named `pushframe-<job>.service/.timer`; logs
|
|
41
|
+
at `~/.local/state/pushframe/<job>.log` (TMR-03).
|
|
42
|
+
- Venus constraint (2026-09): the account trips on write volume; timers
|
|
43
|
+
MUST default to conservative pacing (`--batch-size` forwarded from the
|
|
44
|
+
pair config, `OnCalendar` spaced, `RandomizedDelaySec` to de-sync hosts).
|
|
45
|
+
|
|
46
|
+
## Failure modes to add to the PRF sweep inventory
|
|
47
|
+
|
|
48
|
+
- `--pair <name>` unknown → named error listing installed pair names
|
|
49
|
+
- `--all` with zero pairs configured → named error with the
|
|
50
|
+
`pushframe config` remedy (never a silent success)
|
|
51
|
+
- `schedule add` without a systemd user session (`loginctl` absent) →
|
|
52
|
+
named error + linger remedy
|
|
53
|
+
- duplicate pair name on create → named error (never silently overwrite)
|
|
54
|
+
|
|
55
|
+
## Constraints & invariants
|
|
56
|
+
|
|
57
|
+
- SAFE-01..04 apply PER PAIR (threshold is computed against that pair's
|
|
58
|
+
target frame), and the shared budget caps the whole `--all` run.
|
|
59
|
+
- Timers install as user units; no root anywhere; `loginctl enable-linger`
|
|
60
|
+
documented for headless hosts (venus) — never executed by the tool.
|
|
61
|
+
- Dry-run remains the default for google-sync; scheduled jobs pass
|
|
62
|
+
`--apply --yes` explicitly in the generated unit's ExecStart.
|
|
63
|
+
- The `TripDetectedError` first-occurrence stop and the 60-min guidance
|
|
64
|
+
apply to scheduled runs: the unit must NOT restart on failure
|
|
65
|
+
(`Restart=no`), the next timer tick is the retry.
|