Files
hemx/PLAN.md
T
slhx agent 5ea747d968 feat(kanban): recover after retry exhaustion
req: sync/004\nreq: sync/010\nreq: sync/011\nreq: sync/014\nreq: sync/016
2026-07-13 16:47:52 +02:00

15 KiB

Hemx v1 implementation plan

Authority: REQUIREMENTS.md. Product evidence: docs/v1-product-evidence.md. This file is an execution cursor, not requirement authority or release permission. Publishing, deployment, artifact upload, and package-registry mutation are out of scope.

Product boundary

Hemx v1 keeps server-rendered HTML and ordinary Rust as the default. Generated resources and the versioned EffectBatch are the one UI contract. Client-local WASM and durable sync are optional execution layers, not a component framework. Hemplate remains the rendering/Surface foundation; application auth, persistence, encryption, retention, backup, and deployment policy remain host concerns.

Slice 1 — one real client-local handler

  • User value: a Rust author marks one high-frequency handler local and gets immediate browser behavior without app-authored JavaScript or a request.
  • State: Done. hemx-build validates same-origin client module metadata and generates the WASM import, initialization, handler registration, fingerprint, and ready marker. Client handlers receive versioned ClientEvent/root-owned ClientState; incompatible input is rejected before handler execution, reports an actionable hemx:client-error, restores pending UI, and invokes an explicitly declared server fallback. Real WASM applies the ordinary generated-target EffectBatch with zero request on valid input.
  • Build: add the smallest optional hemx-wasm boundary for #[hemx::handler(client)]; export only opted-in handlers; generate typed event/state ABI glue; run one existing generated-target interaction through the ordinary EffectBatch interpreter; preserve an explicit native/server fallback.
  • Refusals: no VDOM, component lifecycle, global store, sync queue, second effect protocol, or generic WASM framework.
  • Requirements: client_local/001-010, security/001, security/005-006, performance/003, v1_release/001.
  • Proof: cargo test -p hemx-wasm --test browser client_handler_applies_effect_batch_without_network -- --exact serves only generated template HTML, the ordinary runtime, and generated client bootstrap; it visibly updates a generated target through real WASM, keeps the resource count unchanged for valid input, and proves invalid state diagnostics, pending restoration, and one declared fallback request. Formatting, workspace tests, strict all-target Clippy, and wasm-target build pass.

Slice 2 — direct manipulation that survives interruption

  • User value: Kanban drag/reorder follows the pointer immediately, remains keyboard operable, and cannot apply stale work after cancellation or root removal.
  • State: Done. The canonical Kanban client board is rendered through Hemplate and generated resources, then reordered by its real WASM handler for drag/drop and Arrow-key interaction. Client-local runs use validated latest/drop policy; stale/unmounted completions cannot apply effects; moved-card focus, live status, reduced-motion state, root cleanup, and a measured sub-100 ms local response are browser-proven.
  • Build: use the client handler in the canonical Kanban path; add cancellation/supersession, root-owned state cleanup, keyboard equivalent, focus/status behavior, reduced-motion behavior, and measured response/frame budgets.
  • Refusals: no persistence, collaboration, or animation framework yet.
  • Requirements: client_local/011-014, accessibility/001-007, operations/002-003, performance/001, performance/003, milestone/001.
  • Proof: cargo test -p hemx-wasm --test browser kanban_reorder_has_pointer_keyboard_focus_and_reduced_motion_parity -- --exact covers real-WASM pointer and keyboard reorder, focus/status, reduced motion, and the 100 ms response budget; the client-handler browser proof covers cancellation, removal, error recovery, zero-request behavior, and root cleanup.

Slice 3 — durable offline command log

  • User value: an opted-in Kanban mutation remains available after network loss and browser reload without storing DOM patches as truth.
  • State: Complete — the app-owned reorder_card command is transactionally persisted with schema, actor, session, causal id, and app payload before projection; an app-owned service worker caches only the generated shell/resources, and reload restores the projection after the fixture server is stopped and proven unreachable. Native recovery controls export a versioned credential-free command envelope, delete queued commands while preserving actor/causal identity, and reset command data, identity, cache, and registration behind explicit confirmation. Unknown schemas and malformed current-schema records stop before projection with explicit recovery diagnostics. Transaction conflicts and injected quota exhaustion neither project nor emit durability claims and remain recoverable through export/delete/reset. Replay is preflighted before effects, capped by the app at 64 commands, and measured against a 100 ms browser budget. A programmatic queued/busy state appears within the 100 ms direct-interaction budget before blocked persistence completes. The ordinary server-first route loads only the fingerprinted base runtime and creates no client root, service worker, or IndexedDB database; PWA/WASM policy remains app-owned and opt-in.
  • Build: add an optional durable command-log adapter around platform transactional storage; persist versioned command ids and app payload before projection; restore projection after reload; expose queue state, export/delete/reset, quota/corruption failure, and migration refusal.
  • Refusals: no server reconciliation, CRDT, mandatory IndexedDB, credential storage, or policy hidden in core.
  • Requirements: local/001-004, sync/009, sync/014-015, sync/020, security/007, accessibility/004, client_local/013, performance/003, performance/005-006.
  • Proof: cargo test -p hemx-wasm --test browser kanban_command_persists_before_projection_and_restores_after_reload -- --exact proves transactional persist-before-project ordering, app-owned shell caching, current-version replay after a real Firefox reload with the fixture server unreachable, stable identity metadata, and explicit unknown-schema refusal through real WASM. cargo test -p hemx-wasm --test browser kanban_command_export_delete_and_reset_are_recoverable -- --exact proves accessible export/delete/reset entry points, versioned credential-free export, confirmation before destructive actions, preserved identity after queue deletion, and fresh identity/baseline projection after reset. cargo test -p hemx-wasm --test browser kanban_persistence_failure_does_not_project_and_recovers -- --exact proves transactional failure does not project or emit kanban:command-persisted, reports non-payload stage/code diagnostics, and recovers through the ordinary deletion path. cargo test -p hemx-wasm --test browser kanban_corrupt_command_refuses_projection_and_recovers -- --exact proves strict current-schema validation, no partial projection, visible non-payload diagnostics, raw versioned export for recovery, and ordinary deletion recovery. cargo test -p hemx-wasm --test browser kanban_replay_is_bounded_and_within_budget -- --exact proves 64-command preflight/replay within the 100 ms browser budget, zero partial projection at 65 commands, and export/delete recovery. cargo test -p hemx-wasm --test browser kanban_quota_failure_is_fail_closed_and_recoverable -- --exact proves quota-specific fail-closed behavior, no false durability event, transactional metadata rollback, and export/delete/reset recovery. cargo test -p hemx-wasm --test browser kanban_queued_status_precedes_durable_projection_within_budget -- --exact proves queued/busy feedback within the performance/003 and client_local/013 100 ms budget while persistence is blocked, followed by durable projection only after commit. cargo test -p hemx-kanban-example --test browser_e2e server_first_route_does_not_load_optional_client_assets -- --exact proves the server-first route loads only its fingerprinted base runtime and creates no optional client root, PWA/WASM request, service worker, or IndexedDB database.

Slice 4 — authoritative reconnect and convergence

  • User value: offline and concurrent work reconnects without duplicate mutation, silent loss, stale authorization, or ambiguous conflict.
  • State: In progress — one app-owned move_card server command validates a durable client command id, applies the authoritative canonical column once, returns the same acknowledgement for an identical retry, rejects id reuse with a different payload, assigns one server sequence, and redelivers that canonical acknowledgement after a real EventSource disconnect/reconnect. Canonical acknowledgements and the next sequence are durably stored in a strict versioned JSON envelope using fsync plus atomic replacement; startup refuses malformed/unknown state, rebuilds the canonical board, and preserves idempotency and event replay across a real process restart. A dedicated opt-in sync route reads one pending IndexedDB command, retries transient failures with capped exponential backoff and randomized jitter, exposes online/offline state plus an accessible manual retry after exhaustion, waits for canonical acknowledgement on the reconnecting transport, and only then removes the durable command; later retry converges without a new local mutation. Canonical payload conflicts are not retried and remain durable with a visible reason. Broader conflict decisions, multi-tab leadership, backpressure, and auth isolation remain.
  • Build: materialize hemx-sync over an integration transport with idempotent server command processing, snapshot/change cursor, durable acknowledgements, bounded ordered replay, current auth checks, rejection/conflict results, canonical replacement, reconnect jitter/backoff, multi-tab coordination, and redacted diagnostics.
  • Refusals: no default CRDT, transport in core, cached enqueue-time permission, unbounded queue, or silent last-write-wins policy.
  • Requirements: sync/001-023, operations/001-005, security/002-005, performance/004-005.
  • Proof: cargo test -p hemx-kanban-example --test browser_e2e idempotent_server_command_is_acknowledged_after_reconnect -- --exact proves duplicate POST delivery yields one identical canonical acknowledgement/sequence, conflicting id reuse is rejected, EventSource reconnects after a server-closed first stream, the acknowledgement is delivered once with its sequence as event id, and a page reload shows the authoritative card in the canonical column. cargo test -p hemx-kanban-example --test browser_e2e pending_local_command_uploads_with_bounded_retry_and_is_removed_on_ack -- --exact proves automatic platform-store upload, one explicit transient failure, bounded exponential backoff plus jitter, reconnect acknowledgement, pending-before-ack ordering, acknowledged removal, canonical board convergence, and non-retried 409 rejection remaining durable with a visible reason. cargo test -p hemx-kanban-example --test browser_e2e canonical_acknowledgement_survives_server_restart -- --exact proves the versioned store is materialized before success, a real process restart reloads the same idempotent acknowledgement/sequence, EventSource replays it by id, and canonical board state is rebuilt. cargo test -p hemx-kanban-example --test browser_e2e exhausted_offline_retries_keep_command_until_later_reconnect -- --exact proves three bounded retries exhaust into visible offline/manual-recovery state while the command remains durable, then a later retry acknowledges/removes it and converges canonically. The completed slice proof must additionally cover broader partial reject/conflict decisions, missing-history snapshots, two tabs, backpressure, upgrade mid-queue, and multi-user isolation.

Slice 5 — local-first multiplayer Kanban milestone

  • User value: the complete north-star app demonstrates SSR-first startup, direct manipulation, offline durability, optimistic projection, reconciliation, and live presence as one comprehensible workflow.
  • State: Blocked by Slice 4.
  • Build: connect the previous slices in the canonical Kanban example; keep native server-rendered fallback; add presence and server-canonical conflict presentation; exercise deploy fingerprint recovery and accessible online/offline/conflict state.
  • Refusals: no demo-only runtime, hidden app JS, proprietary service, or requirement to load collaboration code for server-first apps.
  • Requirements: milestone/001, v1_release/001-002, accessibility/001-007, operations/006, performance/006.
  • Proof: one browser journey covers SSR/no-script fallback, local drag, keyboard reorder, offline/reload, concurrent peer edit, reconnect/convergence, presence, conflict/rejection recovery, mixed-version reload, and optional-asset isolation.

Slice 6 — production integration reference

  • User value: adopters can copy a proven boundary for durable storage, auth, transactions, security controls, observability, and restart recovery without hemx owning vendor policy.
  • State: Ready after public execution/sync contracts stabilize.
  • Build: evolve one existing reference app using ordinary integration adapters; add durable app storage, authenticated/authorized allowed and denied mutations, CSRF/origin checks, transaction rollback, bounded input, structured failures, health/readiness, tracing/metrics hooks, and restart/deploy recovery.
  • Refusals: no built-in database/auth provider, compliance claim, telemetry vendor, deployment system, or repository framework.
  • Requirements: security/001-009, operations/001-008, v1_release/003, existing adapter/*, integration/*, and diagnostics/* contracts.
  • Proof: end-to-end test survives process restart and mixed deployment, proves allowed/denied/rolled-back mutations and redacted diagnostics, and maps each framework-owned ASVS-relevant control to a failing/passing case.

Slice 7 — v1 compatibility and closure

  • User value: maintainers and adopters receive a reproducible, migration-aware v1 with no known material contradiction and no hidden publication side effect.
  • State: Blocked by Slices 1-6 and explicit authority for any missing local audit tool installation.
  • Build: freeze the supported Rust/browser/WASM/integration matrix; reconcile public/generated/Surface/symbol/wire/runtime/persisted-schema compatibility; add migration fixtures; make canonical examples compatibility tests; update the progressive tutorial path; run all local release gates and disposition every P0/P1, advisory, unsafe-code, license, performance, accessibility, and documentation finding.
  • Refusals: no publish, deploy, upload, store submission, speculative feature, or weakening a gate to make it pass.
  • Requirements: v1_release/001-010, versioning/*, test/*, diag/*, performance/*, security/008, and all requirements changed by the preceding slices.
  • Proof: clean-tree formatting, workspace tests, strict all-target Clippy, compile-fail, browser/WASM/offline/multiplayer scenarios, benchmark budgets, approved pinned lockfile audit, requirements proof audit, docs/examples checks, and independent contradiction review all pass with no unresolved P0/P1.