Skip to content

FR: fleet-wide foreign-app scrub (ml_status.sh --scrub) #55

Description

@uurf

Summary

Remove foreign-show packages (and other unexpected com.tindrum.* apps) from already-deployed devices over WiFi, without re-provisioning. Closes the gap left by v1.2.0, where SHOW_REMOVE_PACKAGES only applies during the provision pass — devices already on the floor with a foreign show app aren't cleaned until re-provisioned.

Why this lives in the status tooling

ml_status.sh already (a) iterates the fleet in parallel, (b) detects foreign packages — tindrum_extra at ml_status.sh:320 = installed com.tindrum.* that isn't TARGET_PACKAGE/KIOSK_PACKAGE, written into the per-device run JSON, and (c) has the --fix apply machinery (fix_device, ml_status.sh:408; dispatch ml_status.sh:694). The scrub plan can be computed from the JSON already collected — no extra device round-trip.

Why a NEW flag, not --fix

CLAUDE.md is explicit: "behavior that's fine in the flash→provision→deploy chain (e.g. removing apps) is wrong for a standalone fleet remediation pass." --fix only nudges settings (non-destructive, safe on show day). Uninstalling packages must be a deliberate, separate, confirmed action.

CLI surface

ml_status.sh --scrub            # dry-run: report what WOULD be removed (default)
ml_status.sh --scrub --apply    # actually uninstall
ml_status.sh --scrub -d <ip>    # single device
  • Dry-run by default; --apply is the trigger (mirrors --check muscle memory).
  • Composable with --failures; --json emits the plan/result.

What gets scrubbed

  1. SHOW_REMOVE_PACKAGES (active show conf) — known foreign-show apps. Always in scope (BLUE → com.tindrum.kagamu).
  2. Detected extras — the existing tindrum_extra set, gated behind --scrub-extras so an unexpected-but-maybe-wanted package isn't nuked by reflex.

Hard exclusions (never removable): active SHOW_PACKAGE and KIOSK_PACKAGE (ml_status.sh:69-70) — belt-and-suspenders even if SHOW_REMOVE_PACKAGES is misconfigured to list the active app.

Implementation notes (reuse, don't rebuild)

  • Add --scrub/--apply/--scrub-extras to the arg case (ml_status.sh:88).
  • New scrub_device() modeled on fix_device (ml_status.sh:408): read per-device JSON, intersect installed pkgs with SHOW_REMOVE_PACKAGES (+extras), minus exclusions. Uninstall via the proven two-step pm uninstall --user 0 <pkg> then pm uninstall <pkg> (ml_provision.sh:602-605). Log to logs/<ts>_<serial>.log.
  • Dispatch parallels the AUTO_FIX loop (ml_status.sh:694).
  • Guard every capture with || true — see the set -e/pipefail regression in v1.2.1; an unguarded x=$(… grep …) aborts the whole run.

Safety / guardrails

  • --scrub --apply is destructive → must call show_confirm (like flash/provision/deploy), honoring ML_SHOW_CONFIRMED. Plain --scrub (dry-run) stays banner-only and read-only.
  • Online-only (skip OFFLINE stubs); idempotent ("already gone" = no-op success); per-device + final Scrubbed/Skipped/Failed counts; a failed uninstall is logged, never aborts the fleet run.

Reporting

  • Table: post-run block of devices × removed packages.
  • JSON: add "scrub": { "planned":[…], "removed":[…], "failed":[…] } per device (additive; tindrum_extra already present).
  • Dashboard: fleet_dashboard.html already flags extra com.tindrum.*; rows clear on the next --json refresh — no dashboard change needed.

Testing (device-free, fits tests/)

Feed scrub_device a synthetic per-device JSON + fake adb/pm shim; assert: foreign app targeted, SHOW_PACKAGE/KIOSK_PACKAGE never targeted, blank config = no-op, dry-run issues zero uninstall calls.

Open decisions

  1. Dry-run default + --apply (recommended) vs. act immediately with show_confirm as the only gate.
  2. Ship --scrub-extras tier in v1, or SHOW_REMOVE_PACKAGES-only first.
  3. Flag name: --scrub (preferred) vs --remove-foreign vs --clean.

Context: spun out of the Osaka BLUE bench work (v1.2.0 added SHOW_REMOVE_PACKAGES for the provision pass). Spec only — not scheduled.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions