Root-anchored mutation through the interactive confirmation path
Fixture:fixture-test-exec-mutation-a@4
Given
The built termux-janitor executable starts on a pseudo-terminal with an isolated environment:
HOME contains exactly one temporary file a.tmp, PREFIX points at a separate empty
directory, XDG_STATE_HOME points at a third directory, and TERM names a non-dumb terminal.
The second run replaces the reviewed leaf's content after the scan and before confirmation. The
third run nests a.tmp one directory deeper and swaps that ancestor directory for a new one
holding the identical leaf after the scan and before confirmation. The fourth and fifth runs give
HOME a sub tree containing a.tmp, an empty directory, and a symlink pointing outside the
reviewed tree; the fifth replaces the reviewed a.tmp child with a same-name successor before
confirmation.
When
Five sessions drive the terminal.
Every session focuses its target through search so the canonical checklist ordering never affects
which row receives the keys.
The confirmed session waits for the alternate screen, searches for a.tmp, selects the focused
match, enters confirmation, types the exact phrase, and presses Enter.
The drifted session repeats the same interaction after rewriting a.tmp between the scan and
the confirmation phrase.
The ancestor-drifted session repeats it against the nested a.tmp after the ancestor swap.
The directory-confirmed session searches for sub, selects the directory, and completes the
same confirmation.
The directory-drifted session repeats that interaction after replacing the reviewed child.
Required invariants
INV-EXEC-MUTATION-A:
The confirmed session exits cleanly without crash markers, unlinks exactly a.tmp from its
selected root, and appends adjacent intent and success result frames to the run log.
INV-EXEC-ANCESTOR-A:
The ancestor-drifted session never mutates: the reviewed leaf itself is unchanged at its path,
no success frame is recorded, and the run log records a failure result frame.
INV-EXEC-DIRECTORY-A:
The directory-confirmed session removes the whole reviewed sub tree, including the empty
directory and the symlink, records a success result frame, and the symlink target outside the
reviewed tree survives.
Forbidden effects
NO-EXEC-MUTATION-A:
The drifted session never records a successful mutation: the changed leaf survives and the run
log records a failure result frame.
NO-EXEC-DIRECTORY-A:
The directory-drifted session never records a successful mutation: the replaced child and the
directory survive and the run log records a failure result frame.
Ancestor-replacement drift, mount changes, and whole-directory child-set comparison are owned by
the manifest suite in requirements/manifest_a.md.
Limitations
The sessions observe the final filesystem and run-log state plus the terminal byte stream; they do
not trace syscalls, so adjacency of check and mutation is inferred from the recorded frame order
and the single final-interval race disclosure in PRODUCT.md section 11.1 remains outside this
fixture.
# Root-anchored mutation through the interactive confirmation path
**Fixture:** `fixture-test-exec-mutation-a@4`
## Given
The built `termux-janitor` executable starts on a pseudo-terminal with an isolated environment:
`HOME` contains exactly one temporary file `a.tmp`, `PREFIX` points at a separate empty
directory, `XDG_STATE_HOME` points at a third directory, and `TERM` names a non-dumb terminal.
The second run replaces the reviewed leaf's content after the scan and before confirmation. The
third run nests `a.tmp` one directory deeper and swaps that ancestor directory for a new one
holding the identical leaf after the scan and before confirmation. The fourth and fifth runs give
`HOME` a `sub` tree containing `a.tmp`, an empty directory, and a symlink pointing outside the
reviewed tree; the fifth replaces the reviewed `a.tmp` child with a same-name successor before
confirmation.
## When
Five sessions drive the terminal.
Every session focuses its target through search so the canonical checklist ordering never affects
which row receives the keys.
1. The confirmed session waits for the alternate screen, searches for `a.tmp`, selects the focused
match, enters confirmation, types the exact phrase, and presses Enter.
2. The drifted session repeats the same interaction after rewriting `a.tmp` between the scan and
the confirmation phrase.
3. The ancestor-drifted session repeats it against the nested `a.tmp` after the ancestor swap.
4. The directory-confirmed session searches for `sub`, selects the directory, and completes the
same confirmation.
5. The directory-drifted session repeats that interaction after replacing the reviewed child.
## Required invariants
- **`INV-EXEC-MUTATION-A`:**
The confirmed session exits cleanly without crash markers, unlinks exactly `a.tmp` from its
selected root, and appends adjacent intent and success result frames to the run log.
- **`INV-EXEC-ANCESTOR-A`:**
The ancestor-drifted session never mutates: the reviewed leaf itself is unchanged at its path,
no success frame is recorded, and the run log records a failure result frame.
- **`INV-EXEC-DIRECTORY-A`:**
The directory-confirmed session removes the whole reviewed `sub` tree, including the empty
directory and the symlink, records a success result frame, and the symlink target outside the
reviewed tree survives.
## Forbidden effects
- **`NO-EXEC-MUTATION-A`:**
The drifted session never records a successful mutation: the changed leaf survives and the run
log records a failure result frame.
- **`NO-EXEC-DIRECTORY-A`:**
The directory-drifted session never records a successful mutation: the replaced child and the
directory survive and the run log records a failure result frame.
## Instrumentation
```zig tj-test
try fixture.given_program("termux-janitor");
try fixture.given_isolated_tree();
try fixture.when_run_sessions();
try fixture.then_inv_exec_mutation_a();
try fixture.then_inv_exec_ancestor_a();
try fixture.then_inv_exec_directory_a();
try fixture.forbid_no_exec_mutation_a();
try fixture.forbid_no_exec_directory_a();
```
## Variations
Ancestor-replacement drift, mount changes, and whole-directory child-set comparison are owned by
the manifest suite in [`requirements/manifest_a.md`](../requirements/manifest_a.md).
## Limitations
The sessions observe the final filesystem and run-log state plus the terminal byte stream; they do
not trace syscalls, so adjacency of check and mutation is inferred from the recorded frame order
and the single final-interval race disclosure in `PRODUCT.md` section 11.1 remains outside this
fixture.