Skip to content

Bundle 49: bounded import progress and cancellation lifecycle (49C) #103

Description

@nulleimy

Value

Complete Bundle 49 by making a long desktop library import observable and safely cancellable without exposing host paths, sidecar credentials, ports, process handles or generic host authority to the renderer.

Dependency

  • predecessor PR: Bundle 49: desktop library read model completion #102 (Bundle 49B read-model completion)
  • predecessor exact head: 1884a874a2c467919959dee17f0257faeb79c944
  • implementation must not be merged ahead of its predecessor
  • canonical merge/release authorization remains separate

Current architecture finding

renderer
  -> library_import_root (awaits whole operation)
  -> Rust spawn_blocking
  -> one-shot authenticated Python sidecar
  -> POST /v1/library/import (blocking)
  -> scan -> import metadata/identity -> persist
  -> final result

The scanner already supports a cancel_requested callback, but the desktop service currently constructs it without a cancellation source. The current renderer cannot cancel an in-flight import because the only import command returns after the whole sidecar request finishes.

Target schema tree

renderer
  -> library_import_start(capability_id)
       -> opaque import_job_id
  -> library_import_status(import_job_id) [bounded polling]
       -> state + phase + monotonic safe counters
  -> library_import_cancel(import_job_id)
       -> idempotent cancellation request

Rust ImportJobRegistry
  -> owns job identity + lifecycle
  -> owns sidecar session control
  -> never exposes root/secret/nonce/port/process handle

Python sidecar
  -> cooperative cancellation token
  -> scanner / importer / persistence cancellation checkpoints
  -> bounded progress snapshots
  -> graceful stop
  -> forced supervisor termination only as bounded fallback

Lifecycle contract

Job states:

  • pending
  • running
  • cancelling
  • succeeded
  • cancelled
  • failed

Phases:

  • starting
  • scanning
  • importing
  • persisting
  • finalizing

Safe counters:

  • discovered_entries
  • accepted
  • imported
  • persisted

Counters are non-negative and monotonic. Unknown totals must remain unknown rather than fabricated.

Security invariants

  • opaque random job IDs only;
  • capability ID is resolved by trusted Rust before job creation;
  • renderer never receives canonical root path;
  • renderer never receives sidecar secret, nonce, loopback port or PID;
  • no generic filesystem/shell/network command is introduced;
  • status/cancel reject unknown or stale job IDs fail-closed;
  • cancellation is idempotent;
  • at most one active library import per desktop app instance for this slice;
  • progress payloads contain no absolute paths or raw exception details;
  • hard process termination is fallback, not the primary cancellation mechanism;
  • SQLite writes remain transactionally safe and partial completion is explicit.

Deliverables

  • Rust ImportJobRegistry and bounded lifecycle DTOs;
  • narrow Tauri start/status/cancel commands;
  • cooperative cancellation wiring into the Python import pipeline;
  • phase/counter progress reporting;
  • renderer Cancel action and accessible progress state;
  • deterministic job lifecycle tests;
  • sidecar auth tests for status/cancel controls;
  • renderer authority regression tests;
  • rollback document and schema tree.

Acceptance

  • import start returns promptly with an opaque job ID;
  • renderer can observe phase changes without direct sidecar/network access;
  • counters never decrease;
  • cancellation during scanning terminates cooperatively and returns cancelled;
  • cancellation during later phases reaches a defined checkpoint and never reports fake success;
  • repeated cancel is safe;
  • unknown/stale job IDs fail closed;
  • app exit terminates active sidecar within bounded shutdown time;
  • full Python CI and Rust desktop proof gates pass;
  • no new absolute-path leak is introduced.

Out of scope

  • multiple concurrent imports;
  • resumable imports after process restart;
  • background daemon mode;
  • analysis jobs (Bundle 50);
  • cloud synchronization;
  • release/signing/notarization;
  • merge authorization.

Governance

This issue authorizes no merge, release, deploy or production effect. The safest implementation base is chosen only after predecessor #102 reaches its governed merge boundary.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bundleBundle workflowenhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions