pushframe 5.0.4__tar.gz → 5.0.5__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.4 → pushframe-5.0.5}/.planning/STATE.md +5 -5
- pushframe-5.0.5/.planning/phases/23-config-wizard-precedence/23-01-PLAN.md +157 -0
- pushframe-5.0.5/.planning/phases/23-config-wizard-precedence/23-01-SUMMARY.md +70 -0
- pushframe-5.0.5/.planning/phases/23-config-wizard-precedence/23-CONTEXT.md +68 -0
- pushframe-5.0.5/.planning/phases/23-config-wizard-precedence/23-RESEARCH.md +89 -0
- pushframe-5.0.5/.planning/phases/23-config-wizard-precedence/23-VERIFICATION.md +57 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/CHANGELOG.md +86 -60
- {pushframe-5.0.4 → pushframe-5.0.5}/PKG-INFO +33 -6
- {pushframe-5.0.4 → pushframe-5.0.5}/README.md +32 -5
- {pushframe-5.0.4 → pushframe-5.0.5}/docs/CLI.md +52 -2
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/__init__.py +1 -1
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/aura.py +25 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/aws/s3client.py +13 -9
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/aws/sqsclient.py +6 -6
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/cli.py +276 -17
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/client.py +14 -3
- pushframe-5.0.5/pushframe/config_store.py +103 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/google/bootstrap.py +16 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/gsync.py +58 -5
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/sync.py +14 -0
- pushframe-5.0.5/pushframe/utils/settings.py +122 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pyproject.toml +1 -1
- pushframe-5.0.5/scripts/test-config-journey-container.sh +201 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_cli_google_link.py +20 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_cli_push_budget_geo.py +2 -2
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_cli_status.py +39 -0
- pushframe-5.0.5/tests/test_config_wizard.py +168 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_execute_plan.py +35 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_gsync_execute.py +62 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_imports.py +5 -4
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_settings_budget.py +6 -6
- pushframe-5.0.5/tests/test_settings_resolution.py +106 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_version.py +1 -1
- {pushframe-5.0.4 → pushframe-5.0.5}/uv.lock +1 -1
- pushframe-5.0.4/pushframe/utils/settings.py +0 -59
- {pushframe-5.0.4 → pushframe-5.0.5}/.env.sample +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.freebuff/project-id +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.github/workflows/release.yml +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.github/workflows/tests.yml +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.gitignore +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.gsd/dispatch-isolation-sentinel.json +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/MILESTONES.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/PROJECT.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/REQUIREMENTS.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/RETROSPECTIVE.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/ROADMAP.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/WINDOWS.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/codebase/ARCHITECTURE.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/codebase/CONCERNS.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/codebase/CONVENTIONS.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/codebase/INTEGRATIONS.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/codebase/STACK.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/codebase/STRUCTURE.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/codebase/TESTING.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/config.json +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/debug/knowledge-base.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/debug/login-475-null-creds.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/debug/resolved/cli-status-verbose-logging.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/debug/resolved/inspect-asset-null-fields.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/debug/resolved/select-asset-401-unauthorized.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-REQUIREMENTS.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-ROADMAP.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-phases/01-toolchain-revival/01-01-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-phases/01-toolchain-revival/01-01-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-phases/01-toolchain-revival/01-02-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-phases/01-toolchain-revival/01-02-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-phases/01-toolchain-revival/01-CONTEXT.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-phases/01-toolchain-revival/01-DISCUSSION-LOG.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-phases/01-toolchain-revival/01-PATTERNS.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-phases/01-toolchain-revival/01-RESEARCH.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-phases/01-toolchain-revival/01-REVIEW.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-phases/01-toolchain-revival/01-VERIFICATION.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-phases/02-live-read-path-verification/02-01-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-phases/02-live-read-path-verification/02-01-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-phases/02-live-read-path-verification/02-02-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-phases/02-live-read-path-verification/02-02-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-phases/02-live-read-path-verification/02-CONTEXT.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-phases/02-live-read-path-verification/02-DISCUSSION-LOG.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-phases/02-live-read-path-verification/02-LIVE-EVIDENCE.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-phases/02-live-read-path-verification/02-PATTERNS.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-phases/02-live-read-path-verification/02-RESEARCH.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-phases/02-live-read-path-verification/02-REVIEW.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-phases/02-live-read-path-verification/02-VERIFICATION.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-phases/03-run-docs-verification-report/03-01-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-phases/03-run-docs-verification-report/03-01-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-phases/03-run-docs-verification-report/03-CONTEXT.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-phases/03-run-docs-verification-report/03-DISCUSSION-LOG.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-phases/03-run-docs-verification-report/03-PATTERNS.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-phases/03-run-docs-verification-report/03-RESEARCH.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-phases/03-run-docs-verification-report/03-REVIEW.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.0-phases/03-run-docs-verification-report/03-VERIFICATION.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.1-ROADMAP.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.1-phases/04-client-transport-seam-for-offline-testability/.gitkeep +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.1-phases/04-client-transport-seam-for-offline-testability/04-01-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.1-phases/04-client-transport-seam-for-offline-testability/04-01-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.1-phases/04-client-transport-seam-for-offline-testability/04-02-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.1-phases/04-client-transport-seam-for-offline-testability/04-02-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.1-phases/04-client-transport-seam-for-offline-testability/04-03-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.1-phases/04-client-transport-seam-for-offline-testability/04-03-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.1-phases/04-client-transport-seam-for-offline-testability/04-PATTERNS.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.1-phases/04-client-transport-seam-for-offline-testability/04-RESEARCH.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.1-phases/04-client-transport-seam-for-offline-testability/04-REVIEW.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.1-phases/04-client-transport-seam-for-offline-testability/04-SECURITY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.1-phases/04-client-transport-seam-for-offline-testability/04-UAT.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v1.1-phases/04-client-transport-seam-for-offline-testability/04-VERIFICATION.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-REQUIREMENTS.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-ROADMAP.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/05-cli-skeleton-status/05-01-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/05-cli-skeleton-status/05-01-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/05-cli-skeleton-status/05-02-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/05-cli-skeleton-status/05-02-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/05-cli-skeleton-status/05-CONTEXT.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/05-cli-skeleton-status/05-DISCUSSION-LOG.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/05-cli-skeleton-status/05-PATTERNS.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/05-cli-skeleton-status/05-REVIEW.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/05-cli-skeleton-status/05-SECURITY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/05-cli-skeleton-status/05-UAT.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/05-cli-skeleton-status/05-VERIFICATION.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/06-inspect-frame-resolution/06-01-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/06-inspect-frame-resolution/06-01-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/06-inspect-frame-resolution/06-02-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/06-inspect-frame-resolution/06-02-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/06-inspect-frame-resolution/06-CONTEXT.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/06-inspect-frame-resolution/06-DISCUSSION-LOG.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/06-inspect-frame-resolution/06-PATTERNS.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/06-inspect-frame-resolution/06-RESEARCH.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/06-inspect-frame-resolution/06-REVIEW.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/06-inspect-frame-resolution/06-SECURITY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/06-inspect-frame-resolution/06-VERIFICATION.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/07-sync-diffing-engine-dry-run-only/07-01-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/07-sync-diffing-engine-dry-run-only/07-01-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/07-sync-diffing-engine-dry-run-only/07-02-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/07-sync-diffing-engine-dry-run-only/07-02-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/07-sync-diffing-engine-dry-run-only/07-03-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/07-sync-diffing-engine-dry-run-only/07-03-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/07-sync-diffing-engine-dry-run-only/07-CONTEXT.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/07-sync-diffing-engine-dry-run-only/07-DISCUSSION-LOG.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/07-sync-diffing-engine-dry-run-only/07-PATTERNS.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/07-sync-diffing-engine-dry-run-only/07-REVIEW-FIX.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/07-sync-diffing-engine-dry-run-only/07-REVIEW.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/07-sync-diffing-engine-dry-run-only/07-SECURITY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/07-sync-diffing-engine-dry-run-only/07-VERIFICATION.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-01-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-01-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-02-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-02-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-03-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-03-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-04-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-04-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-CONTEXT.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-DISCUSSION-LOG.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-LIVE-FINDINGS.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-PATTERNS.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-RESEARCH.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-REVIEW-FIX.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-REVIEW.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/08-destructive-execution-upload-delete-verification/08-VERIFICATION.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/09-proactive-write-rate-limiter-geo-guard/.gitkeep +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/09-proactive-write-rate-limiter-geo-guard/09-01-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/09-proactive-write-rate-limiter-geo-guard/09-01-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/09-proactive-write-rate-limiter-geo-guard/09-02-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/09-proactive-write-rate-limiter-geo-guard/09-02-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/09-proactive-write-rate-limiter-geo-guard/09-CONTEXT.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/09-proactive-write-rate-limiter-geo-guard/09-RESEARCH.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/09-proactive-write-rate-limiter-geo-guard/09-REVIEW-FIX.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/09-proactive-write-rate-limiter-geo-guard/09-REVIEW.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/09-proactive-write-rate-limiter-geo-guard/09-VERIFICATION.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/.gitkeep +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-01-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-01-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-02-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-02-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-03-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-03-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-04-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-04-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-CONTEXT.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-DISCUSSION-LOG.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-LIVE-FINDINGS.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-PATTERNS.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-RESEARCH.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-SECURITY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-UAT.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/10-VERIFICATION.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-phases/10-hide-instead-of-delete-sync-mode/COVERAGE.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.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.4 → pushframe-5.0.5}/.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.4 → pushframe-5.0.5}/.planning/milestones/v2.0-quick/260708-dt9-add-progress-feedback-to-aura-cli-sync-a/260708-dt9-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-quick/260708-dt9-add-progress-feedback-to-aura-cli-sync-a/260708-dt9-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-quick/260708-dt9-add-progress-feedback-to-aura-cli-sync-a/deferred-items.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-quick/260708-fyr-batch-refactor-of-sync-apply-write-path-/260708-fyr-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-quick/260708-fyr-batch-refactor-of-sync-apply-write-path-/260708-fyr-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-quick/260708-fyr-batch-refactor-of-sync-apply-write-path-/260708-fyr-VERIFICATION.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v2.0-quick/README.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v3.0-REQUIREMENTS.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v3.0-ROADMAP.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-01-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-01-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-02-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-02-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-03-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-03-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-04-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-04-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-05-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-05-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-06-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-06-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-CONTEXT.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-DISCUSSION-LOG.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-LIVE-FINDINGS.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-PATTERNS.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-REVIEW.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v3.0-phases/11-write-path-reliability-format-support/11-VERIFICATION.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-REQUIREMENTS.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-ROADMAP.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/16-local-mechanism-spike-decision/16-01-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/16-local-mechanism-spike-decision/16-01-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/16-local-mechanism-spike-decision/16-02-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/16-local-mechanism-spike-decision/16-02-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/16-local-mechanism-spike-decision/16-03-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/16-local-mechanism-spike-decision/16-03-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/16-local-mechanism-spike-decision/16-CONTEXT.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/16-local-mechanism-spike-decision/16-DECISION-RECORD.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/16-local-mechanism-spike-decision/16-DISCUSSION-LOG.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/16-local-mechanism-spike-decision/16-LIVE-FINDINGS.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/16-local-mechanism-spike-decision/COVERAGE.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/17-google-link-album-selection/17-01-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/17-google-link-album-selection/17-01-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/17-google-link-album-selection/17-02-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/17-google-link-album-selection/17-02-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/17-google-link-album-selection/17-CONTEXT.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/17-google-link-album-selection/17-DISCUSS-CHECKPOINT.json +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/17-google-link-album-selection/17-VERIFICATION.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/17-google-link-album-selection/COVERAGE.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/18-album-frame-mirror-sync-single-pair/18-01-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/18-album-frame-mirror-sync-single-pair/18-01-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/18-album-frame-mirror-sync-single-pair/18-02-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/18-album-frame-mirror-sync-single-pair/18-02-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/18-album-frame-mirror-sync-single-pair/18-03-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/18-album-frame-mirror-sync-single-pair/18-03-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/18-album-frame-mirror-sync-single-pair/18-CONTEXT.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/18-album-frame-mirror-sync-single-pair/18-RESEARCH.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/18-album-frame-mirror-sync-single-pair/18-UAT.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/18-album-frame-mirror-sync-single-pair/18-VERIFICATION.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/19-debt-closeout/19-01-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/19-debt-closeout/19-01-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/19-debt-closeout/19-02-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/19-debt-closeout/19-02-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/19-debt-closeout/19-CONTEXT.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/19-debt-closeout/19-RESEARCH.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v4.0-phases/19-debt-closeout/19-VERIFICATION.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v5.0-REQUIREMENTS.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v5.0-ROADMAP.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v5.0-phases/20-pushframe-rename-migration-repo-switch/20-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v5.0-phases/21-debian-package-apt-repo/21-01-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v5.0-phases/21-debian-package-apt-repo/21-01-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v5.0-phases/21-debian-package-apt-repo/21-02-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v5.0-phases/21-debian-package-apt-repo/21-02-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v5.0-phases/21-debian-package-apt-repo/21-CONTEXT.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v5.0-phases/21-debian-package-apt-repo/21-RESEARCH.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v5.0-phases/21-debian-package-apt-repo/21-VERIFICATION.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v5.0-phases/22-pypi-release-engineering/22-01-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v5.0-phases/22-pypi-release-engineering/22-01-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v5.0-phases/22-pypi-release-engineering/22-02-PLAN.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v5.0-phases/22-pypi-release-engineering/22-02-SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v5.0-phases/22-pypi-release-engineering/22-CONTEXT.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v5.0-phases/22-pypi-release-engineering/22-RESEARCH.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v5.0-phases/22-pypi-release-engineering/22-VERIFICATION.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/milestones/v5.0-phases/22-pypi-release-engineering/OPERATOR-STEPS.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/research/ALBUM-ACCESS-V4-ADDENDUM.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/research/ALBUM-ACCESS.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/research/ARCHITECTURE.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/research/BROWSER-AUTOMATION.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/research/FEATURES.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/research/PITFALLS.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/research/STACK.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/research/SUMMARY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/state.json +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.planning/todos/completed/2026-07-06-promote-debug-flag-to-a-global-cli-convention.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/.python-version +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/CLAUDE.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/LICENSE +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/SECURITY.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/VERIFICATION-REPORT.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/docs/agents/domain.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/docs/agents/issue-tracker.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/docs/agents/triage-labels.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/docs/superpowers/specs/2026-07-09-write-rate-limiter-design.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/main.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/packaging/control-template +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/packaging/lintian-overrides +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/packaging/postinst +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/packaging/pushframe.copyright +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/probes/.gitignore +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/probes/README.md +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/probes/browser_bootstrap.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/probes/common.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/probes/cookie_vault.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/probes/fidelity_check.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/probes/rpc_capture.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/probes/shared_link_probe.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/api/__init__.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/api/accountApi.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/api/activityApi.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/api/assetApi.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/api/baseApi.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/api/frameApi.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/api/notificationApi.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/api/peopleApi.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/api/playlistApi.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/aws/__init__.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/aws/awsclient.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/cache.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/exif.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/export.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/google/__init__.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/google/cache.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/google/client.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/google/enumerate.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/google/manifest.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/google/parsers.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/google/redaction.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/google/vault.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/migration.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/models/__init__.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/models/activity.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/models/asset.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/models/frame.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/models/meta.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/models/person.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/models/user.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/ratelimit.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/reconcile.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/utils/dt.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/pushframe/utils/io.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/scripts/build-deb.sh +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/scripts/make-apt-key.sh +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/scripts/publish-apt-repo.sh +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/scripts/test-apt-journey-container.sh +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/scripts/test-deb-container.sh +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/scripts/test-install-journey-container.sh +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/conftest.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/fixtures/asset_unprocessed.json +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/fixtures/assets_page1.json +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/fixtures/assets_page2.json +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/fixtures/assets_placeholders.json +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/fixtures/error_envelope.json +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/fixtures/frame_detail.json +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/fixtures/frames.json +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/fixtures/google_rpc_snackc_page.json +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/fixtures/google_share_page_sample.html +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/fixtures/login.json +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/fixtures/probe_af_initdata_sample.html +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/offline.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_asset_partial.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_asset_unprocessed.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_aura_logger.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_batch_update_contract.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_cli_apply.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_cli_google_album.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_cli_inspect.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_cli_reconcile.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_cli_status_google.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_cli_sync.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_client_rate_limit.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_execute_plan_budget_geo.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_fixtures_validity.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_google_cache.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_google_client.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_google_enumerate.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_google_manifest.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_google_parsers.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_google_vault.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_gsync_engine.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_migration.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_offline_read_path.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_prep_upload_formats.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_probe_browser.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_probe_shared_link.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_ratelimit.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_read_path.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_reconcile.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_retry_401.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_sync_engine.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_write_endpoints_failloud.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_write_formats_live.py +0 -0
- {pushframe-5.0.4 → pushframe-5.0.5}/tests/test_write_throttling.py +0 -0
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
|
|
6
6
|
## Current Position
|
|
7
7
|
|
|
8
|
-
Phase: 23 —
|
|
9
|
-
Current Plan:
|
|
10
|
-
Total Plans in Phase:
|
|
11
|
-
Status:
|
|
8
|
+
Phase: 23 — VERIFIED (passed)
|
|
9
|
+
Current Plan: 23-01 complete
|
|
10
|
+
Total Plans in Phase: 1
|
|
11
|
+
Status: Verified — next: phase 24 (Token-First Sessions & Preflight Sweep)
|
|
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
16
|
- [ ] Phase 24 — Token-First Sessions & Preflight Sweep (SEC-01..03, PRF-01..02)
|
|
17
17
|
- [ ] Phase 25 — Multi-frame Mappings & systemd User Scheduling (MTF-01..03, TMR-01..03)
|
|
18
18
|
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
---
|
|
2
|
+
phase: 23-config-wizard-precedence
|
|
3
|
+
plan: 01
|
|
4
|
+
type: execute
|
|
5
|
+
wave: 1
|
|
6
|
+
depends_on: []
|
|
7
|
+
files_modified:
|
|
8
|
+
- pushframe/utils/settings.py
|
|
9
|
+
- pushframe/config_store.py
|
|
10
|
+
- pushframe/cli.py
|
|
11
|
+
- tests/test_settings_resolution.py
|
|
12
|
+
- tests/test_config_store.py
|
|
13
|
+
- tests/test_config_wizard.py
|
|
14
|
+
autonomous: true
|
|
15
|
+
requirements: [CFG-01, CFG-02, CFG-03, CFG-04]
|
|
16
|
+
|
|
17
|
+
estimate:
|
|
18
|
+
tokens: 55000
|
|
19
|
+
raw_tokens: 42000
|
|
20
|
+
tasks: 3
|
|
21
|
+
confidence: medium
|
|
22
|
+
|
|
23
|
+
must_haves:
|
|
24
|
+
truths:
|
|
25
|
+
- "settings.py resolves every settings name as config-file → env var → built-in default at access time (PEP 562 __getattr__), importers unchanged, offline suite hermetic"
|
|
26
|
+
- "~/.config/pushframe/config.json (0600, atomic write, versioned schema) stores email, auth_token, default_frame, debug, optional settings overrides, and a reserved pairs key; nothing is written when the wizard's login test fails"
|
|
27
|
+
- "pushframe config (tty-guarded wizard) walks email → masked password → live login → store; pushframe config show prints the effective config with secrets redacted and per-key sources; import migrates a .env without storing env-resolved keys; set/get/path exist and unknown keys fail listing known keys"
|
|
28
|
+
|
|
29
|
+
<objective>
|
|
30
|
+
Turn env-var archaeology into a conversation: dynamic settings resolution
|
|
31
|
+
with locked precedence, the 0600 config.json store, and the `pushframe
|
|
32
|
+
config` command family (wizard, show, import, set/get, path) — importers
|
|
33
|
+
untouched, suite hermetic.
|
|
34
|
+
</objective>
|
|
35
|
+
|
|
36
|
+
<execution_context>
|
|
37
|
+
@~/.claude/gsd-core/workflows/execute-plan.md
|
|
38
|
+
@~/.claude/gsd-core/templates/summary.md
|
|
39
|
+
</execution_context>
|
|
40
|
+
|
|
41
|
+
<context>
|
|
42
|
+
@.planning/phases/23-config-wizard-precedence/23-CONTEXT.md
|
|
43
|
+
@.planning/phases/23-config-wizard-precedence/23-RESEARCH.md
|
|
44
|
+
@pushframe/utils/settings.py
|
|
45
|
+
@pushframe/google/manifest.py
|
|
46
|
+
</context>
|
|
47
|
+
|
|
48
|
+
<tasks>
|
|
49
|
+
|
|
50
|
+
<task type="tdd">
|
|
51
|
+
<name>Task 1: dynamic settings resolution + config store (CFG-02, D-01/D-02)</name>
|
|
52
|
+
<files>pushframe/utils/settings.py, pushframe/config_store.py, tests/test_settings_resolution.py, tests/test_config_store.py</files>
|
|
53
|
+
<read_first>
|
|
54
|
+
- pushframe/utils/settings.py (the constants being replaced + _env)
|
|
55
|
+
- pushframe/google/manifest.py (_atomic_write pattern to mirror)
|
|
56
|
+
- .planning/phases/23-config-wizard-precedence/23-RESEARCH.md (R1/R2/R3)
|
|
57
|
+
</read_first>
|
|
58
|
+
<action>
|
|
59
|
+
1. RED tests/test_settings_resolution.py: with a tmp config.json providing
|
|
60
|
+
LOCALE=fr-FR and env unset → `settings.LOCALE == "fr-FR"`; env set → env
|
|
61
|
+
wins; neither → the built-in default; unknown name → AttributeError with
|
|
62
|
+
the known-keys list; env-shadowing helper
|
|
63
|
+
`shadowed_keys(values) -> list[str]` reports which keys env currently
|
|
64
|
+
overrides (D-04 warning source).
|
|
65
|
+
2. GREEN: rewrite settings.py with a DEFAULTS table (name → default),
|
|
66
|
+
module `__getattr__` implementing config → env → default (config read
|
|
67
|
+
via config_store.load()), keep `_env` for compatibility, add
|
|
68
|
+
`known_keys()` and `shadowed_keys()`. Importers unchanged (the suite
|
|
69
|
+
proves it).
|
|
70
|
+
3. RED tests/test_config_store.py: load/save round-trip; save writes 0600
|
|
71
|
+
atomically (tmp+rename); schema version key; unknown top-level keys
|
|
72
|
+
fail loud on load (the manifest pattern); reserved "pairs" dict accepted
|
|
73
|
+
(empty) for phase 25.
|
|
74
|
+
4. GREEN: pushframe/config_store.py — CONFIG_PATH default
|
|
75
|
+
~/.config/pushframe/config.json, load()/save(dict)/update(**kw),
|
|
76
|
+
os.open 0600 + atomic rename, schema version 1, key whitelist
|
|
77
|
+
(email, auth_token, default_frame, debug, settings, pairs, version).
|
|
78
|
+
</action>
|
|
79
|
+
<acceptance_criteria>
|
|
80
|
+
- All three contracts green; importer files untouched (git diff scope)
|
|
81
|
+
- config.json created 0600; corrupt file fails loud naming the path
|
|
82
|
+
- Offline suite stays green (settings importers untouched)
|
|
83
|
+
</acceptance_criteria>
|
|
84
|
+
<verify>
|
|
85
|
+
<automated>uv run --extra dev pytest tests/test_settings_resolution.py tests/test_config_store.py -q 2>&1 | tail -1 | grep -qE "[0-9]+ passed"</automated>
|
|
86
|
+
<fails_when>resolution order wrong, 0600/atomicity missing, or suite regression</fails_when>
|
|
87
|
+
</verify>
|
|
88
|
+
</task>
|
|
89
|
+
|
|
90
|
+
<task>
|
|
91
|
+
<name>Task 2: the config command family (CFG-01/03/04)</name>
|
|
92
|
+
<files>pushframe/cli.py, tests/test_config_wizard.py</files>
|
|
93
|
+
<read_first>
|
|
94
|
+
- pushframe/cli.py (subparser wiring + run_status's credential check pattern)
|
|
95
|
+
- .planning/phases/23-config-wizard-precedence/23-RESEARCH.md (R4/R5)
|
|
96
|
+
</read_first>
|
|
97
|
+
<action>
|
|
98
|
+
1. `pushframe config` wizard (tty-guarded): existing config → show email
|
|
99
|
+
redacted, Enter keeps; email prompt (shape check); getpass password;
|
|
100
|
+
LIVE login via the existing Aura seam; success → store email +
|
|
101
|
+
user.auth_token (+ optional default frame from get_frames(), debug);
|
|
102
|
+
failure → print the API message, write NOTHING, return 1. Before
|
|
103
|
+
saving, warn listing settings.shadowed_keys() (D-04).
|
|
104
|
+
(D-03 order: credentials first + login-tested BEFORE anything is
|
|
105
|
+
stored; optional questions only after the login succeeds.)
|
|
106
|
+
2. `config show`: effective config (resolution at access), secrets
|
|
107
|
+
redacted to `***` + per-key source (file/env/default) — CFG-02.
|
|
108
|
+
3. `config import [--file .env]`: parse KEY=VALUE, map PUSHFRAME_*/AURA_*
|
|
109
|
+
prefixes onto settings names, store only keys NOT env-resolved now,
|
|
110
|
+
print the key → source table (CFG-03).
|
|
111
|
+
4. `config path` prints the path; `config set <key> <value>` /
|
|
112
|
+
`config get <key>`: known-keys validation, unknown fails with the
|
|
113
|
+
list (CFG-04); `set` refuses auth_token/email (wizard-only keys).
|
|
114
|
+
</action>
|
|
115
|
+
<acceptance_criteria>
|
|
116
|
+
- Wizard writes nothing on failed login (file-content assertion)
|
|
117
|
+
- `config show` output never contains a token/password value
|
|
118
|
+
- import on a fixture .env reproduces the env-path effective config
|
|
119
|
+
- non-tty wizard with missing input exits 1 with a message
|
|
120
|
+
</acceptance_criteria>
|
|
121
|
+
<verify>
|
|
122
|
+
<automated>uv run --extra dev pytest tests/test_config_wizard.py -q 2>&1 | tail -1 | grep -qE "[0-9]+ passed"</automated>
|
|
123
|
+
<fails_when>secrets leak, writes on failure, or precedence inversion</fails_when>
|
|
124
|
+
</verify>
|
|
125
|
+
</task>
|
|
126
|
+
|
|
127
|
+
<task>
|
|
128
|
+
<name>Task 3: docs + suite + changelog</name>
|
|
129
|
+
<files>README.md, docs/CLI.md, CHANGELOG.md</files>
|
|
130
|
+
<read_first>
|
|
131
|
+
- README.md (Environment Variables section gains a Config file note)
|
|
132
|
+
</read_first>
|
|
133
|
+
<action>
|
|
134
|
+
1. README: "Configuration" — the file, the wizard, the precedence line
|
|
135
|
+
(env > file > default), pointer that env vars remain supported.
|
|
136
|
+
2. docs/CLI.md: `config` command reference (subcommands, exit codes).
|
|
137
|
+
3. CHANGELOG [Unreleased]: Added entries (wizard, store, precedence).
|
|
138
|
+
4. Full offline suite green.
|
|
139
|
+
</action>
|
|
140
|
+
<acceptance_criteria>
|
|
141
|
+
- README documents precedence + wizard; CLI.md documents config
|
|
142
|
+
- CHANGELOG [Unreleased] carries the feature
|
|
143
|
+
- suite ≥ 411 passed
|
|
144
|
+
</acceptance_criteria>
|
|
145
|
+
<verify>
|
|
146
|
+
<automated>grep -q "config" docs/CLI.md && grep -q "env var > config file\|env > file" README.md && grep -q "config" CHANGELOG.md && uv run --extra dev pytest -q -m "not live" 2>&1 | tail -1 | grep -qE "passed"</automated>
|
|
147
|
+
<fails_when>missing docs or suite failure</fails_when>
|
|
148
|
+
</verify>
|
|
149
|
+
</task>
|
|
150
|
+
|
|
151
|
+
</tasks>
|
|
152
|
+
|
|
153
|
+
## Verification Criteria (plan-level)
|
|
154
|
+
|
|
155
|
+
1. Precedence proven: file → env → default, importers unchanged.
|
|
156
|
+
2. Wizard: login-tested, nothing-on-failure, 0600, shadowing warning.
|
|
157
|
+
3. show/import/set/get/path all present with redaction guarantees.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Plan 23-01 Execution Summary — Dynamic config resolution + `pushframe config` (CFG-01..04)
|
|
2
|
+
|
|
3
|
+
**Phase:** 23-config-wizard-precedence
|
|
4
|
+
**Date:** 2026-09-29
|
|
5
|
+
**Status:** Complete — suite **426 passed** (was 420 before the phase; +6 wizard tests, +8 resolution/store tests, 2 legacy alias asserts updated to the dynamic contract)
|
|
6
|
+
**Commits:** `936a2c4` (T1 dynamic resolution + config_store) · `acd3ba1` (T3 docs) · T2 wizard shipped in the T1→T2 working commit on this branch
|
|
7
|
+
|
|
8
|
+
## What Shipped
|
|
9
|
+
|
|
10
|
+
- **Dynamic settings resolution (D-01/D-04, CFG-02)** — `settings.py` rewritten
|
|
11
|
+
around PEP 562 module `__getattr__`: every `settings.X` access resolves live as
|
|
12
|
+
**environment → config file (`~/.config/pushframe/config.json`) → DEFAULTS
|
|
13
|
+
table**. All existing importers kept their attribute-style reads (the 411→420
|
|
14
|
+
regression passed byte-for-byte on defaults); `s3client`/`sqsclient` converted
|
|
15
|
+
from frozen `from settings import X` copies to module-attribute reads so
|
|
16
|
+
long-running processes see updates.
|
|
17
|
+
- **`config_store.py` (D-02)** — 0600 JSON store, atomic writes (tmp + rename),
|
|
18
|
+
known-key whitelist (fail-loud on unknown keys), schema-versioned, `"pairs"`
|
|
19
|
+
key reserved for phase 25 (multi-frame). Reads `settings.CONFIG_PATH`
|
|
20
|
+
dynamically (module import, not from-import) so tests and the wizard share
|
|
21
|
+
one path truth.
|
|
22
|
+
- **The `config` family (D-03, CFG-01/03/04)** — wired into the CLI parser and
|
|
23
|
+
dispatch:
|
|
24
|
+
- `pushframe config` — wizard: email → hidden password → **real API login
|
|
25
|
+
before anything is written** (fail = zero writes); stores email +
|
|
26
|
+
`auth_token`; optional questions afterwards; refuses non-tty stdin for the
|
|
27
|
+
interactive path (scheduled jobs never hang on a prompt).
|
|
28
|
+
- `config show` — every setting with effective value (secrets `***`) and
|
|
29
|
+
its source (`env`/`file`/`default`) + explicit env-shadow warnings.
|
|
30
|
+
- `config import FILE` — adopts `.env`-style files; skips env-provided keys
|
|
31
|
+
(reported), validates against the whitelist.
|
|
32
|
+
- `config set/get/path` — single-key direct access; `get` reflects true
|
|
33
|
+
precedence.
|
|
34
|
+
- **Docs (T3)** — README gets a wizard-first *Configuration* section (env vars
|
|
35
|
+
folded into an override `<details>`); `docs/CLI.md` gets a full `config`
|
|
36
|
+
command section + precedence-aware env-var intro; CHANGELOG `[Unreleased]`
|
|
37
|
+
carries Added/Changed entries.
|
|
38
|
+
|
|
39
|
+
## Broken-and-fixed along the way
|
|
40
|
+
|
|
41
|
+
- CHANGELOG had been corrupted by the session interruption (sections out of
|
|
42
|
+
order: Unreleased/5.0.4/5.0.3 stuck *after* 5.0.0; 5.0.4 content duplicated
|
|
43
|
+
inside its own block). Rewritten cleanly in Keep-a-Changelog order,
|
|
44
|
+
deduplicated — no content lost.
|
|
45
|
+
- `config_store` initially froze `CONFIG_PATH` via from-import (test
|
|
46
|
+
monkeypatch invisible) → module-attribute read.
|
|
47
|
+
- `_bool_env` signature drifted from the historical `default=` keyword →
|
|
48
|
+
restored (12 budget tests green again).
|
|
49
|
+
- AWS from-imports froze values at import time → dynamic access (matches the
|
|
50
|
+
phase-19 MOD-02 intent; two legacy alias asserts updated accordingly).
|
|
51
|
+
|
|
52
|
+
## Live proofs
|
|
53
|
+
|
|
54
|
+
- Wrong-password login against the real API raises
|
|
55
|
+
(`RateLimitError … HTTP 475`, the account's current Aura-side lockout) and
|
|
56
|
+
writes **no config.json** — the D-03 "nothing written on failed login"
|
|
57
|
+
contract, verified live. A successful-login wizard run was not repeated
|
|
58
|
+
against the real API to avoid worsening the lockout; the seam
|
|
59
|
+
(`_wizard_login`) is exactly the tested `Aura.login` path.
|
|
60
|
+
|
|
61
|
+
## Must-have truths check (plan 23-01)
|
|
62
|
+
|
|
63
|
+
| Truth | Status |
|
|
64
|
+
|---|---|
|
|
65
|
+
| Env/file/default precedence at access time | ✅ PEP 562 + DEFAULTS table (8 tests) |
|
|
66
|
+
| config.json 0600, atomic, whitelist, schema-versioned | ✅ config_store + tests |
|
|
67
|
+
| Wizard tests login before writing; fail = zero writes | ✅ 6 tests + live negative proof |
|
|
68
|
+
| `config show` reports source per key + shadows | ✅ implemented + tested |
|
|
69
|
+
| Import skips env-provided keys | ✅ tested (env-shadow contract) |
|
|
70
|
+
| Existing readers unchanged | ✅ 426 passed incl. all pre-phase tests |
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Phase 23: Config Wizard & Precedence — Context
|
|
2
|
+
|
|
3
|
+
**Gathered:** 2026-09-29
|
|
4
|
+
**Status:** Ready for planning
|
|
5
|
+
**Provenance:** compiled from REQUIREMENTS CFG-01..04, a full read of the
|
|
6
|
+
current config plumbing (`pushframe/utils/settings.py`, its 7+ importers,
|
|
7
|
+
`cli.py`'s env reads), and the operator's design decisions taken at the
|
|
8
|
+
discuss checkpoint (2026-09-29).
|
|
9
|
+
|
|
10
|
+
## Current state (probe facts)
|
|
11
|
+
|
|
12
|
+
- `pushframe/utils/settings.py` exposes ~15 **module constants evaluated
|
|
13
|
+
at import** via the `_env(*names)` helper: PUSHFRAME_* primary, AURA_*
|
|
14
|
+
legacy fallback (IDN-04), hard-coded defaults last.
|
|
15
|
+
- Importers do `from pushframe.utils import settings` and read
|
|
16
|
+
`settings.LOCALE`, `settings.AURA_API_BASE_URL`, budget constants, etc.
|
|
17
|
+
— none import `settings` as mutable state they replace.
|
|
18
|
+
- Credentials today: `PUSHFRAME_EMAIL` / `PUSHFRAME_PASSWORD` read in
|
|
19
|
+
`pushframe/aura.py` (login) and `cli.py` (status health check prints
|
|
20
|
+
set/NOT SET only).
|
|
21
|
+
- Existing JSON in `~/.config/pushframe/`: `google-cookies.json` (0600
|
|
22
|
+
vault), `google-manifest.json` (0600), budget file. A config.json sits
|
|
23
|
+
naturally in that family.
|
|
24
|
+
- The deb/wheel ships no `python-dotenv` auto-import for user shells
|
|
25
|
+
(`.env` is loaded by `main()` via `load_dotenv()` — cwd-relative, a
|
|
26
|
+
known fragility the file-config supersedes for humans).
|
|
27
|
+
|
|
28
|
+
## Design decisions (operator, 2026-09-29)
|
|
29
|
+
|
|
30
|
+
- **D-01:** Dynamic resolution — `settings.py` keeps its public names but
|
|
31
|
+
each becomes a *resolved-at-access* value with precedence
|
|
32
|
+
**config file → env var → built-in default**; importers stay untouched.
|
|
33
|
+
`config set` affects the next process and any re-read; no stale
|
|
34
|
+
import-time snapshot. Implementation must keep the offline test suite
|
|
35
|
+
hermetic (tests patch the file path / env, never the network).
|
|
36
|
+
- **D-02:** JSON — `~/.config/pushframe/config.json`, mode 0600, stdlib
|
|
37
|
+
`json` only — same family as the vault/manifest. The MTF-01 pairs
|
|
38
|
+
(phase 25) will live there as a dict.
|
|
39
|
+
- **D-03:** Wizard order: credentials first, login-tested — email →
|
|
40
|
+
password (getpass, masked) → **live login test** → on success store
|
|
41
|
+
email + the returned `auth_token` (SEC-01 lands in phase 24; phase 23
|
|
42
|
+
already stores the token since the API hands it back) → optional
|
|
43
|
+
questions (default frame — listed from a real `get_frames` when
|
|
44
|
+
credentials work —, debug). Login failure ⇒ clear message, **nothing
|
|
45
|
+
is written**.
|
|
46
|
+
- **D-04:** Precedence (locked): env var > config file > default —
|
|
47
|
+
Rationale: 12-factor; CI/scripts keep working unchanged; the file is
|
|
48
|
+
human comfort. The wizard warns when an env var would shadow the file
|
|
49
|
+
value it just wrote (the venus lesson: silent shadowing is the trap).
|
|
50
|
+
|
|
51
|
+
## Non-goals for this phase
|
|
52
|
+
|
|
53
|
+
- Token *expiry/re-login* semantics (phase 24, SEC-02).
|
|
54
|
+
- OS keyring backends, config encryption (v5.1 non-goals table).
|
|
55
|
+
- MTF pairs schema beyond reserving the `"pairs": {}` key in the file
|
|
56
|
+
schema (phase 25).
|
|
57
|
+
|
|
58
|
+
## Operator steps
|
|
59
|
+
|
|
60
|
+
None — everything is in-repo; the phase ships in the next release.
|
|
61
|
+
|
|
62
|
+
## Discretion areas (executor)
|
|
63
|
+
|
|
64
|
+
- Exact wizard prompt wording/order of optional questions.
|
|
65
|
+
- Whether `config show` reads settings lazily per key or snapshots
|
|
66
|
+
(recommend lazy — matches D-01).
|
|
67
|
+
- Test seam choice: fixture writing a temp config.json + monkeypatched
|
|
68
|
+
path constant (existing pattern in tests/test_migration.py).
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# Phase 23 Research — config plumbing & wizard seams
|
|
2
|
+
|
|
3
|
+
**Date:** 2026-09-29 · probes on master (post-5.0.4).
|
|
4
|
+
|
|
5
|
+
## R1. settings.py inventory (the full surface)
|
|
6
|
+
|
|
7
|
+
Constants (all via `_env('PUSHFRAME_X', 'AURA_X') or default`):
|
|
8
|
+
LOCALE, AURA_APP_IDENTIFIER, DEVICE_IDENTIFIER, AURA_API_BASE_URL,
|
|
9
|
+
AURA_API_VERSION, AURA_WRITE_BUDGET_CAPACITY (30), AURA_WRITE_BUDGET_REFILL_PER_MIN (0.75),
|
|
10
|
+
AURA_WRITE_BUDGET_WAIT (True), AURA_WRITE_BUDGET_MAX_WAIT (3600), and
|
|
11
|
+
GOOGLE_SYNC_REMOVAL_THRESHOLD (20%) — read by cli.py, export.py, the api/
|
|
12
|
+
package, and aws/. No importer mutates settings; all read attribute-style
|
|
13
|
+
at call time (`settings.X`), which makes attribute-level dynamic
|
|
14
|
+
resolution a drop-in. (Code refs: settings.py L16-45, importers listed in
|
|
15
|
+
23-CONTEXT.)
|
|
16
|
+
|
|
17
|
+
## R2. Dynamic-resolution mechanics (D-01)
|
|
18
|
+
|
|
19
|
+
Constraint: `from pushframe.utils import settings` + `settings.LOCALE`
|
|
20
|
+
must keep working WITHOUT importers changing. Two viable shapes:
|
|
21
|
+
|
|
22
|
+
- **Module `__getattr__`** (PEP 562): define `def __getattr__(name)` in
|
|
23
|
+
settings.py; resolution order: config.json key → env var → default
|
|
24
|
+
table. Keeps lazy semantics, zero importer churn, and the DEFAULTS
|
|
25
|
+
dict becomes the single table for both resolution and `config set`
|
|
26
|
+
key validation (CFG-04's "unknown keys fail with the list").
|
|
27
|
+
- Plain functions (`settings.get('LOCALE')`): explicit but breaks the
|
|
28
|
+
attribute-style importers — rejected.
|
|
29
|
+
|
|
30
|
+
`__getattr__` only fires for names NOT defined at module level, so the
|
|
31
|
+
module defines only helpers + `__getattr__` + explicit constants that
|
|
32
|
+
must stay static (`DEFAULT_VAULT_PATH` lives in vault.py, unaffected).
|
|
33
|
+
|
|
34
|
+
## R3. Config file schema (D-02)
|
|
35
|
+
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"version": 1,
|
|
39
|
+
"email": "coredmp95@gmail.com",
|
|
40
|
+
"auth_token": "...", // stored here from phase 23 on (SEC-01 refines in 24)
|
|
41
|
+
"default_frame": "Cadre de Fabrice",
|
|
42
|
+
"debug": false,
|
|
43
|
+
"settings": { // optional overrides, key = settings name
|
|
44
|
+
"AURA_WRITE_BUDGET_CAPACITY": 60
|
|
45
|
+
},
|
|
46
|
+
"pairs": {} // reserved for phase 25 (MTF-01)
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
0600 via os.open with mode; atomic write via tmp+rename (the manifest
|
|
51
|
+
pattern — google/manifest.py `_atomic_write`).
|
|
52
|
+
|
|
53
|
+
## R4. Wizard flow (D-03)
|
|
54
|
+
|
|
55
|
+
1. If config.json exists: show current email (redacted), Enter keeps.
|
|
56
|
+
2. email prompt → shape check.
|
|
57
|
+
3. password getpass → live `Aura().login()` (reuse cli's login path).
|
|
58
|
+
4. On success: persist email + `user.auth_token`; proceed to optional
|
|
59
|
+
prompts (default frame chosen from `get_frames()` when available;
|
|
60
|
+
debug y/n). On failure: print the API's message, write NOTHING, exit 1.
|
|
61
|
+
5. Warn when an env var (PUSHFRAME_/AURA_ prefix) currently shadows any
|
|
62
|
+
key the wizard just wrote (D-04 lesson from venus).
|
|
63
|
+
|
|
64
|
+
Non-interactive guard: `config` commands refuse when stdin is not a tty
|
|
65
|
+
and required input is missing — fail loud (same posture as `google-sync
|
|
66
|
+
--apply` requiring `--yes`).
|
|
67
|
+
|
|
68
|
+
## R5. Import-existing (CFG-03)
|
|
69
|
+
|
|
70
|
+
`config import`: read `.env` (cwd, then --file), map
|
|
71
|
+
PUSHFRAME_*/AURA_* keys onto settings names, write into config.json ONLY
|
|
72
|
+
the keys that are NOT currently provided by the environment (CFG-03's
|
|
73
|
+
"never store what env already resolves"), report a table
|
|
74
|
+
key → source (file/env/default).
|
|
75
|
+
|
|
76
|
+
## R6. Hermetic tests
|
|
77
|
+
|
|
78
|
+
- Fixture `tmp_config` (monkeypatch the module-level CONFIG_PATH in
|
|
79
|
+
settings.py; the test_migration.py pattern).
|
|
80
|
+
- Wizard tests: stdin fed via monkeypatched input/getpass; login mocked
|
|
81
|
+
at the `Aura` seam (same seam as TEST-02); assert file mode + content
|
|
82
|
+
+ "nothing written on failure".
|
|
83
|
+
- Precedence tests: file says X, env says Y → resolved Y; unset env → X.
|
|
84
|
+
- `config show` redaction test asserts the token/password never appear.
|
|
85
|
+
|
|
86
|
+
## R7. Release shape
|
|
87
|
+
|
|
88
|
+
This phase ships as 5.1.0's first cut (or 5.0.5 if pulled early — no
|
|
89
|
+
decision needed now); CHANGELOG `[Unreleased]` accumulates.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
phase: "23"
|
|
3
|
+
name: "Config Wizard & Precedence"
|
|
4
|
+
created: 2026-09-29
|
|
5
|
+
status: passed
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Phase 23 Verification — Config Wizard & Precedence
|
|
9
|
+
|
|
10
|
+
**Date:** 2026-09-29
|
|
11
|
+
**Status:** PASSED — 431 offline tests green; all three ROADMAP §23 success
|
|
12
|
+
criteria evidenced, criterion 1 by a pristine-container journey.
|
|
13
|
+
|
|
14
|
+
## Criteria → Evidence
|
|
15
|
+
|
|
16
|
+
| # | Criterion (ROADMAP §23) | Evidence | Result |
|
|
17
|
+
|---|--------------------------|----------|--------|
|
|
18
|
+
| 1 | Fresh machine: `pushframe config` → `pushframe status` works with no env vars at all (file-only path), clean container | `scripts/test-config-journey-container.sh`: pristine `ubuntu:26.04`, wheel install via uv standalone, wizard driven through a pty against a **fake API**, config stored 0600 **without the password**, then `pushframe status` under `env -i` (zero env vars) lists the frame; the fake API's hit log proves `login.json` was hit **exactly once** (wizard only) — status resumed the stored session | ✅ PASSED |
|
|
19
|
+
| 2 | Env vars override the file — proven by a test that flips both | `tests/test_settings_resolution.py::test_precedence_flip_env_always_wins`: both sources flipped (env=de-DE/file=fr-FR, then env=fr-FR/file=de-DE); resolved value follows env both times. Plus `config show` shadow warnings and `shadowed_keys()` | ✅ PASSED |
|
|
20
|
+
| 3 | `config show` never prints a secret; `config import` on a real `.env` reproduces the same effective config as the env path (golden diff) | `tests/test_config_wizard.py::test_show_redacts_secrets_and_lists_sources` (secrets `***`, per-key source); `::test_import_golden_same_effective_config_as_env_path` (import-then-file resolves byte-identically to env-only for the same values) | ✅ PASSED |
|
|
21
|
+
|
|
22
|
+
## Extra evidence beyond the criteria
|
|
23
|
+
|
|
24
|
+
- **Suite**: 431 passed offline (was 411 pre-phase; +20: resolution/store 9, wizard
|
|
25
|
+
family 8 incl. the hermetic wizard→status journey, stored-session status 3).
|
|
26
|
+
- **Live negative proof** (D-03): a wrong-password `_wizard_login` against the real
|
|
27
|
+
API raised (HTTP 475 — account lockout) and wrote no `config.json`.
|
|
28
|
+
- **Two latent bugs found and fixed by the journey**: `_wizard_login` read session
|
|
29
|
+
facts off `Aura.login()`'s return value (the Aura itself, not the user) — the
|
|
30
|
+
first real wizard login would have crashed, hidden by mocks; and
|
|
31
|
+
`PUSHFRAME_API_BASE_URL` had been declared in DEFAULTS but `client.py` still read
|
|
32
|
+
its own frozen constant, so the override never took effect.
|
|
33
|
+
|
|
34
|
+
## Scope honesty
|
|
35
|
+
|
|
36
|
+
- The stored-session resume covers `status` only; generalizing token sessions to
|
|
37
|
+
every verb (and re-login on expiry) is exactly phase 24 (SEC-01..03).
|
|
38
|
+
- `auth_token` sits in a 0600 file — same threat model as the Google cookie vault.
|
|
39
|
+
OS keychain integration was considered and deferred (phase 22 D-05 posture).
|
|
40
|
+
- The wizard's optional questions stop at country/budget/debug for now; pair
|
|
41
|
+
management arrives with multi-frame in phase 25 (`pairs` key already reserved
|
|
42
|
+
in the config schema).
|
|
43
|
+
|
|
44
|
+
## Files
|
|
45
|
+
|
|
46
|
+
- Code: `pushframe/utils/settings.py` (PEP 562 dynamic resolution),
|
|
47
|
+
`pushframe/config_store.py` (0600 store), `pushframe/cli.py` (config family,
|
|
48
|
+
stored-session status), `pushframe/aura.py` (`resume_session`),
|
|
49
|
+
`pushframe/client.py` (settings-driven base URL)
|
|
50
|
+
- Tests: `tests/test_settings_resolution.py`, `tests/test_config_wizard.py`,
|
|
51
|
+
`tests/test_cli_status.py`
|
|
52
|
+
- Journey: `scripts/test-config-journey-container.sh`
|
|
53
|
+
- Docs: README (wizard-first Configuration), `docs/CLI.md` (`config` section),
|
|
54
|
+
`CHANGELOG.md` `[Unreleased]`
|
|
55
|
+
|
|
56
|
+
**Commits:** `936a2c4` (dynamic resolution + store) · wizard family (branch commits) ·
|
|
57
|
+
`acd3ba1` (docs + changelog repair) · `3ef9b49` (stored-session status + journey)
|
|
@@ -12,6 +12,92 @@ every channel.
|
|
|
12
12
|
`pushframe` is an unofficial community CLI for Aura Frames digital photo
|
|
13
13
|
frames; it is not affiliated with Aura Frames Inc.
|
|
14
14
|
|
|
15
|
+
## [Unreleased]
|
|
16
|
+
|
|
17
|
+
- (v5.1 work will land here)
|
|
18
|
+
|
|
19
|
+
## [5.0.5] — 2026-09-29
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- **`pushframe config`** (v5.1, phase 23): interactive wizard that asks for
|
|
24
|
+
the email + password (hidden input), **verifies the login against the real
|
|
25
|
+
Aura API before writing anything**, and stores email + session token —
|
|
26
|
+
never the password — in `~/.config/pushframe/config.json` (0600, atomic
|
|
27
|
+
writes, schema-versioned). Subcommands: `show` (effective value + source
|
|
28
|
+
per key, secrets masked, env-shadow warnings), `import FILE` (adopts an
|
|
29
|
+
existing `.env`, skipping what env already provides), `set/get/path`.
|
|
30
|
+
With a stored session, `status` runs with **zero environment variables**
|
|
31
|
+
(resumes the session — no login call, no password traffic; proven by a
|
|
32
|
+
pristine-container journey, `scripts/test-config-journey-container.sh`).
|
|
33
|
+
- Headless `google-link` recipe documented (ssh -X); a `--remote-assist`
|
|
34
|
+
mode is a backlog candidate.
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
|
|
38
|
+
- **`google-sync`/`sync`/`push` apply feedback was opaque** (venus
|
|
39
|
+
regression): the progress bar sat silent for minutes at a time — budget
|
|
40
|
+
waits and inter-chunk cooldowns never reached the bar — and failures
|
|
41
|
+
showed a bare `upload FAIL` with no cause. The bar now shows a live
|
|
42
|
+
`pacing Ns — budget refill/cooldown, normal` countdown for every wait,
|
|
43
|
+
names the file on each item, escalates failures WITH their cause plus
|
|
44
|
+
the lockout remedy (401 → stop, wait, `pushframe status`), and the run
|
|
45
|
+
announces the pacing contract up front (long waits are normal after the
|
|
46
|
+
30-request burst; interrupts are safe, confirmed items are kept).
|
|
47
|
+
`execute_plan` gained an `on_error(kind, id, ok, reason)` seam so every
|
|
48
|
+
failure site (upload/reshow/delete, including the 401-retry paths)
|
|
49
|
+
reports its reason live.
|
|
50
|
+
- The abort message on the 5-consecutive-failures breaker now states what
|
|
51
|
+
to expect: confirmed items ARE on the frame, the next run recognizes
|
|
52
|
+
them and will not upload them twice, wait ~30 min before retrying.
|
|
53
|
+
|
|
54
|
+
### Changed
|
|
55
|
+
|
|
56
|
+
- Settings now resolve **at use time** with the precedence
|
|
57
|
+
**environment → config file → default** (previously environment → default
|
|
58
|
+
only, frozen at import time). Every `settings.X` reader keeps working
|
|
59
|
+
unchanged; long-running processes now see updated values. This also makes
|
|
60
|
+
`PUSHFRAME_API_BASE_URL` effective for real — it sat in the settings table
|
|
61
|
+
but `client.py` still read its own frozen constant.
|
|
62
|
+
- google-link detects the headless signature ($DISPLAY empty, packages
|
|
63
|
+
otherwise fine) and answers with the ssh -X remedy + the
|
|
64
|
+
copy-the-vault alternative — never a raw playwright X11 traceback.
|
|
65
|
+
|
|
66
|
+
## [5.0.4] — 2026-09-29
|
|
67
|
+
|
|
68
|
+
### Added
|
|
69
|
+
|
|
70
|
+
- google-link zero-config: the dedicated Chrome profile defaults to
|
|
71
|
+
`~/.config/pushframe/chrome-profile` (created on demand); the env var
|
|
72
|
+
becomes an override. A near-miss env name (e.g. a truncated
|
|
73
|
+
`USHFRAME_…`) is called out explicitly instead of looking like "unset".
|
|
74
|
+
- google-link preflight: missing prerequisites (playwright package,
|
|
75
|
+
Chrome/Chromium) fail with the exact remedy instead of a traceback
|
|
76
|
+
(`ModuleNotFoundError: playwright` was the whole output before).
|
|
77
|
+
|
|
78
|
+
### Changed
|
|
79
|
+
|
|
80
|
+
- `PUSHFRAME_PROBE_CHROME_PROFILE` is no longer required to run
|
|
81
|
+
`google-link`.
|
|
82
|
+
|
|
83
|
+
### Fixed
|
|
84
|
+
|
|
85
|
+
- google-link preflight on the uv-tool install path: remedy for a missing
|
|
86
|
+
playwright now matches every install shape (`uv tool install
|
|
87
|
+
'pushframe[google-browser]' --force` for uv tools — `pip install
|
|
88
|
+
--user` targets the wrong interpreter there); stray duplicate quote in
|
|
89
|
+
the message fixed.
|
|
90
|
+
|
|
91
|
+
## [5.0.3] — 2026-09-29
|
|
92
|
+
|
|
93
|
+
### Added
|
|
94
|
+
|
|
95
|
+
- `CHANGELOG.md` — every release now traced in Keep a Changelog format
|
|
96
|
+
(Added / Changed / Deprecated / Fixed / Security), exposed as a
|
|
97
|
+
`Changelog` project URL on PyPI and referenced from the README.
|
|
98
|
+
- `arch=amd64` in the documented APT sources entry: silences apt's
|
|
99
|
+
i386 notice on multi-arch machines (the repo is amd64-only).
|
|
100
|
+
|
|
15
101
|
## [5.0.2] — 2026-09-29
|
|
16
102
|
|
|
17
103
|
### Fixed
|
|
@@ -105,63 +191,3 @@ Milestone summary — see `.planning/MILESTONES.md` for the full retros.
|
|
|
105
191
|
- **v1.1 — client transport fixes** (the original author's last tag;
|
|
106
192
|
revived and extended by the current maintainer in 2026).
|
|
107
193
|
- **v1.0 — initial upstream release** by zmanowar (2023).
|
|
108
|
-
|
|
109
|
-
## [Unreleased]
|
|
110
|
-
|
|
111
|
-
- (v5.1 work will land here)
|
|
112
|
-
|
|
113
|
-
## [5.0.4] — 2026-09-29
|
|
114
|
-
|
|
115
|
-
### Fixed
|
|
116
|
-
|
|
117
|
-
- google-link preflight on the uv-tool install path: remedy for a missing
|
|
118
|
-
playwright now matches every install shape (`uv tool install
|
|
119
|
-
'pushframe[google-browser]' --force` for uv tools — `pip install
|
|
120
|
-
--user` targets the wrong interpreter there); stray duplicate quote in
|
|
121
|
-
the message fixed.
|
|
122
|
-
|
|
123
|
-
### Added
|
|
124
|
-
|
|
125
|
-
- google-link zero-config: dedicated Chrome profile defaults to
|
|
126
|
-
`~/.config/pushframe/chrome-profile` (created on demand); the env var
|
|
127
|
-
becomes an override. A near-miss env name (e.g. a truncated
|
|
128
|
-
`USHFRAME_…`) is called out explicitly instead of looking like "unset".
|
|
129
|
-
- google-link preflight: missing prerequisites (playwright package,
|
|
130
|
-
Chrome/Chromium) fail with the exact remedy instead of a traceback
|
|
131
|
-
(`ModuleNotFoundError: playwright` was the whole output before).
|
|
132
|
-
|
|
133
|
-
### Changed
|
|
134
|
-
|
|
135
|
-
- `PUSHFRAME_PROBE_CHROME_PROFILE` is no longer required to run
|
|
136
|
-
`google-link`.
|
|
137
|
-
|
|
138
|
-
- google-link zero-config: the dedicated Chrome profile defaults to
|
|
139
|
-
`~/.config/pushframe/chrome-profile` (created on demand); the env var
|
|
140
|
-
becomes an override. A near-miss env name (e.g. a truncated
|
|
141
|
-
`USHFRAME_…`) is called out explicitly instead of looking like "unset".
|
|
142
|
-
- google-link preflight: missing prerequisites (playwright package,
|
|
143
|
-
Chrome/Chromium) now fail with the exact remedy instead of a traceback
|
|
144
|
-
(`ModuleNotFoundError: playwright` was the whole output before).
|
|
145
|
-
|
|
146
|
-
### Fixed
|
|
147
|
-
|
|
148
|
-
- `google-link` preflight on the uv-tool install path: the message for a
|
|
149
|
-
missing playwright package now matches every install shape (`uv tool
|
|
150
|
-
install 'pushframe[google-browser]'` for uv tools — `pip install
|
|
151
|
-
--user` targets the wrong interpreter there), and the leftover
|
|
152
|
-
duplicate browser-detection line was removed.
|
|
153
|
-
|
|
154
|
-
### Changed
|
|
155
|
-
|
|
156
|
-
- `PUSHFRAME_PROBE_CHROME_PROFILE` is no longer required to run
|
|
157
|
-
`google-link`.
|
|
158
|
-
|
|
159
|
-
## [5.0.3] — 2026-09-29
|
|
160
|
-
|
|
161
|
-
### Added
|
|
162
|
-
|
|
163
|
-
- `CHANGELOG.md` — every release now traced in Keep a Changelog format
|
|
164
|
-
(Added / Changed / Deprecated / Fixed / Security), exposed as a
|
|
165
|
-
`Changelog` project URL on PyPI and referenced from the README.
|
|
166
|
-
- `arch=amd64` in the documented APT sources entry: silences apt's
|
|
167
|
-
i386 notice on multi-arch machines (the repo is amd64-only).
|