anthodev/lapin-futepublic Fork
⑂ develop
Commits
⬇ Clone ▾
git clone https://git.rickub.com/anthodev/lapin-fute.git
git clone ssh://git@rickub.com/anthodev/lapin-fute.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP

[feature] Persist and synchronize favorites #6

Closed@github-anthodev opened this issue Aug 29, 2026
  • @github-anthodev opened this issue · Aug 29, 2026

    🔢 Impact Version (SemVer)

    MINOR


    🌿 Suggested Branch

    feature/sync-favorites/companion/6


    📝 Description

    Persist one complete versioned phone configuration—favorites plus the personal PRIM key—in PebbleKit JS, expose only non-secret state to the page/watch, cache last normalized results, and synchronize favorite metadata atomically. This ticket owns localStorage schema/migration/removal, ApiKeyUpdate, credential status, event plumbing, compact AppMessage sequencing, restart recovery, and secret-safe logs.


    ✅ Expected Behavior

    One atomic phone-local configuration survives restarts. Options can keep/replace/remove the key without receiving its stored value. The watch receives favorites plus non-secret key status, never the key. Interrupted/repeated synchronization preserves the prior complete list, while phone-local last results support immediate event-driven display without background activity.


    📋 Acceptance Criteria

    • Store one versioned JSON object under exact UUID-scoped localStorage key lapinFuteConfig, containing favorites, primApiKey, and non-secret keyStatus; document plaintext storage and survival across uninstall/reinstall.
    • Open options via a non-server-visible fragment containing favorites plus hasKey only. Apply ApiKeyUpdate atomically: KEEP retains key/status, REPLACE overwrites key and sets CONFIGURED, REMOVE erases key and sets MISSING; invalid/cancelled responses preserve prior bytes.
    • Maintain non-secret phone/watch keyStatus (MISSING | CONFIGURED | INVALID). Missing maps to API_KEY_REQUIRED; 401/403 marks INVALID until replace/remove. MISSING/INVALID suppress every departure XHR, and the raw key never reaches watch.
    • Centralize all backend departure XHR in one helper that reads the key only at send time, sets the Authorization header, strips it from errors, and records only credential-free request/cache timing counters.
    • Store one compact last complete result per current favorite under lapinFuteResults, never the key; prune deleted/unknown favorites and enforce the frozen favorite/result size bounds. On launch/selection, send cached data immediately with original age before an optional event fetch.
    • Encode only favorites, keyStatus, and normalized result data within the frozen AppMessage budgets. Chain multi-message updates on callbacks and atomically commit only the complete matching sequence.
    • Resume/restart interrupted synchronization without mixing versions; migrate explicitly supported schemas and reject unknown versions without modifying valid local/watch data.
    • Derive/document maximum favorite count from the tightest measured AppMessage, watch heap/storage, localStorage, and encoded configuration-fragment round-trip limit; keep that many labels browsable offline.
    • Favorites/key configuration survive companion restart; favorites/key status survive watch restart; add/reorder/rename/delete yield the same ordered list on both sides.
    • Interruption after any message preserves the previous watch configuration; repeated delivery is idempotent; invalid/unsupported input never replaces valid data.
    • Prove app exit stops PebbleKit JS and produces no XHR/AppMessage/background activity for a 10-minute observed idle window.
    • Record storage/migration/remove/invalid-key proof, secret scan/log audit, result-cache launch behavior, restart/edit parity, every-position interruption, AppMessage bytes/counts, and zero-background evidence in a resolution comment.

    🔗 Affected Endpoints/Routes

    • No backend route is added; implement the foundation-frozen departure Authorization/error client against recorded HTTP fixtures. The composed live route remains gated by both this issue and [feature] Normalize live PRIM departures (https://github.com/Anthodev/lapin-fute/issues/4) in the watch ticket.
    • Own the options open/save fragment, lapinFuteConfig/lapinFuteResults, centralized XHR helper, and secret-free PebbleKit JS ↔ watch AppMessage plane.

    🧪 Tests to Add

    Type Coverage
    PebbleKit JS unit tests Atomic config storage, `KEEP
    Secret boundary tests Exact lifecycle: new page memory → close fragment → PKJS handoff/request memory + lapinFuteConfig → Authorization → relay request/upstream memory → exact-host PRIM apikey; absent elsewhere
    PebbleKit JS/Alloy AppMessage tests keyStatus, atomic commit, incomplete/mismatched rejection, callback sequencing, idempotent redelivery, measured bytes/count proven on both emulators (emery and gabbro)
    Lifecycle/both-emulator smoke Restart/edit parity, every-position interruption, cached-result launch, disconnect browse, 10-minute exited-app zero activity on emery and gabbro

    ⚙️ Constraints

    • Reuse the frozen contracts/budgets from [feature] Establish the application foundation and contracts (https://github.com/Anthodev/lapin-fute/issues/2); amend them rather than creating a second protocol.
    • Accept the explicit product trade-off: personal key is plaintext in UUID-scoped PebbleKit JS localStorage; minimize exposure rather than adding ineffective client-side encryption.
    • The one-time configuration-close fragment may contain a replacement key; never GET/query/log it. The existing key never re-enters the hosted page.
    • Whole-list replacement is atomic; serviceId stays opaque; user/backend labels remain verbatim.
    • No key in favorites/results/AppMessage/watch storage, no server-side credential/user preference storage, and no background scheduler.

    🔀 Dependencies

    • Blocked by [feature] Build the companion configuration page (https://github.com/Anthodev/lapin-fute/issues/5).
    • Reuse the foundation contracts from [feature] Establish the application foundation and contracts (https://github.com/Anthodev/lapin-fute/issues/2).

    ⚠️ Impact on Existing Behavior

    Replaces the one-favorite round-trip fixture from [feature] Establish the application foundation and contracts (https://github.com/Anthodev/lapin-fute/issues/2) with durable whole-list persistence and synchronization, and gives [feature] Build the companion configuration page (https://github.com/Anthodev/lapin-fute/issues/5) its save semantics.


    🔍 Technical Context

    Required references

Sign in to comment.