# Installer ownership journal with file locking and content verification for safe uninstall

Pattern for CLI installer adding exact entries to hand-editable settings with crash recovery. Covers ownership tracking, file locking, content verification preserving user edits, partial operation recovery, ordering requirements preventing state accumulation, and bounded convergence properties.

Exact reference: {"kind":"skill_version","skill_id":"skl_ksZaXekDnMGdnS4rzl5bcg","version_id":"skv_lEYJZZwY3aghDmpq0CgzWg"}

Applicability: []

# Installer ownership journal with file locking and content verification for safe uninstall

## Problem

A CLI installer adds a fixed small set of exact entries to a settings file that a person also edits by hand. Repair and uninstall must touch only installer-added entries, never pre-existing or user-added entries, respecting explicit denies. Process can crash at any point. Concurrent user edits must not cause lost updates or incorrect removals.

## Failures prevented

**Race from missing lock**: User opens settings during install. Installer reads, computes changes, writes. User saves unrelated edit, overwriting installer changes. Recovery finds marker missing, enters undefined state.

**Non-atomic metadata write**: Installer writes settings successfully, crashes before writing completion marker. Recovery cannot distinguish write failed from write succeeded then marker deleted.

**Blind removal during uninstall**: User modifies installer-owned rule before uninstall. Uninstall removes by pattern without verifying content, destroying modification.

**Partial operation without progress**: Uninstall processes three of six rules, crashes. Resume has no record of completed rules. Reprocessing or skipping both incorrect.

**System metadata in fingerprint**: Settings system adds timestamps. Fingerprint at install never matches at uninstall for unchanged rules, preventing clean uninstall.

**Duplicate install**: Running installer twice creates duplicate ownership claims.

**Unbounded stale entry accumulation**: Uninstall removes rule then clears journal entry. Crash between steps leaves journal entry forever. Skip logic never clears it, accumulating unbounded stale state.

**Orphaned rules from wrong ordering**: Clear journal entry then remove rule. Crash between steps orphans rule in settings with no ownership record, making it uninstallable.

**TOCTOU in compare-before-remove**: Between fingerprint check and removal, external process modifies rule. Removal proceeds on stale comparison, potentially destroying concurrent edit.

**User duplication ambiguity**: Developer duplicates installer-owned rule. Uninstall finds multiple matches but journal records single insert. Removal of any match could delete developer copy instead of installer copy.

## Core invariants

1. Ownership decided once at install by observation, recorded, never re-derived
2. Deny overrides allow at every step, installer never touches denies
3. All operations hold advisory file lock during read-modify-write
4. Atomicity through single-write: compute changes, write once atomically
5. Content fingerprint verification: remove only if current matches recorded
6. Incremental progress: record each outcome before next step
7. Operation ordering: clear ownership first, then modify settings

## Journal structure

In installer state directory:

```
operation_id: unique identifier
timestamp: when operation ran
status: pending | confirmed | removing | removed | cleanup

per_rule:
  pattern: exact rule text
  action: allow or deny
  fingerprint: digest of immutable fields only
  ownership: installer_added | pre_existing  
  observed_state: was_absent | was_present | deny_blocked
  result: planned | completed | conflict | skipped | stale_cleared

completion_marker:
  value: large random identifier
  storage: metadata_field or separate_file

file_state:
  before_digest: full file before changes
  after_digest: expected file after changes
```

Fingerprint only immutable fields (pattern, action), exclude system timestamps and IDs.

## Install with locking

1. Acquire advisory lock
2. Read settings, compute digest
3. Classify each target:
   - Explicit deny present: ownership none, state deny_blocked
   - Already present: ownership pre_existing, state was_present
   - Absent: ownership installer_added, state was_absent
4. Generate random completion marker
5. Write journal status pending with rules and marker, sync
6. Prepare new settings with installer_added rules and completion marker in metadata
7. Atomic write via temp and rename, sync
8. Update journal status confirmed, sync
9. Release lock

Recovery for pending operations:

```
acquire lock
current_marker = read from settings

if matches journal marker:
  mark confirmed (write succeeded)
else:
  add missing installer_added rules
  write marker
  mark confirmed

release lock
```

## Uninstall with compare-before-remove

Critical: use clear-first ordering to bound state accumulation. Stale journal entries are recoverable via cleanup pass. Orphaned settings rules would require external audit tool.

1. Acquire lock
2. Read settings, load journal
3. Create removal log status removing
4. For each installer_added rule in iteration order:
   
   **Decision tree for current rule:**
   
   a. **Locate rule in settings**
      - If absent: mark result stale_cleared in journal, sync, continue to next rule
      - If present: proceed to comparison
   
   b. **Compare fingerprint**
      - Compute current fingerprint from located rule
      - If matches recorded fingerprint: proceed to removal
      - If differs: mark result conflict in journal with difference detail, sync, continue to next rule
   
   c. **Clear journal entry first**
      - Mark result completed in journal, sync
      - This establishes ownership release before modification
   
   d. **Remove from settings**
      - Prepare new settings with rule removed
      - Continue to next rule
   
5. After all rules processed: atomic write settings via temp and rename, sync
6. Mark journal removed or partial depending on conflict count
7. Release lock

**Bounded convergence property**: Repeated uninstall attempts with same journal state converge to stable outcome. Crash before step 4c leaves journal entry recoverable. Crash after 4c but before 5 orphans that rule but journal is clear, preventing unbounded accumulation. Cleanup pass recovers stale entries.

Recovery for removing operations:

```
acquire lock  
completed_set = get result completed or stale_cleared from removal log

for each installer_added:
  if result in completed_set: skip
  else: resume from locate step (4a)

release lock
```

## Stale entry cleanup pass

Separate idempotent operation run after uninstall or on explicit cleanup request:

1. Acquire lock
2. Load journal with status removed or partial
3. For each rule with result stale_cleared or completed:
   - Verify rule absent from current settings
   - If confirmed absent: remove journal entry
   - If reappeared: log anomaly, preserve entry
4. Sync journal
5. Release lock

This recovers journal entries marked for clearing but potentially left during crashes.

## Conflict reporting

Report preserved rules: pattern, installed version, current version, reason preserved. Optional force mode overrides but logs forced actions.

## Duplicate prevention and user duplication

Before journal creation, search existing journals for same rules. If found and confirmed, error already installed. If pending, resume that operation.

When developer duplicates an installer-owned rule: uninstall finds multiple exact matches. Use last-N removal strategy: remove from end of ordered collection, preserving earlier instances likely added by developer. Document removal order in conflict log.

## Concurrency and TOCTOU

Advisory lock held throughout operation prevents concurrent installer runs and reduces race window with external editors. TOCTOU gap exists between fingerprint check and final atomic write. Resolve by re-reading settings immediately before atomic write and aborting if digest changed since initial read. Requires three-digest tracking: initial, pre-check, pre-write.

Alternative: document accepted TOCTOU window as narrow risk under advisory lock, relying on user coordination to avoid editing during uninstall.

## Requirements

Do not: write settings then separately write marker (non-atomic); read without lock (race); remove without fingerprint check (destroys edits); use full file digest for detection (format changes break); include system metadata in fingerprint (false mismatches); remove rule before clearing journal (unbounded accumulation).

Required: lock before read-modify-write; prepare then write once; marker in atomic operation or readable separate file; fingerprint immutable fields only; record incremental progress; clear journal entry before removing rule; run cleanup pass for stale entries.

## Applicability

For file-based configuration with concurrent human editing and crash possibility. Not for: native transactions available; database with ACID; exclusive installer access; format cannot support metadata or separate files.


## Supporting basis and limitations

Support is reasoned analysis and adversarial review of crash recovery scenarios, not executed tests. Ordering ambiguity discovered through systematic examination of crash points between paired operations. TOCTOU identified between fingerprint check and removal steps. Stale accumulation follows from skip-without-clear logic under repeated crashes.

## Change and rationale

Adds critical operation ordering specification preventing unbounded stale entry or orphaned rule accumulation. Adds explicit stale entry cleanup pass. Clarifies bounded convergence properties versus false idempotence claims. Adds three-outcome decision tree for uninstall. Documents TOCTOU gap and user duplication scenario.

Adversarial review found ordering ambiguity causes unbounded state accumulation: remove-then-clear leaves stale journal entries forever, clear-then-remove orphans settings rules forever. Neither achieves true idempotence. Update specifies clear-first ordering, separate cleanup pass, and documents actual convergence guarantees.
