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] Establish the application foundation and contracts #2

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

    🔢 Impact Version (SemVer)

    MINOR


    🌿 Suggested Branch

    feature/application-contracts/foundation/2


    📝 Description

    Lapin Futé is a greenfield Pebble application. Establish one reproducible repository foundation for the single Alloy embedded-JavaScript watch app (src/embeddedjs on the RePebble Alloy XS runtime plus only the standard Pebble VM bootstrap C file), the PebbleKit JS companion, the TypeScript backend, and the mobile configuration page, then freeze the shared data, credential, performance, and radio-message contracts before work fans out.

    This ticket owns the Fedora-compatible toolchain (RePebble Alloy with Pebble SDK 4.33.1 and pebble-tool 5.0.40, firmware floor 4.32+), the emery+gabbro platform matrix with emery primary and the Pebble Time 2 physical release watch, bilingual resources, personal-PRIM-key boundaries, event-driven request contract, measurable performance/battery budgets, and first PebbleKit JS ↔ watch round trip.


    ✅ Expected Behavior

    A clean clone builds with one documented repository command. The primary emulator renders a recorded departure fixture, PebbleKit JS sends one complete favorite/result through the frozen AppMessage contract without sending the personal PRIM key, and the watch rejects unsupported schema versions without replacing its last complete state. Baseline resource/radio/battery measurements and pass/fail budgets exist before later tickets add work.


    📋 Acceptance Criteria

    • Create the Alloy embedded-JavaScript watch application — src/embeddedjs modules (main, controller, model, protocol, message-queue, localization, ui, contracts, all listed in src/embeddedjs/manifest.json) plus only the standard Alloy VM bootstrap C file — alongside the PebbleKit JS companion, TypeScript backend, and configuration-page packages in one repository; keep each component inside the ownership boundaries described below.
    • Pin Pebble SDK 4.33.1, pebble-tool 5.0.40, and the RePebble Alloy runtime with firmware floor 4.32+ building reproducibly on Fedora; record installation prerequisites and focused build/emulator commands in the repository.
    • Record the frozen platform matrix — emery (Pebble Time 2) as the primary emulator plus gabbro (Pebble Round 2) as the second emulator, with the Pebble Time 2 (emery) as the named physical release watch — that [feature] Deliver Pebble watch consultation (https://github.com/Anthodev/lapin-fute/issues/7), [feature] Harden failures and compatibility (https://github.com/Anthodev/lapin-fute/issues/8), and [feature] Ship Lapin Futé version 1 (https://github.com/Anthodev/lapin-fute/issues/9) must use for proof. Base the choice on verified Alloy platform support (emery and gabbro) and available hardware, not an assumed device.
    • Freeze Favorite, DepartureResult, ErrorResult, and phone-only ApiKeyUpdate as versioned contracts with every field/enum listed in Technical Context; add API_KEY_REQUIRED and API_KEY_INVALID to the stable error codes.
    • Freeze client→backend Authorization: Bearer <personal PRIM key> and backend→PRIM apikey forwarding, including exact scheme, bounded UTF-8 value length, mandatory redaction, fixed timeout/response-size limits, and exact-host rules.
    • Freeze the numeric AppMessage key map 0–24 with symbolic runtime aliases: packages/watch/package.json declares an explicit messageKeys object mapping each symbolic alias to its frozen number (the array-style default, whose keys start at 10000, is forbidden), runtime payloads send only the symbolic aliases, and the build proves alias→numeric resolution. Include only non-secret keyStatus (MISSING | CONFIGURED | INVALID), payload budgets for every platform, maximum favorite/result sizes, callback-chained sequencing, and atomic commit.
    • Freeze the event-driven request contract: app open, active-favorite selection after a 500 ms settling debounce, and manual Select only; 60-second fresh-cache reuse; no periodic timer, Wakeup API, Worker, timeline refresh, background request, or immediate retry loop.
    • Measure per-platform binary size (≤120 KiB), loaded image/RAM (≤56 KiB), XS arena headroom (≥25% free with settled drift ≤1 KiB), computed AppMessage buffers, messages/bytes per fixture refresh, and a physical-watch battery baseline; these shared Alloy/XS/Piu/Message gates replace the C-specific buffer/heap gates while the radio/battery gates stay unchanged. Freeze pass/fail budgets plus the exact repeatable 24-hour A/B scenario (firmware/app build, watch model, connectivity/backlight, idle time, and scripted event counts) used by hardening/release.
    • Freeze French/English authored-copy handling: the companion derives the normalized active-watch language and reuses the DISPLAY_NAME key contextually on CONFIG_BEGIN to deliver the exact fr/en resource set to the watch; French selects French and every other locale falls back to English; user-authored favorite labels and backend transport labels are never translated.
    • A clean clone builds with the documented repository command.
    • The primary (emery) emulator displays a recorded DepartureResult.
    • PebbleKit JS sends one Favorite and one DepartureResult through the frozen contract.
    • The watch rejects an unsupported schema version and preserves its last complete state.
    • Record links to build/emulator output, contract/redaction tests, resource/radio/battery baselines and budgets, message-round-trip proof, and unsupported-version proof in a resolution comment before closing this issue.

    🔗 Affected Endpoints/Routes

    • Freeze the departure-route credential/error interface for later backend fixtures; do not make a live PRIM call or store a personal key in this ticket.
    • No user-account, credential-vault, or secret-bearing AppMessage route is permitted.

    🧪 Tests to Add

    Type Coverage
    Contract tests Every shared field/enum, phone-only ApiKeyUpdate, credential/error semantics, numeric AppMessage key, schema rejection, request/favorite binding, maximum payload
    Secret-boundary tests Exact lifecycle: newly entered page memory → close fragment → PKJS handoff/request memory + localStorage → HTTPS Authorization → relay memory → PRIM header; absent elsewhere
    PebbleKit JS/Alloy tests Favorite/result encoding, keyStatus only, versioned round trip, last-state preservation, symbolic-alias → numeric 0–24 messageKeys package/build proof
    Performance baseline Clean Fedora build, per-platform Alloy/XS/Piu/Message gate measurements (binary, loaded image/RAM, XS arenas, computed AppMessage buffers), zero periodic/background event path, controlled physical-watch battery baseline

    ⚙️ Constraints

    • serviceId is opaque outside the backend; a configuration update replaces the whole favorite list atomically.
    • The only key lifecycle is: newly entered configuration-page control/JS memory → one-time changed-value close fragment → PebbleKit JS handoff/request memory plus plaintext localStorage → HTTPS Authorization → transient backend request/upstream-call memory → exact-host PRIM apikey. The fragment is not HTTP. Never path/query, favorites/results, AppMessage/watch, backend persistence/cache, logs/metrics/traces/errors, or public artifacts.
    • requestId and favoriteId bind a response to the request that produced it; the watch discards a late response for another request or favorite.
    • AppMessage buffers are computed from measured dictionaries and runtime platform maxima, not hard-coded folklore; messages chain on outbox_sent.
    • No recurring second/minute network timer exists. A minute tick may update displayed countdown/age only while the view is active and must never trigger network or Bluetooth traffic.
    • Keep source code, issue text, logs, and technical documentation in English; only authored product copy is localized.

    🔀 Dependencies

    None. This is the first implementation ticket.


    ⚠️ Impact on Existing Behavior

    Greenfield repository; there is no existing application behavior to preserve.


    🔍 Technical Context

    Architecture ownership

    • Backend: IDFM lookup, transient personal-key relay to the exact PRIM host, external validation/normalization, and non-secret shared fresh caching; no credential/user-preference persistence or Pebble rendering.
    • Configuration page: search/editing plus masked key enter/replace/remove; receives only hasKey, never the stored key on reopen, and owns no persistent secret/watch state.
    • PebbleKit JS: one atomic phone-local configuration containing favorites and the personal key, event-driven HTTPS requests, and watch synchronization; no SIRI parsing or watch layout.
    • Watch: one Alloy embedded-JavaScript app (Piu UI, built-in fonts, touchCount 0) with button input, non-secret keyStatus, compact state, and rendering; no key, stop search, external API, animations/media/networking/WakeUp/Worker/timeline polling, or secret storage.

    Favorite

    schemaVersion, id, opaque serviceId, optional displayName, stopLabel, lineLabel, destinationLabel, sortOrder.

    DepartureResult

    schemaVersion, requestId, favoriteId, fetchedAt, optional sourceUpdatedAt, freshness (REALTIME | SCHEDULED | MIXED | STALE), departures[] with expectedAt, optional aimedAt, minutes, status (ON_TIME | DELAYED | CANCELLED | UNKNOWN), and optional nextIntervalMinutes.

    ApiKeyUpdate (phone-only; never AppMessage)

    schemaVersion, action (KEEP | REPLACE | REMOVE), optional value present only for REPLACE.

    ErrorResult

    schemaVersion, requestId, optional favoriteId, code (API_KEY_REQUIRED | API_KEY_INVALID | INVALID_SERVICE | SOURCE_UNAVAILABLE | RATE_LIMITED | INVALID_RESPONSE), occurredAt, and optional sanitized numeric retryAfterSeconds.

    Required references

Sign in to comment.