Termux Janitor v1 specification
1. Purpose
The product direction is summarized in GOALS.md.
Termux Janitor is a local Zig TUI for understanding and reclaiming storage used by Termux and its related tools. It scans accessible storage, visualizes disk usage, explains cleanup opportunities, and executes only the actions confirmed by the operator.
The tool optimizes for safe, informed cleanup on both phone-sized terminals and large screens. It is not a background cleaner and does not perform unattended deletion.
spec/EXPERIMENTAL_EVIDENCE.md maps platform experiments to the
requirements they support and states their limits. Experiments constrain unsafe inference but do
not replace this specification or automated acceptance evidence.
This specification incorporates the normative version 1 details in
LIMITS.md,
FILESYSTEM_CAPABILITIES.md,
CLASSIFICATION.md,
ADAPTERS.md,
CONFIGURATION.md,
MAN_PAGE.md,
ARTIFACT.md, and
TERMUX_PACKAGES.md. Structured registries explicitly linked by these
specifications own their declared facts. Conflicts stop work for an explicit decision; supporting
details never override a core guarantee.
2. Core guarantees
- TJ-CORE-01. No cleanup action runs without an interactive final confirmation.
- TJ-CORE-02. The operator can inspect and change every pre-selected action before confirmation.
- TJ-CORE-03. Every finding reached within the configured scan scope remains individually selectable for review. Findings classified as sensitive are never pre-selected or selected by bulk commands.
- TJ-CORE-04. Every pre-selection is rule-based and has a human-readable explanation in the details view.
- TJ-CORE-05. Every filesystem object selected for mutation and its reviewed ancestry are revalidated immediately before mutation, and every observed mismatch blocks that action. The residual POSIX races defined in section 11.1 are disclosed rather than claimed to be preventable.
- TJ-CORE-06. Symlink targets are never deleted through a symlink.
- TJ-CORE-07. The tool uses only the operator's existing permissions and never escalates privileges.
- TJ-CORE-08. Scan metadata is not persisted between runs.
- TJ-CORE-09. Scan incompleteness is visibly indicated and never presented as a complete result.
- TJ-CORE-10. Package removal and package-cache cleanup use the owning package manager rather than direct file deletion.
- TJ-CORE-11. Core behavior is implemented in-process; external executables are invoked only when an authoritative package-manager or platform operation cannot safely be replaced. A shell is never used as an execution intermediary.
3. Terminology
TJ-CONCEPT-02. Concept identities, canonical terms, definitions, invariants, aliases, distinctions, and requirement
relationships are owned by model/concepts.zon. The generated
GLOSSARY.md is the human-readable projection.
Implementation logic uses generated concept identifiers rather than presentation strings. UI labels may project a concept differently for context or width but never redefine it.
4. Scope
4.1 Default scan roots
TJ-SCAN-01. The initial configuration attempts to scan all accessible, non-excluded content below:
$HOME$PREFIX- configured workspace roots, defaulting to
~/Workspace - Termux shared-storage entry points created by
termux-setup-storage - installed
proot-distroroots - stores and caches discovered by supported package-manager adapters
TJ-SCAN-01. Roots and exclusions are configurable. The UI lists configured roots and exclusions so an intentionally narrowed scope cannot be mistaken for the default scope. Overlapping roots are deduplicated by filesystem identity. The scanner crosses filesystem and mount boundaries.
All application storage uses compile-time fixed capacities allocated during initialization before scanning begins.
Traversal streams results into bounded aggregates rather than retaining every encountered object.
Every retained path, traversal depth, queue, finding, package result, warning, UI output, and captured child-process output has an explicit fixed upper bound.
Reaching a bound marks the affected result incomplete and produces a visible warning; it never silently omits data or allocates additional memory.
The compact UI uses a human-friendly storage name; details show mount point, filesystem type, device identity, and root.
Each encountered filesystem records runtime-observed capabilities and confidence for identity, allocated-block accounting, descriptor-relative access, and no-follow behavior.
A filesystem type or successful stat call alone is not evidence that every capability is reliable.
4.2 In-scope cleanup classes
TJ-SCAN-11.
- package-manager caches
- packages classified as removable or orphans by their package manager
- superseded package, runtime, toolchain, and SDK versions
- large installed packages for operator review
- known build output directories
- version-control ignored files
- stale temporary files in known temporary locations
- temporary files identified by conservative filename and location heuristics
- old rotated or inactive log files
- broken symlinks
- trash contents
- other adapter-provided cleanup actions that satisfy this specification
4.3 Supported package ecosystems
Version 1 ships adapters for:
- Termux
pkgand APT - Cargo and rustup
- npm, pnpm, and Yarn
- pip, pipx, and uv
- Gradle and Maven
- Zig
- Go
- RubyGems and Bundler
- Composer
proot-distro- mise
- Android packages belonging to Termux and Termux add-ons
The adapter architecture must allow additional ecosystems without changes to scanning, ranking, selection, confirmation, or execution code.
Aliases and overlapping managers share one ownership domain: for example, Termux pkg and APT
must not duplicate package state or propose competing actions.
Canonical package identity consists of the ownership domain, manager installation instance or root, and manager-native transaction key. It includes a version or slot only when the manager treats that value as a distinct installed instance. Alias managers merge records only when authoritative metadata resolves them to the same canonical identity, and the merged record retains every contributing adapter as provenance. Equal display names in different ownership domains remain separate. Display names, discovery order, and executable paths are not package identity.
Unknown identity never deduplicates package state. When ownership domains overlap and no single authoritative owner can be established, affected findings remain selectable for review, but no executable action is available until ownership is established. Unavailable managers are silently skipped unless their files are detected but their executable cannot be used, in which case the UI shows a warning.
TJ-ADAPT-12. Network access is permitted only when local data cannot satisfy the current operator-visible adapter operation. Before initiating access, the UI publishes a non-modal disclosure containing the adapter, purpose, and request kind. The request starts no earlier than the next event-loop turn so normal cancellation can stop admission. Disclosure is informational and never opens a confirmation prompt.
Each request is scoped to one adapter operation and scan generation. The tool does not prefetch, probe speculatively, or request data for hidden or unrequested work. Identical retries remain in the same scope; new or widened work receives a new disclosure. Configuration provides global offline mode and per-adapter network disablement, both of which deny access without prompting.
The owning manager controls its configured destinations and credentials unless a supported process sandbox establishes a stronger boundary. The tool passes only the bounded environment required for the operation and never reads, displays, copies, or logs credentials or payloads. The run log records only bounded adapter, purpose, request-kind, timing, result, and uncertainty metadata.
The tool has no telemetry, remote service, or project-owned server.
5. Manual-selection policy
5.1 Manual-only by default
TJ-CLASS-13. The following findings are manual-only candidates:
- tracked source files
- unignored files in detected version-control work trees
- repository administration data, including VCS metadata
- files heuristically classified as source code when no repository metadata exists
- regular files directly below
$HOME - user documents below standard or configured document roots
Manual-only policy limits automation, not operator authority. These findings remain visible and individually selectable, but no recommendation or bulk command selects them. The compact checklist does not render per-finding policy labels; the selection control is the only per-row state. Applicable evidence and risk remain shown in details and the review plan. Configuration may add manual-only roots or patterns but cannot make the default classes eligible for automatic selection. An exclusion removes objects from the configured scan scope; exclusions remain visible in the scope summary.
Selecting a directory individually may authorize its descendants only when the complete reviewed manifest fits and is shown in the review plan. No scan-root alias, bulk command, adapter proposal, or incomplete parent action implicitly authorizes a manual-only descendant.
Before planning direct deletion inside a package-manager-controlled installed, store, cache, configuration, or service namespace, the tool establishes the authoritative owner. Missing, incomplete, unknown, or conflicting ownership leaves the finding selectable for review but makes direct deletion unavailable. An unavailable adapter never enables direct-deletion fallback. Selection expresses operator intent but never overrides execution-safety preconditions; unavailable selections cannot enter the confirmable plan.
5.2 Workspace policy
TJ-CLASS-13. Every configured workspace root is treated as source-bearing.
Repository detection and ignore classification use all supported VCS semantics.
For Git this includes tracked status, nested .gitignore files, .git/info/exclude, global excludes, submodules, and worktrees.
Every workspace finding is individually selectable. Tracked and unignored content, repository administration data, ignored content, and recognized build output are manual-only candidates and are therefore never pre-selected or selected by a bulk command.
5.3 Downloads and trash
TJ-CLASS-13. Downloads are fully reported and individually selectable. Known disposable installers, archives, duplicates, and temporary downloads are manual-only unless an adapter proves they are a redundant cache artifact. Trash content is individually selectable and may be pre-selected by age rules. Cleanup is permanent and does not move items to another trash location.
6. Classification and recommendation
6.1 Default thresholds
TJ-CLASS-14. Version 1 uses compiled defaults that can later be overridden in configuration. Exact compiled
values are canonical in spec/model/thresholds.zon and projected here:
large_file_bytes: 104857600 byteslarge_directory_bytes: 524288000 bytesold_seconds: 2592000 secondsstale_temporary_seconds: 604800 secondsstale_trash_seconds: 2592000 secondsold_rotated_log_seconds: 2592000 seconds
large file means at least large_file_bytes allocated; large directory means at least
large_directory_bytes allocated; old means no content modification for at least old_seconds;
stale known temporary file means at least stale_temporary_seconds old; stale trash item means
at least stale_trash_seconds old; old rotated log means at least old_rotated_log_seconds old.
Package review list: ten largest installed packages across discovered ecosystems.
MiB means exactly 1,048,576 bytes. Size and age thresholds are inclusive. One day means exactly
86,400 elapsed seconds, not a local calendar day.
Each scan generation captures one realtime reference immediately before scanning and uses it for all age calculations in that generation. Refresh starts a new generation and captures a new reference. A missing, invalid, unrepresentable, or future timestamp has unknown age, cannot satisfy an age rule, and produces a warning. When timestamp resolution is known, the recorded value represents an interval; age eligibility uses the latest possible instant in that interval so rounding cannot make an item appear older than established.
Access time is advisory only because Android and mounted filesystems may not maintain it reliably. Modification time, package-manager state, naming patterns, containment, and version relationships are stronger evidence.
6.2 Safe pre-selection
TJ-CLASS-15. A candidate may be pre-selected only when a specific rule identifies it as low risk. Default low-risk rules include:
- package-manager cache entries that the manager declares removable and whose execution can be bound to the reviewed effect set;
- dependencies that the manager declares automatically installed and no longer required;
- producer-declared disposable artifacts whose ownership and disposal semantics are established;
- stale trash entries whose deletion time is established by validated metadata from the trash implementation or a documented trash format.
Age, pathname, naming pattern, a currently missing symlink target, and absence from observable process file-descriptor tables are supporting evidence only. None proves inactivity or disposability.
The following are never pre-selected or selected by a bulk command:
- manual-only candidates;
- any candidate inside a configured workspace;
- explicitly installed packages;
- Downloads content, except proven redundant package-manager cache artifacts;
- live logs;
- broken symlinks without producer-specific disposal evidence;
- generic rotated logs or temporary files without producer-specific disposal semantics;
- heuristic-only candidates whose purpose is uncertain.
All recommendations show their rule, evidence, estimated reclaimed blocks, age evidence, and risk classification in the details view.
6.3 Logs and temporary files
TJ-CLASS-16. The scanner distinguishes logs from lock files. It recognizes ecosystem-specific logs, rotated logs, compressed logs, and conventional log directories. An observed open, mapped, or actively changing log is manual-only. When no reference is observed, details say “no reference observed”; absence of an observation is not proof that the log is inactive. Deletion removes the selected log file; the tool does not truncate active logs.
Temporary-file eligibility combines known locations, known producer patterns, age, and process evidence when present. Generic location, age, and negative process observation permit manual review but not recommendation. Pre-selection requires producer-specific evidence that the exact artifact is disposable. An arbitrary file outside a known context cannot be recommended solely because its name resembles a temporary file.
6.4 Package reporting
Package adapters prefer the manager's own concepts of explicit installation, automatic dependency, orphan, active version, cache, and removable state. They do not invent “unused” from access times, which are advisory only, when manager semantics exist.
The compact package view initially ranks retained installed packages with reliable allocated size
across all managers and shows the first min(10, ranked_count) using the default total ordering.
Stable tie-breakers resolve equal sizes; ties never expand the list beyond ten. Unknown-size packages
do not fill vacant positions because they cannot truthfully be ranked as largest. The view shows the
retained unknown-size package count.
Filters and an explicit “show all” operation expose all retained discovered packages, including unknown-size packages. “Show all” changes only the view; it does not rescan, allocate storage, or recover dropped findings. Active filters and search still apply.
Capacity exhaustion keeps the package result visibly incomplete. A bounded saturating counter shows
the exact dropped count while representable and otherwise reports “at least N omitted,” where N
is the counter maximum. The UI calls this view “all retained packages,” never all discovered
packages.
Explicitly installed large packages are eligible for manual uninstall but never pre-selected. Obsolete version artifacts are separate candidates only when the adapter can distinguish them from the explicit package and establish their disposal semantics.
7. Size accounting
TJ-SIZE-01. Allocated disk blocks are the primary size and ranking metric where the root has established reliable allocated-block semantics (the enumerated capability states in FILESYSTEM_CAPABILITIES.md).
TJ-SIZE-02. On Linux, stat and statx block counts use 512-byte units; conversion to allocated bytes uses
checked multiplication. Every size conversion, addition, subtraction, and reclaim estimate uses
checked arithmetic. Overflow never wraps, clamps, or becomes a saturated size; it makes only the
affected metric unknown. Independent metrics and known child or sibling values remain known.
Any aggregate with an unknown contribution is unknown and incomplete. Each affected displayed aggregate contributes one visible warning, while bounded technical details identify the failed operation and inputs in the run log. Unknown results use the normal unknown-size ordering and rendering rules. Saturation is permitted for bounded diagnostic occurrence counters, never for storage values.
A mediated or unsupported filesystem may report allocated size as unknown; apparent byte size remains visible but is never silently substituted as allocated or reclaimable space. Apparent byte size appears in details. Directories show recursively deduplicated totals.
TJ-SIZE-04. Accounting identity is scoped to one scan generation and one runtime filesystem identity domain. A trusted key contains filesystem identity, device, and inode. Accounting identities are never persisted or reused across refreshes, remounts, filesystem-daemon restarts, or runs.
TJ-SIZE-05. Within each displayed aggregate, one trusted inode is counted once. Every directory row shows its
recursively deduplicated size independently. When another displayed aggregate may contain any of
the same inodes, the row shows the text non-additive beside its size in compact and ASCII views;
Unicode and color may supplement but never replace that text. Details explain the overlap.
Displayed row values are never summed to produce a total. Unified, selected, filtered, and per-filesystem totals are computed directly from trusted inode identities in their own membership sets. Filtering changes aggregate membership but never the stored size of an individual directory finding. When untrusted identity prevents overlap determination, the affected total is unknown and incomplete rather than treating rows as additive.
Missing, unstable, untrusted, or conflicting identity retains the finding but makes every affected deduplicated total unknown and incomplete; path equality is never substituted for identity. A conflicting observation produces a warning. Scan-time accounting identity does not extend into execution: mutation still performs the complete immediate revalidation required by section 11.1. TJ-SIZE-06. An inode receives inode-attributed reclaim credit only when filesystem identity and allocated-block semantics are trusted, link count is reliable, distinct reviewed directory entries equal that count, and every entry is selected, executable, and included in the same confirmed plan. Aliases to one directory entry never count as distinct links. Link count and every entry identity must still match during revalidation.
Otherwise inode-attributed reclaim credit is zero because the plan has not established release of the inode. Possible links outside scan scope also make the accounting evidence incomplete. A changed link count or identity invalidates the action and returns it to review; when observed during execution revalidation, it is an action failure and execution pauses under section 11.3.
TJ-SIZE-07. Snapshots, shared extents, open references, delayed release, and unsupported backing-store semantics do not change established inode-attributed credit, but make eventual physical reclamation uncertain. After partial execution or failure, observed credit is derived only from completed mutations. Reports distinguish apparent bytes, inode-attributed allocated bytes, and estimated eventual reclaimed bytes.
The unified view may present separate per-filesystem totals. It presents one cross-filesystem reclaim estimate only when runtime evidence establishes the backing-store relationship and prevents double counting; otherwise no additive device total is shown. Filesystem boundaries remain visible in details and scan progress.
8. Symbolic links and traversal
TJ-LINK-01. Traversal uses lstat semantics without following and does not follow directory symlinks.
A target reachable through an independently configured or physical root is scanned there and
deduplicated.
TJ-LINK-02. Version 1 supports the host Termux namespace rooted at / and one namespace for each installed
proot-distro, rooted at its recorded rootfs. Relative targets resolve from the symlink's parent
inside its namespace. Absolute host targets resolve from /; absolute proot targets resolve from
that distro's rootfs and never from host /. Parent components cannot escape the namespace root.
Arbitrary containers and adapter-defined target namespaces are unsupported in version 1.
TJ-LINK-03. Target resolution follows at most 40 symlink entries. A loop, depth exhaustion, missing namespace, or untrusted namespace root makes target status unknown, not broken. A broken symlink is eligible for manual review only after package ownership and the target-resolution namespace are established. Host-namespace absence does not make a proot link broken.
TJ-LINK-04. The symlink directory-entry identity and resolved target identity remain separate. Target resolution is classification evidence only and never mutation authority. Deleting a symlink deletes only its directory entry. No cleanup action resolves a selected symlink and mutates its target. Symlink loops cannot block or multiply a scan.
9. TUI
9.1 Compact view
TJ-UI-11 in UI_GUIDELINES.md defines the compact checklist, finding order, filters, and presentation. It projects Product-owned finding, classification, accounting, and selection semantics without redefining them.
9.2 Details
TJ-UI-12 in UI_GUIDELINES.md defines applicability, unknown-field, and finding-detail presentation. Browse results never become cleanup authority; TJ-PLAN-06 owns reviewed manifest authority.
9.3 Interaction
TJ-UI-01 through TJ-UI-09 in UI_GUIDELINES.md and its directory-browser/key-map sections define interaction. Product-owned selection, authorization, and mutation guarantees remain authoritative.
9.4 Layout and accessibility
TJ-TERM-01 through TJ-TERM-10 in UI_GUIDELINES.md define terminal, layout, decoding, accessibility, and output behavior.
9.5 Warnings and scan failures
TJ-CORE-09, TJ-UI-02, and TJ-UI-10 define visible incompleteness, warning summaries, and measured progress. UI_GUIDELINES.md defines their presentation.
10. Planning, dry-run, and confirmation
10.1 Canonical state model
This diagram is the one canonical state model. It projects existing requirements and defines no new behavior; each transition is owned by the requirements named below.
stateDiagram-v2
direction TB
finding: Discovered finding
eligible: Eligible candidate
recommended: Recommended candidate
selected: Selected
review: Reviewed plan
authorized: Authorized
executing: Executing
paused: Paused on failure
reconciled: Reconciled outcome
[*] --> finding: scan
finding --> eligible: classification
eligible --> recommended: high-confidence preselection
eligible --> selected: operator selection
recommended --> selected: selection
selected --> review: dry-run planning
review --> authorized: exact confirmation phrase
review --> selected: material plan change
authorized --> executing: revalidation
executing --> reconciled: observed result
executing --> paused: action failure
paused --> reconciled: abort or cancellation
paused --> executing: independent actions only
Transition ownership: classification and eligibility are owned by TJ-CLASS-01 through TJ-CLASS-16;
preselection by TJ-CLASS-15, with manual-only candidates never recommended (TJ-CLASS-13); selection
and its bulk scope by TJ-UI-04 and the interaction rules in UI_GUIDELINES.md;
planning and plan identity by TJ-PLAN-01 through TJ-PLAN-08; authorization solely by the exact
confirmation phrase (TJ-PLAN-09, TJ-PLAN-10); execution by TJ-EXEC-01 through TJ-EXEC-05; failure
pause, independent continuation, and reconciliation by TJ-FAIL-01 and TJ-FAIL-03. Cancellation at
any stage reconciles observed state through TJ-FAIL-03 and TJ-UI-06. No path from finding to mutation
bypasses the reviewed plan and its exact confirmation phrase.
TJ-PLAN-01. Selection leads to a deterministic review plan: identical retained findings, configuration, adapter state, and selection produce the same ordered actions and effect sets.
TJ-PLAN-01. A plan is an immutable fixed-capacity snapshot owned by one scan generation. Actions are stored in canonical execution order and identified by their array index. Dependencies reference only earlier indexes, making the plan a bounded directed acyclic graph. Each action is tagged as direct filesystem, package removal, package-cache, or other adapter action.
TJ-PLAN-02. Canonical execution order uses a deterministic topological sort. Explicit dependencies always win. At each step, the next dependency-ready action is the least key in this order: package removal, package-cache, other adapter, direct filesystem; then ownership domain or filesystem identity; then category identifier; then raw target identity bytes; then canonical action bytes. Actions with identical canonical bytes collapse as duplicates before ordering. Discovery, completion, adapter, and configured-root order never enter the key. Display headings and grouping never change execution order.
TJ-PLAN-03. A direct filesystem action contains selected-root identity, raw path bytes, ancestor snapshots, leaf identity, complete manifest, expected earlier-plan effects, estimate, and evidence. A package action contains ownership domain, manager instance, exact executable and argument vector, reviewed manager state, effect set, capability evidence, and applicable network scope. No plan field references mutable scan, adapter, or UI storage.
TJ-PLAN-04. A dependency exists only when an earlier action is a prerequisite or may change state reviewed by a later action. Each expected effect records its source action index, affected entry identity or manager-state key, reviewed before-state, and exact expected after-state. Revalidation accepts a difference only when it exactly matches an expected effect from a successfully completed dependency. Unexpected, missing, additional, or ambiguous effects invalidate the later action. Package expectations remain manager-level effect-set changes; direct filesystem expectations are entry-specific and never authorize a new path or identity. A dependency cycle makes the plan invalid and unconfirmable.
TJ-PLAN-05. The canonical plan encoding is versioned, fixed-endian, length-prefixed, padding-free, and preserves raw bytes and explicit unknown tags. Rendering, confirmation, logging, revalidation, and execution consume the same immutable plan snapshot.
TJ-PLAN-06. A directory manifest header contains selected-root identity, selected-directory identity, filesystem
and mount scope, generation, action index, entry count, capacity, and completeness state. Each entry
contains its entry and parent indexes, raw basename bytes, device, inode, type, symlink status, link
count, size, modification timestamp and resolution, accounting-confidence state, expected unlink
or rmdir mutation, and applicable expected earlier-action effects. Parent indexes reconstruct the
reviewed raw path; no separately stored display or authority path may diverge from that chain.
For an individually selected directory, every descendant, including empty directories and manual-only descendants, must fit and be exposed in the review plan. Bulk selection never grants this descendant authority. Completeness requires successful bounded enumeration and metadata collection for every reviewed directory with no omission, cancellation, or exhausted capacity. An incomplete manifest remains reviewable but makes the action unavailable for confirmation and execution. New descendants never join execution automatically.
TJ-PLAN-07. Manifest execution order is deepest-first; equal-depth entries sort by raw path bytes ascending.
Symlink target data is classification evidence and never manifest mutation authority.
The review view groups package actions by ownership domain and manager instance, then action kind;
direct filesystem actions by filesystem identity, then category; and other adapter actions by adapter
identity, then category. Unknown ownership or filesystem identity uses an explicit unknown group
sorted after known groups. Empty groups are omitted.
Canonical execution order is preserved within each group. Grouping never changes execution order or dependency indexes, and cross-group dependencies remain visible on both affected entries. Group headers show action count, known reclaim estimate, unknown or incomplete state, warning count, and side-effect count. Collapsing a group is presentation-only and never changes plan identity.
Every expanded plan entry shows action index, kind, availability, dependency indexes, reason, evidence, risk, estimate state, warnings, expected side effects, and expected earlier-action effects.
A direct filesystem entry additionally shows the human-readable path and lossless byte-escaped raw path; selected root; filesystem, mount, device, inode, type, symlink state, and link count; allocated and apparent size; modification time and timestamp resolution; manifest count, capacity, completeness, and mutation order; exact descriptor-relative operation; and residual race disclosure.
A package entry additionally shows canonical package identity and contributing adapter provenance; ownership domain; manager instance; package version or slot; manager and runtime version; effective configuration; exact executable and individually escaped argument vector; reviewed manager state and complete effect set; indirect operations, hooks, cache scope, and network scope; state-binding method and capability evidence; and the exact reason when planning or execution is unavailable. Display escaping never changes raw bytes used by revalidation or execution.
TJ-PLAN-08. Dry-run performs all discovery, dependency resolution, read-only planning that adapters support, and revalidation without requesting filesystem mutation except for the configured run log. Dry-run and mutation-enabled planning are equivalent only when their canonical serialized plans are byte-identical for unchanged inputs. Both modes render and retain that same review plan. A dry-run may access the network. Read-only operations can cause kernel-managed access-time updates on filesystems that maintain them; this platform effect is disclosed and is not treated as cleanup mutation.
A package manager's --dry-run, --simulate, or similarly named option is not evidence that the operation is read-only.
Read-only planning capability is established separately for each adapter operation and supported manager version under the effective configuration.
This includes manager startup and shutdown behavior such as log rotation, cache initialization, runtime compilation caches, lock creation, and temporary files.
If all manager-requested filesystem mutation other than the configured run log cannot be prohibited, that planning operation is unavailable and is not invoked.
TJ-PLAN-09. Immediately before final confirmation, adapters refresh any package-manager plan whose state may have changed. The final confirmation is short and terse but keeps every required field present:
- the exact total action count;
- action counts in fixed order for package removals, package-cache operations, direct filesystem actions, and other adapter actions;
- the direct-filesystem manifest entry count when applicable;
- estimated reclaimable space;
- explicit
yesornovalues for package uninstall and permanent deletion; - warning and uncertainty counts;
- that deletion is permanent when applicable;
- the exact
CONFIRMinstruction.
A reclaim estimate is shown as an exact value, unknown, <value>, incomplete, or
<value>, eventual release uncertain, as applicable. Saturated warning or uncertainty counters show
at least N, where N is the counter maximum. Unknown estimates do not block confirmation because
their uncertainty is explicit. Unknown action scope, identity, manifest, ownership, or effect set
makes the plan unconfirmable because execution authority is incomplete.
TJ-PLAN-10. The operator confirms by typing the exact case-sensitive ASCII phrase CONFIRM followed by Enter.
Input is bounded to seven phrase bytes; an eighth printable byte clears it. Backspace edits it.
Enter on a mismatch clears the input and remains on confirmation. Escape or Ctrl-C clears it and returns to review.
Confirmation input begins only after the complete prompt has been submitted to terminal transport. Entering confirmation discards previously buffered input, and only input events decoded for the current confirmation generation contribute characters. Bracketed paste, mouse input, escape sequences, and stale replay events never contribute. Repetition of one key cannot form the phrase. Any plan-generation change clears entered text and returns to review.
Legacy terminals do not distinguish unmarked paste, macros, or synthetic input from the same bytes produced by typing. Those bytes therefore remain indistinguishable; help discloses this terminal limit instead of claiming universal paste detection.
TJ-PLAN-11. Confirmation applies only to the displayed plan. Two plans are materially equivalent only when all operator-visible and execution-relevant fields are identical. This includes ordered actions and dependencies; action kinds and target paths; target identities, ancestry, manifests, and mount scope; package state, effect sets, hooks, cache scope, executables, and argument vectors; reclaim estimates and uncertainty; warnings, risk, availability, side effects, provenance, and evidence.
Terminal dimensions, text reflow, focus, scrolling, expanded rows, color, and ASCII presentation do not affect material equivalence because they do not change the reviewed plan. Any other observed plan change returns the operator to review rather than widening consent.
Broad package-manager operations, including whole-cache cleanup, are available only when operation-specific evidence shows that execution cannot silently include effects added after review. Otherwise the operation is unavailable under the exact-plan guarantee. When the plan contains direct filesystem deletion, confirmation also discloses the non-atomic revalidation limit from section 11.1.
11. Execution safety
11.1 Filesystem revalidation
TJ-EXEC-01. Every selected root, reviewed ancestor, and leaf check compares the raw path component, filesystem identity, mount ID, device, inode, type, symlink status, link count, size, modification timestamp and resolution, adjusted only by exact expected earlier-action effects.
TJ-EXEC-02. Before every mutation, execution performs this sequence:
- reopen and validate the selected root;
- reopen each reviewed ancestor from that root with descriptor-relative no-follow semantics;
- validate each ancestor before descending;
- validate the leaf without following it;
- for a directory, compare the complete reviewed child set;
- perform one final parent-and-leaf metadata check;
- immediately issue the descriptor-relative mutation.
Steps 6 and 7 are adjacent filesystem effects in the execution trace. All mutation arguments are prepared beforehand; no event processing, yielding, other application effect, or unrelated work intervenes.
TJ-EXEC-03. A regular file uses a descriptor-relative no-follow metadata check followed by unlinkat. A symlink
uses AT_SYMLINK_NOFOLLOW metadata and unlinks only its directory entry. A directory is opened with
no-follow semantics, checks its complete child set, and uses unlinkat with AT_REMOVEDIR only after
its manifest children. Other entry types remain visible but direct mutation is unavailable in
version 1.
TJ-EXEC-04. A retained descendant-parent descriptor is never ancestry evidence. Recursive directory deletion reconstructs every ancestry chain from the selected-root descriptor and mutates only manifest entries in manifest execution order. It does not escape through implementation-controlled path or symlink traversal. A new, missing, moved, duplicated, materially changed, or cross-mount entry invalidates the whole directory action.
TJ-EXEC-05. An unsupported required check makes the finding's action unavailable before confirmation. An unexpected difference or unsupported check observed after execution starts fails the action and pauses execution. Changes exactly recorded as effects of successfully completed dependencies are expected rather than mismatches.
Linux and POSIX provide no atomic “unlink this name only if it still has this identity and reviewed ancestry” operation.
Revalidation and unlinkat are therefore separate system calls: a process with the operator's permissions can replace a directory entry after the final successful check but before deletion, causing the same-name replacement entry to be removed. It can also move a reviewed ancestor after the ancestry check, causing a reviewed entry to be deleted at a location outside the reviewed tree.
Version 1 does not claim protection against these final-interval races or against a deliberately adversarial process running with the operator's permissions.
This residual risk is shown in direct-filesystem-action details and in final confirmation;
package-manager actions remain governed by the owning manager's concurrency behavior.
Direct-action details use this text:
warning: direct deletion has a residual POSIX race. After final checks and before
unlinkat, a process with your permissions can replace the selected name or move a reviewed ancestor. Janitor may then delete the same-name replacement or the reviewed entry at its moved location.
Final confirmation uses this shorter text:
warning: direct filesystem deletion is non-atomic. A concurrent same-permission replacement or ancestor move after revalidation can cause deletion outside the reviewed identity or location.
11.2 Package actions
TJ-ADAPT-13. Package actions use the owning manager's supported mechanism because that manager owns transaction semantics and installed state. The core never reimplements package transactions or directly deletes installed-package files.
TJ-ADAPT-06 through TJ-ADAPT-13 in REQUIREMENTS.md own planning, effect binding,
process invocation, result normalization, Android confirmation, and observability. The exact-plan
guarantee ends at the reviewed manager-level effect set, not an invented file-level manifest.
Details and results identify manager-controlled or unobservable effects without claiming file-level
completeness.
11.3 Failure behavior
TJ-FAIL-01. On any non-logging action failure, execution pauses. The TUI shows the failed action and concise reason, then asks whether to continue with independent remaining actions or abort. An action is independent only when it has no direct or transitive dependency on a failed, cancelled, skipped, partial, or uncertain action. Those outcomes block every dependent action. Continuing requires an explicit operator choice and admits only independent actions that still pass revalidation. There is no resume-across-restarts feature in version 1. Run logs are historical audit evidence only. The tool never persists executable plans, selections, candidate state, manifests, dependency cursors, confirmation state, cancellation handles, or generation storage, and startup never reconstructs mutation authority from a log.
TJ-FAIL-02. After a crash or restart, the tool begins a new scan generation and requires discovery, selection, review, revalidation, and confirmation again. Previous effects may appear only through freshly observed filesystem or manager state; they are not imported as expected effects. Previous incomplete or uncertain records remain history and warnings. Old logs cannot authorize, skip, deduplicate, or continue an action. Log retention and viewing are independent from resume behavior.
TJ-FAIL-03. Cancellation stops new mutation admission on the next event-loop turn. An already admitted syscall
or manager transaction retains its reserved completion and audit slots, and the UI shows
cancellation requested; action outcome pending until observed completion. Outcome classification
uses observed state, never event arrival order.
If mutation occurred, the tool appends and flushes its result, updates observed accounting and affected findings, invalidates the remaining reviewed plan, and reports that cancellation did not prevent the admitted mutation. If no mutation occurred, it records cancellation without an observed effect. A partial or unobservable manager outcome is uncertain and requires fresh discovery before another plan. The tool never attempts automatic rollback. Cancellation offers no continuation into remaining mutations; after reconciliation the operator returns to review or quits. The generation and its storage remain live until every admitted action is reconciled.
TJ-LOG-01. The run log is created and opened during initialization with append-only, no-follow, and close-on-exec semantics. If the file is created, initialization flushes the file and its parent directory before scanning. Open, creation, or initial flush failure enters review-only mode: scanning and dry-run discovery remain available, every mutation action is unavailable, and a warning remains persistent. One descriptor remains open for the run; the log is never rotated or replaced during that run.
TJ-LOG-02. Complete initialized intent-frame, result-frame, and audit-event capacity is reserved before action admission. This reserves bounded application storage, not filesystem space. If complete authority or observed-effect data cannot fit, the action is unavailable before admission. Diagnostic truncation is permitted only when authority and observed-effect fields remain complete.
TJ-LOG-03. Every complete frame uses bounded partial-write handling. An intent frame is flushed with
fdatasync; fsync is accepted only when fdatasync is unsupported and fsync succeeds. Mutation
admission occurs only after that flush succeeds. Every result frame is flushed by the same rule.
EINTR retries use a compile-time bound, and exhaustion is a failure. Any other write or flush
failure blocks mutation admission or pauses execution after mutation. Unsupported flush semantics on
the logging filesystem make all mutation unavailable.
Here, durable means only that the kernel accepted fdatasync or fsync. The tool does not claim
survival against dishonest hardware, firmware, kernel, or filesystem behavior. When logging is
writable, dry-run uses the same logging and durability path because its run log is the sole
permitted requested mutation. Without writable logging, dry-run remains review-only.
TJ-LOG-04. The run log is an append-only sequence of framed UTF-8 JSON records. Each frame is one line:
<decimal payload length> <eight lowercase CRC-32/ISO-HDLC hex digits> <single-line JSON payload>\n
The checksum detects torn or corrupted records, not malicious modification. Each common payload contains schema version, monotonic record sequence, run and scan-generation identifiers, record type, realtime timestamp, canonical plan version, action index, and action kind.
TJ-LOG-05. An intent record additionally contains the exact bounded action snapshot; dependencies and completed prerequisite states; losslessly escaped target bytes; filesystem operation or package executable and argument vector; reviewed manifest or manager effect set; expected earlier effects; and estimate and uncertainty state. Authority fields are never truncated. An action whose complete intent cannot fit is unavailable for confirmation.
A result record additionally contains success, failure, cancellation, partial, or uncertain status; observed filesystem mutations or manager-level effects; applicable exit status or signal; bounded diagnostics and truncation state; resulting accounting state; and audit-completeness state.
Record sequences increase strictly. Exactly one appended and flushed intent precedes every mutation admission, and exactly one result append is attempted after every admitted action. A partial, malformed, or checksum-invalid frame is not a record and marks the audit log incomplete. Every field and record has a compile-time bound. Only diagnostics may be truncated, always explicitly. Paths and arguments are losslessly escaped. Credentials, payloads, unrelated environment values, and unrestricted child output are forbidden.
TJ-LOG-06. Intent append, partial write, retry exhaustion, or flush failure admits no mutation, marks the log incomplete when a partial frame exists, and disables mutation for the rest of the run. The tool never truncates or repairs a partial record automatically.
Result append or flush failure after mutation pauses execution, marks the observed result and audit log uncertain, and disables every further mutation because the mutation cannot be undone merely to repair logging. Logging failures never offer continuation with independent actions; only review and quit remain usable. Disk-full, quota, read-only remount, disconnect, and descriptor failures use these same fail-closed paths.
12. Configuration and state
TJ-CONFIG-01 through TJ-CONFIG-07 in REQUIREMENTS.md and
CONFIGURATION.md define location, grammar, precedence, fields, and startup
errors. Invalid safety-relevant configuration never starts a scan.
The tool stores configuration and run logs only. It never stores scan indexes, file metadata caches, candidate lists, executable plans, resume state, or usage telemetry.
13. CLI surface
Exact executable, option, argument, and exit-status data is owned by
model/cli.zon. Build tooling generates typed CLI records, option identifiers, and
status constants from that registry.
The default invocation scans and opens the interactive TUI. Version 1 has no unattended cleanup interface. Interactive commands require terminal stdin and stdout and never degrade to unattended operation. Informational invocations bypass configuration, logging, terminal initialization, and scanning.
14. Adapter contract
Every package adapter satisfies the common contract, ecosystem minimum, effect-set schema, result
normalization, and process registry in ADAPTERS.md. Network admission remains
subject to section 4.3. Unsupported manager semantics are unknown and never inferred.
15. Acceptance criteria
Version 1 is complete only when every version 1 row in
REQUIREMENTS.md has passing required suites and recorded platform limitations.
The release acceptance set is:
- concepts and terminology: TJ-CONCEPT-01 through TJ-CONCEPT-03;
- traversal and capacity: TJ-SCAN-01 through TJ-SCAN-11 and TJ-LIMIT-01 through TJ-LIMIT-09;
- classification and recommendations: TJ-CLASS-01 through TJ-CLASS-16;
- accounting and links: TJ-SIZE-01 through TJ-SIZE-08 and TJ-LINK-01 through TJ-LINK-04;
- adapters and processes: TJ-ADAPT-01 through TJ-ADAPT-13;
- planning and execution: TJ-PLAN-01 through TJ-PLAN-11 and TJ-EXEC-01 through TJ-EXEC-05;
- failure and audit: TJ-FAIL-01 through TJ-FAIL-03 and TJ-LOG-01 through TJ-LOG-06;
- interaction and terminal: TJ-UI-01 through TJ-UI-12 and TJ-TERM-01 through TJ-TERM-10;
- configuration and CLI: TJ-CONFIG-01 through TJ-CONFIG-07 and TJ-CLI-01 through TJ-CLI-03;
- installed documentation: TJ-MAN-01 through TJ-MAN-07;
- build and release artifacts: TJ-ART-01 through TJ-ART-12;
- official Termux distribution: TJ-DIST-01 through TJ-DIST-13;
- core guarantees: TJ-CORE-01 through TJ-CORE-11.
A warning-only adapter, skipped suite, undocumented limitation, missing capable-platform result, or unmapped normative statement fails acceptance.
15.1 Recorded platform limitations
These are the deliberate platform limitations accepted for version 1. Each entry names the affected requirements; the registry points every affected requirement here, and the acceptance test reviews this section as part of the release set. Anything not listed here is not an accepted limitation.
PL-RACE— residual POSIX final-interval race. Revalidation andunlinkatare separate syscalls; a same-permission process can replace a name or move a reviewed ancestor after the final check. Disclosed in section 11.1 and both confirmation warnings. Affects TJ-EXEC-01, TJ-EXEC-02, TJ-EXEC-05.PL-ROOT-OPEN— application domains cannot open the filesystem root. On Android,openatof/fails withEACCES, so mutation ancestry revalidation anchors at the configured selected root and reopens every reviewed ancestor below it; no prefix above a configured root is ever opened. Disclosed in section 11.1 and recorded inEXPERIMENTAL_EVIDENCE.md. Affects TJ-EXEC-02, TJ-EXEC-04.PL-ATIME— access time is advisory only. Android and mounted filesystems may not maintain access time; it never establishes inactivity or disposability. Disclosed in section 6.1. Affects TJ-CLASS-14.PL-ST-BLOCKS— allocated size trusts reported blocks.st_blocksmetadata, compression, sparse holes, and allocation-unit rounding are reported values the tool does not reinvent. Disclosed inFILESYSTEM_CAPABILITIES.md. Affects TJ-SIZE-01, TJ-SIZE-03.PL-XFS-TOTAL— no cross-filesystem additive total. No portable runtime probe prevents double counting across filesystems; totals stay per-filesystem. Disclosed inFILESYSTEM_CAPABILITIES.md. Affects TJ-SIZE-05, TJ-SIZE-08.PL-FS-CAP— mounted-filesystem capability variance. Shared storage, FUSE, and mount daemons change capability results; confidence never survives refresh, remount, or a changed mount ID. Disclosed inFILESYSTEM_CAPABILITIES.md. Affects TJ-SCAN-05, TJ-SCAN-06.PL-MGR-CONCURRENCY— package-manager-owned concurrency. Transaction semantics and installed state belong to the owning manager; the exact-plan guarantee ends at the reviewed manager-level effect set. Disclosed in section 11.2. Affects TJ-ADAPT-07, TJ-ADAPT-13.PL-ANDROID-CONFIRM— Android confirmation outside tool control. System confirmation dialogs and their outcomes are user-controlled and unobservable to the core. Disclosed inADAPTERS.md. Affects TJ-ADAPT-09.PL-PROC-UNCERTAINTY— process uncertainty is observable, never rollback. Detached descendants, ignored signals, and indivisible syscalls remain recorded uncertainty. Disclosed inADAPTERS.md. Affects TJ-ADAPT-11.PL-LOG-DURABILITY— log durability is kernel acceptance.fdatasync/fsyncsuccess means kernel acceptance, not survival against dishonest storage. Disclosed inEXPERIMENTAL_EVIDENCE.md. Affects TJ-LOG-03, TJ-LOG-06.
16. Out of scope for version 1
- unattended or scheduled cleanup
- privilege escalation
- remote cleanup
- general Android application management outside Termux add-ons
- restoring deleted content
- moving selected content to trash
- resuming interrupted cleanup after process restart
- guaranteed accessibility for every terminal or assistive technology