Skip to content

Fix Malibu launch-agent migration and provider recovery - #952

Merged
antfleet-ops merged 11 commits into
mainfrom
fix/941-malibu-launchagent-migration
Aug 8, 2026
Merged

Fix Malibu launch-agent migration and provider recovery#952
antfleet-ops merged 11 commits into
mainfrom
fix/941-malibu-launchagent-migration

Conversation

@Augustas11

@Augustas11 Augustas11 commented Aug 8, 2026

Copy link
Copy Markdown
Owner

Summary

Closes #941.

This change makes Malibu's provider startup and migration flow safely handle standalone launchd installs, including stale services, custom install locations, missing provider binaries, unsafe filesystem ownership/ACLs, and unrecoverable loaded-provider conflicts.

What changed

  • Reclaims only launchd services whose labels and executable paths match the expected provider installation.
  • Preserves safe custom provider install locations during migration.
  • Adds a trusted-evidence repair path for missing provider binaries without silently adopting untrusted state.
  • Rejects unsafe directory/file ownership, permissions, links, and ACL grants.
  • Routes unrecoverable loaded-provider and manual launchd conflicts to explicit user intervention.
  • Adds transaction, rollback, launchd migration, startup-route, ACL, and repair-intent regression coverage.

Validation

  • bash -n phase3-binary/dist/install.sh
  • bash phase3-binary/dist/test/install_launchd_migration.test.sh
  • bash phase3-binary/dist/test/provider_upgrade_transaction.test.sh
  • bash scripts/test-install-launchd-enable.sh
  • bash scripts/test-watchdog-inline-drift.sh
  • bash phase3-binary/dist/test/install_upgrade_evidence_rollback.test.sh
  • Full macOS xcodebuild test suite: passed
  • Three-lane code, security, and architect audit: C/H/M = 0/0/0

SPEC-GOVERNANCE-DECLARATION-BEGIN
{
"schema_version": "spec-pr-governance-v1",
"behavior_change": "yes",
"contract_change": "none",
"specs": ["SPEC-003", "SPEC-020"],
"requirements": ["SPEC-020-R004"],
"authority_domains": ["provider-onboarding-identity", "provider-autoupdate"],
"arbitration": ["CODE_BUG"],
"tests": [
"phase3-binary/dist/test/install_launchd_migration.test.sh",
"phase3-binary/dist/test/provider_upgrade_transaction.test.sh",
"phase3-binary/dist/test/install_upgrade_evidence_rollback.test.sh",
"phase3-binary/app/Tests/MalibuTests/StartupRouteTests.swift"
],
"journeys": ["not-required"],
"issue": "#941"
}
SPEC-GOVERNANCE-DECLARATION-END

Preserve the incumbent provider while routing stale managed launchd state through explicit Malibu repair, and harden installer evidence and recovery publication against path races.

Constraint: Issue #941 must preserve the current origin/main release lineage and keep d-inference outside the clean-room audit scope.\nRejected: Silent background reinstall; a healthy provider must receive an explicit repair action.\nConfidence: high\nScope-risk: broad\nDirective: Audit the complete fix diff from the pre-fix origin/main base on every follow-up.\nTested: Malibu xcodebuild test, rollback evidence harness, launchd migration harness, launchd enable sequencing, watchdog inline-drift check, shell syntax, and diff check.\nNot-tested: Rootless Docker rewards integration environment.
Keep the documented HOME-scoped install override usable while applying the same owner-private, no-symlink, and no-traversal checks to Malibu manifest routing and credential handoff. Permit normal macOS deny-delete ACLs on directory ancestors without relaxing file-level trust.

Constraint: Existing SPEC-003 custom install overrides and macOS user-directory ACLs must remain compatible.\nRejected: Restricting all installs to ~/macprovider; it strands documented custom-path providers.\nConfidence: high\nScope-risk: moderate\nDirective: Keep custom-path validation aligned across installer, Malibu launchd detection, and credential handoff.\nTested: provider upgrade transaction harness, focused Malibu custom-path/ACL tests, bash syntax, and diff check.\nNot-tested: Rootless Docker rewards integration environment.
Route a loaded provider label with missing or unsafe launchd evidence to explicit manual intervention, and keep rollback fault coverage aligned with randomized descriptor-backed restore staging.

Constraint: The installer cannot safely reclaim a loaded label without a durable owner-safe plist snapshot.\nRejected: Falling through to ordinary onboarding; it leaves a loaded label in an unrecoverable retry loop.\nConfidence: high\nScope-risk: narrow\nDirective: Preserve manual-conflict routing whenever launchd evidence is insufficient for safe automatic reclamation.\nTested: StartupRoute and InstalledProviderMonitor tests, rollback evidence harness, shell syntax, and diff check.\nNot-tested: Rootless Docker rewards integration environment.
Allow the documented macOS group:everyone deny-delete ACL while rejecting all other extended directory ACL entries in both Malibu trust checks and the public installer. Add a regression test for a directory write grant so ACL behavior remains fail-closed.

Constraint: Preserve macOS user-directory deny-delete ACLs without weakening provider install-path ownership and mode checks.
Rejected: Treating every extended ACL as unsafe | macOS commonly applies a non-mutating deny-delete ACL to user directory ancestors.
Confidence: high
Scope-risk: narrow
Directive: Keep Swift and installer directory-ACL allowlists behaviorally identical when provider paths evolve.
Tested: bash -n phase3-binary/dist/install.sh; provider upgrade transaction test; InstalledProviderMonitorTests; full Malibu suite; launchd migration, launchd enable, and watchdog drift tests.
Not-tested: The rollback evidence harness continued running beyond the command wait window and must be confirmed separately.
Carry an explicit repair intent from Malibu to the installer and admit referral-free repair only when owner-private provider identity, manifest, and launchd plist evidence proves the prior install. This covers the deleted-binary failure mode without broadening ordinary fresh-install admission.

Constraint: Preserve referral admission for fresh installs and require the existing transaction/recovery boundary before launchd mutation.
Rejected: Treating every no-referral invocation as repair | External callers must not bypass referral admission without validated prior-install evidence.
Confidence: high
Scope-risk: moderate
Directive: Keep repair intent explicit and evidence-gated; do not infer it from a missing executable alone.
Tested: bash -n phase3-binary/dist/install.sh; provider upgrade transaction test including missing-binary repair evidence; launchd migration test; launchd enable test; watchdog drift test; full Malibu xcodebuild test suite; rollback evidence fault matrix.
Not-tested: Real launchd mutation on a production user session; CoreSimulator emitted an unrelated out-of-date warning during macOS tests.
Make unrecoverable launchd identity conflicts dominate CLI-owned import routing and recheck the boundary before a Start Fresh migration can move configuration. This keeps Malibu's manual-recovery UX aligned with the installer's fail-closed reclaim policy.

Constraint: A foreign or unsafe loaded launchd label must never be hidden behind migration or replacement onboarding.
Rejected: Relying only on installer rejection | It leaves users in a late, confusing failure after a destructive migration choice.
Confidence: high
Scope-risk: narrow
Directive: Preserve a manual-conflict dominance check whenever new migration routes or decisions are added.
Tested: StartupRouteTests; git diff --check.
Not-tested: Full suite and shell matrix will be rerun after the fresh audit cycle.
@cursor

cursor Bot commented Aug 8, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

antfleet-ops
antfleet-ops previously approved these changes Aug 8, 2026

@antfleet-ops antfleet-ops left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independent approval from antfleet-ops: reviewed the complete #941 diff and the recorded validation evidence; no blocking concerns.

Preserve the conformance selector audit when install.sh emits the inlined watchdog through its atomic writer.

Constraint: The watchdog must retain atomic publication while the governance checker must resolve its embedded Python mappings.
Rejected: Restore direct cat redirection | it would weaken the existing atomic write path.
Confidence: high
Scope-risk: narrow
Directive: Keep future atomic script writers recognized by the governance materialization detector.
Tested: python3 scripts/check_spec_governance.py --base-ref origin/main; bash -n phase3-binary/dist/install.sh; git diff --check origin/main..HEAD
Not-tested: GitHub Actions rerun after this commit.
antfleet-ops
antfleet-ops previously approved these changes Aug 8, 2026

@antfleet-ops antfleet-ops left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-approved updated head 8c34885 from antfleet-ops after validating the targeted governance-check fix; no blocking concerns.

Initialize the optional Malibu repair flag in the function-only referral test harness so strict-shell tests match the installer's production variable contract.

Constraint: The harness sources selected installer functions without executing the installer's top-level initialization.
Rejected: Relax the production parameter expansion | the harness is the isolated boundary that needs the default.
Confidence: high
Scope-risk: narrow
Directive: Initialize every installer global consumed by extracted function bodies in partial-source test harnesses.
Tested: bash phase3-binary/dist/test/install_referral_handoff.test.sh; bash -n phase3-binary/dist/test/install_referral_handoff.test.sh; git diff --check origin/main..HEAD
Not-tested: GitHub Actions rerun after this commit.
antfleet-ops
antfleet-ops previously approved these changes Aug 8, 2026

@antfleet-ops antfleet-ops left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-approved updated head a117c18 from antfleet-ops after the referral-harness fix and targeted test passed locally.

Make the Linux rollback matrix print the inner recovery error when its happy-path case returns an unexpected status, preserving deterministic diagnostics for platform-specific failures.

Constraint: The harness runs a nested installer transaction and otherwise discards its stderr for exit 70.
Rejected: Infer the Linux failure from the outer status alone | that obscures the recovery boundary being tested.
Confidence: high
Scope-risk: narrow
Directive: Keep platform-specific transaction failures diagnosable without changing the expected success path.
Tested: bash phase3-binary/dist/test/install_upgrade_evidence_rollback.test.sh; git diff --check origin/main..HEAD
Not-tested: GitHub Actions Linux rerun after this diagnostic change.
antfleet-ops
antfleet-ops previously approved these changes Aug 8, 2026

@antfleet-ops antfleet-ops left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-approved updated head 33823d5 from antfleet-ops; local rollback matrix and referral-handoff tests pass, with added Linux failure diagnostics.

Align app-side repair admission with installer evidence and make rollback preserve mixed-content installs without blocking on special files.

Constraint: Legacy standalone installs may contain owner-private evidence, FIFOs or special files, and unrelated support content; repairs must fail closed without discarding user data.
Rejected: Path-based recursive copies and pre-verifier reads | they can block, race, or admit semantically invalid repairs.
Confidence: high
Scope-risk: broad
Directive: Keep app-side repair predicates and installer recovery snapshots descriptor-validated and semantically aligned.
Tested: xcodebuild test (331 tests); install upgrade evidence rollback matrix; provider upgrade transaction; transaction lock; launchd migration; watchdog rollback and health; referral handoff; watchdog inline drift; bash -n; git diff --check.
Not-tested: Fresh GitHub CI on this final head.
antfleet-ops
antfleet-ops previously approved these changes Aug 8, 2026

@antfleet-ops antfleet-ops left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved final head 946be5c after local verification: 331 app tests and all targeted installer/recovery/watchdog suites passed.

Provide the narrow plist validation shim the launchd migration fixture needs when running its installer harness on non-macOS CI hosts.

Constraint: The fixture exercises macOS launchd behavior through mocks, while Linux CI does not provide Apple's plutil utility.
Rejected: Skipping the migration fixture on Linux | it would remove coverage from the required deploy gate.
Confidence: high
Scope-risk: narrow
Directive: Keep platform-specific fixture dependencies mocked at the test boundary.
Tested: bash phase3-binary/dist/test/install_launchd_migration.test.sh.
Not-tested: Fresh GitHub CI on this head.

@antfleet-ops antfleet-ops left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-approved updated head a292d54: CI portability shim only; launchd migration fixture passes locally.

@antfleet-ops
antfleet-ops merged commit 3564ff0 into main Aug 8, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Standalone→Malibu.app migration leaves zombie LaunchAgent squatting on live.streamvc.macprovider (permanent 'needs attention' loop)

2 participants