Linux administrators compare GNU find age buckets with an exact seven-day timestamp cutoff on a file timeline.

Why find -mtime +7 Leaves Seven-Day-Old Files Behind

Thu, Oct 15, 2026

A retention preview can return exit status zero and still implement the wrong boundary. Consider a read-only scan intended to include every regular file whose modification time is at least seven 24-hour periods old. A familiar predicate, -mtime +7, omits a file that is seven and a half days old. The command has not malfunctioned. It has applied GNU find's rounded age-bucket rule rather than the policy the reviewer thought was authorized.

This playbook turns that ambiguity into a qualification problem. It defines one captured instant T, derives an inclusive cutoff exactly seven 86,400-second periods earlier, authors eight files around that boundary, and compares the legacy bucket predicate with a reference-file predicate. The review reconciles exact filename membership, makes equality visible at one-nanosecond distance, preserves a filename that contains a newline, and treats command failure as evidence rather than as an empty success.

The scope is deliberately narrow: GNU findutils 4.10.0, CPython 3.13.5, one disposable owned directory, and read-only selection. Nothing is deleted, copied, restarted, scheduled, or authorized for later action. The result can be ACCEPT_SELECTION_ONLY when all evidence agrees. It becomes REPAIR_PREDICATE when bucket semantics contradict the frozen cutoff, HOLD_POLICY when the business boundary is unresolved, or HOLD_EVIDENCE when the run cannot prove what it selected.

Write the time rule as an explicit inequality

Start with the policy, not the flag. For this fixture, the authorized rule is file_mtime <= T - 7 * 86400 seconds. The equality sign matters. A file stamped exactly at the cutoff belongs to the eligible set, as does a file one nanosecond earlier. A file one nanosecond later does not. Once the owner approves that inequality, the command can be judged against it without translating the result back into vague prose.

Three common statements are not interchangeable. “At least seven days old” can mean a fixed duration from a captured instant. “Older than the cutoff” is often read as strict inequality and would exclude equality. “Seven calendar days” may depend on local dates, midnight boundaries, daylight-saving changes, or a business end-of-day rule. This laboratory authorizes only the first meaning and makes it inclusive.

Policy wording

Machine rule

This protocol

At least seven 24-hour periods old

mtime <= T - 7*86400s

Authorized and tested

Strictly older than the cutoff

mtime < cutoff

Different boundary; requires another oracle

Seven local calendar days

Calendar/schedule rule

Out of scope; do not infer from this run

A review record should state the captured T, the integer cutoff, the derivation used to obtain it, the owner who approved the rule, and the representation used by the command. A human-readable UTC rendering is helpful for review, but the integer timestamp is the comparison authority in this fixture. Do not reconstruct the cutoff later from the report-open time or from a rounded date label. That would replace a frozen policy instant with a new estimate and make an otherwise identical command answer a different question.

The same discipline applies beyond this one command. Strong Linux and system-administration foundations begin with explicit ownership, observable state, and a reviewable rule. Here, uncertainty about inclusivity or duration is a policy defect, not something an operator should resolve by choosing whichever predicate looks familiar.

HOLD_POLICY: Use this decision when the owner has not settled inclusive versus strict comparison, or when “seven days” might mean calendar dates rather than seven 86,400-second periods.

Follow GNU find’s completed-day comparison

GNU find evaluates -mtime n in completed 24-hour periods. The documented interpretation of the age family says that the fractional part is ignored, and the numeric-prefix rule says +n means greater than n. Read those rules together. A file 7.5 days old has seven completed periods after truncation. Seven is not greater than seven, so -mtime +7 does not match it. A file 8.0 days old has eight completed periods, and eight is greater than seven, so it does match. The relevant positive-age threshold is therefore eight completed days, not seven elapsed days.

The bucket transition is easiest to see with four ages. At 6.999 days, truncation yields six; at exactly 7.0 days it yields seven; at 7.999 days it still yields seven; and at exactly 8.0 days it yields eight. The test -mtime +7 rejects the first three and accepts the fourth. This is why substituting the phrase “more than seven days” for the actual integer comparison is unsafe. The implementation first quantizes age and then applies the numeric-prefix operator.

The GNU find command reference, mirrored on man7.org, points -mtime to the rounding explanation for -atime and separately defines the +n, -n, and exact-n forms. This maintained manual describes the command contract; it is not an empirical test of the installed 4.10.0 binary. Without -daystart, ordinary age tests use 24-hour periods relative to the process’s time reference, not local midnight. The bucket result follows the documented rule rather than indicating a malfunction.

REPAIR_PREDICATE: Apply this decision when the authorized oracle is the inclusive frozen cutoff but the reviewed command uses -mtime +7. The command can execute successfully and still select the wrong membership.

Do not turn a moving relative test into a frozen policy instant

A relative age test is evaluated by each running process against its own notion of the current time. A preview at 09:00 and a review at 09:20 do not share one immutable T merely because the command text is identical. Changing the numeric form may alter a bucket boundary, but it does not preserve the captured instant across separate executions. That distinction matters when evidence must be replayed or compared later.

This protocol stores the approved cutoff as the modification time of a reference file and compares every candidate with that file. Relative predicates remain appropriate for policies that genuinely authorize relative completed-unit semantics. The repair is not “reference files are always better”; it is “represent the policy in the same form the owner approved.”

Author eight files around one captured cutoff

The fixture captures snapshot = time.time_ns() once and defines cutoff = snapshot - 7 * DAY, where DAY is exactly 86,400,000,000,000 nanoseconds. Eight regular files are then authored in one newly created directory. Their contents are small and distinct, but their important property is the intended modification time.

Display name

Authored mtime relative to T

Relation to cutoff

Inclusive oracle

young.txt

T - 6.5 days

Newer by 12 hours

Exclude

seven-half.txt

T - 7.5 days

Older by 12 hours

Include

eight-half.txt

T - 8.5 days

Older by 36 hours

Include

old\nrecord.txt

T - 9.5 days

Older by 60 hours

Include

equal.txt

cutoff

Exactly equal

Include

earlier.txt

cutoff - 1 ns

One nanosecond older

Include

later.txt

cutoff + 1 ns

One nanosecond newer

Exclude

future.txt

T + 1 day

Future relative to T

Exclude

The future-dated record is a deliberate negative control. It confirms that neither predicate should treat every authored file as historical merely because it exists in the fixture. The control does not diagnose why a real file might have a future mtime, and it does not turn this article into a clock-correction procedure. It simply requires the selection and its complement to place that known value on the newer side of the frozen cutoff.

The literal eligible set is therefore five names: seven-half.txt, eight-half.txt, old\nrecord.txt, equal.txt, and earlier.txt. The backslash-n form is only a safe display convention. The actual filename contains one newline character, encoded here as the byte 0x0A, and must remain one record in the evidence.

The driver creates cutoff.ref in the owned working directory but outside the searched files directory. Its modification time is set to the exact cutoff. Keeping it outside the scan root prevents the policy marker itself from entering the candidate inventory. The report records the captured snapshot, the derived cutoff, all authored values, and the expected names.

Require timestamp readback rather than assuming precision

Python’s os timestamp interface accepts integer nanoseconds through os.utime(..., ns=(atime_ns, mtime_ns)) and exposes st_mtime_ns for integer readback. The same documentation cautions that a system may not actually provide nanosecond precision. An API that accepts a number is not proof that the filesystem retained every low-order bit.

Case F1 compares all eight files’ baseline st_mtime_ns readbacks with their authored integers and reads the reference mtime for comparison with cutoff. These checks precede accepted selection evidence. Rounding would destroy the equality and one-nanosecond controls. On such a filesystem, use HOLD_EVIDENCE, not wider margins that silently weaken the oracle. The Python links describe the maintained 3.13 documentation branch; the commissioning interpreter was specifically CPython 3.13.5.

Run the complete read-only selection laboratory

Save the following source as find_age_fixture.py, using UTF-8 without a byte-order mark, LF line endings, and one final newline. Preserve indentation and blank lines. Its SHA-256 is c83a807d8549ce463de65e5ad7be5240fc38b17fb9fdafe71e10abe9ac4f0f7a. Save and run it in an owned writable directory with GNU find available on PATH:

python find_age_fixture.py

The driver checks that find --version identifies GNU findutils, creates a unique owned work directory beside the script with Python’s tempfile.mkdtemp, and executes find through argv lists with subprocess.run. No shell expands filenames or interprets !. Completed probes retain separate output streams and return codes. Each call sets timeout=10; process creation can delay timeout handling. The script records versions but does not enforce the exact 4.10.0 and 3.13.5 patch versions. Review those fields before claiming a matching-environment reproduction.

The nine cases include seven selection probes, F2–F8; F1 and F9 inspect filesystem state. All selection probes use -type f and -print0 and do not modify selected files. F2’s decisive 7.5-day and 8.5-day controls lie twelve hours either side of its eight-day threshold. After F9, a final check requires 0 <= time.time_ns() - snapshot < 60*1_000_000_000. This bounds the measured wall-clock interval from snapshot to that check, not setup before the snapshot or the later report write. It is not a performance result or a complete clock-jump detector.

The supplied commissioning report for this exact source records PASS_9_CASES, with started_utc equal to 2026-10-15T06:49:16.089528+00:00 and finished_utc equal to 2026-10-15T06:49:16.142408+00:00, in the stated GNU findutils 4.10.0 and CPython 3.13.5 scope. These report fields bracket the case phase after fixture creation, not the whole process lifetime. Treat this as one supplied local observation, not a benchmark or a cross-filesystem guarantee. Each rerun needs its own versions, timestamps, platform, source digest, and output.

The finally block attempts to write results.json when a case raises. Earlier failures in discovery, fixture creation, source hashing, or baseline inventory occur outside that block and can leave no report. A timeout or malformed NUL stream can also raise before a complete command record reaches its case row. A missing report, incomplete evidence, or report-write failure blocks acceptance. The small working directory remains available for inspection.

The report begins in FAIL state and appends each case before running its operation. An exception can leave a named FAIL row and an error type and message. Only after all nine cases and the final interval check succeed does the top-level status become PASS_9_CASES. Require successful driver completion and a readable, complete report as well as that field; setting it in memory does not prove the subsequent JSON write succeeded.

Keep expected results separate from observations. The source encodes the authored oracle and asserts what each case should produce. The commissioning result is supplied provenance, not something established by reading documentation. Static or syntax review is not a behavioral execution. Likewise, copying another machine’s output cannot show that a new filesystem retained the nanosecond values. Each rerun earns its own evidence.

"""Read-only GNU find age selection in an owned directory. No deletion commands."""
import hashlib
import json
import os
import platform
import shutil
import subprocess
import tempfile
import time
from datetime import datetime, timezone
from pathlib import Path
DAY = 86400 * 1_000_000_000
def require(ok, message):
    if not ok:
        raise RuntimeError(message)
def main():
    find = shutil.which('find')
    require(find is not None, 'GNU find is required')
    env = dict(os.environ, LC_ALL='C', TZ='UTC')
    version = subprocess.run([find, '--version'], capture_output=True, check=True, text=True, timeout=10, env=env).stdout.splitlines()[0]
    require('GNU findutils' in version, 'This protocol targets GNU find')
    work = Path(tempfile.mkdtemp(prefix='find-age-', dir=Path(__file__).parent))
    scan = work / 'files'
    scan.mkdir()
    snapshot = time.time_ns()
    cutoff = snapshot - 7 * DAY
    authored = {'young.txt': snapshot - 13 * DAY // 2, 'seven-half.txt': snapshot - 15 * DAY // 2, 'eight-half.txt': snapshot - 17 * DAY // 2, 'old\nrecord.txt': snapshot - 19 * DAY // 2, 'equal.txt': cutoff, 'earlier.txt': cutoff - 1, 'later.txt': cutoff + 1, 'future.txt': snapshot + DAY}
    expected = sorted(['seven-half.txt', 'eight-half.txt', 'old\nrecord.txt', 'equal.txt', 'earlier.txt'])
    reference = work / 'cutoff.ref'  # Kept OUTSIDE the searched directory.
    reference.write_bytes(b'fixed cutoff\n')
    os.utime(reference, ns=(cutoff, cutoff))
    for name, stamp in authored.items():
        path = scan / name
        path.write_bytes(('fixture:'+name).encode())
        os.utime(path, ns=(stamp, stamp))
    report = {'status':'FAIL', 'python':platform.python_version(), 'find':version, 'platform':platform.platform(), 'snapshot_ns':snapshot, 'cutoff_ns':cutoff, 'authored_mtime_ns':authored, 'expected_names':expected, 'cases':[], 'started_utc':datetime.now(timezone.utc).isoformat(), 'source_sha256':hashlib.sha256(Path(__file__).read_bytes()).hexdigest()}
    def inventory():
        return {p.name:{'mtime_ns':p.stat().st_mtime_ns, 'size':p.stat().st_size, 'sha256':hashlib.sha256(p.read_bytes()).hexdigest()} for p in scan.iterdir()}
    baseline = inventory()
    def run(predicate, environment=env, start=scan):
        args = [find, str(start), '-type', 'f', *predicate, '-print0']
        process = subprocess.run(args, capture_output=True, timeout=10, env=environment)
        record = {'argv':args, 'returncode':process.returncode, 'stderr':process.stderr.decode(errors='replace'), 'TZ':environment['TZ']}
        names = []
        if process.stdout:
            require(process.stdout.endswith(b'\0'), 'Truncated NUL inventory')
            names = sorted(Path(os.fsdecode(raw)).name for raw in process.stdout[:-1].split(b'\0'))
        record['names'] = names
        record['stdout_hex'] = process.stdout.hex()
        return record
    def case(identity, operation):
        row = {'id':identity, 'status':'FAIL'}
        report['cases'].append(row)
        operation(row)
        row['status'] = 'PASS'
    def selection(row, predicate, wanted, environment=env):
        row.update(run(predicate, environment))
        require(row['returncode'] == 0 and not row['stderr'], 'Incomplete command evidence')
        require(row['names'] == wanted, 'Wrong selected membership')
    try:
        def setup(row):
            require(reference.stat().st_mtime_ns == cutoff, 'Reference timestamp rounded')
            require(all(baseline[n]['mtime_ns'] == t for n,t in authored.items()), 'Filesystem did not retain the authored nanoseconds')
            row['files'] = baseline
            require(len(baseline) == 8, 'Wrong fixture size')
        case('F1', setup)
        case('F2', lambda r: selection(r, ['-mtime', '+7'], sorted(['eight-half.txt', 'old\nrecord.txt'])))
        case('F3', lambda r: selection(r, ['!', '-newer', str(reference)], expected))
        case('F4', lambda r: selection(r, ['-newer', str(reference)], sorted(set(authored)-set(expected))))
        case('F5', lambda r: selection(r, ['!', '-newer', str(reference)], expected, dict(env, TZ='Pacific/Honolulu')))
        def broaden(row):
            other = work / 'changed-cutoff.ref'
            other.write_bytes(b'new policy\n')
            os.utime(other, ns=(cutoff+DAY, cutoff+DAY))
            require(other.stat().st_mtime_ns == cutoff+DAY, 'Changed cutoff rounded')
            selection(row, ['!', '-newer', str(other)], sorted(set(authored)-{'future.txt'}))
        case('F6', broaden)
        def delimiters(row):
            selection(row, ['!', '-newer', str(reference)], expected)
            require('old\nrecord.txt' in row['names'] and len(row['names']) == 5, 'Filename split into multiple records')
            row['line_based_count_would_be'] = len('\n'.join(row['names']).splitlines())
            require(row['line_based_count_would_be'] == 6, 'Line-parser negative control failed')
        case('F7', delimiters)
        def missing(row):
            row.update(run(['!', '-newer', str(reference)], start=work/'absent'))
            require(row['returncode'] != 0 and row['stderr'], 'Expected path failure absent')
            row['decision'] = 'HOLD_NOT_AN_EMPTY_SUCCESS'
        case('F8', missing)
        def unchanged(row):
            row['after'] = inventory()
            require(row['after'] == baseline, 'Fixture contents or mtimes changed')
            require(reference.stat().st_mtime_ns == cutoff, 'Original cutoff changed')
        case('F9', unchanged)
        # Relative probes have twelve-hour margins; reject a long/suspended run.
        require(0 <= time.time_ns()-snapshot < 60 * 1_000_000_000, 'Wall-clock movement or excessive delay invalidates relative-age controls')
        require(len(report['cases']) == 9, 'Incomplete cases')
        report['status'] = 'PASS_9_CASES'
    except Exception as error:
        report['error'] = {'type':type(error).__name__, 'message':str(error)}
        raise
    finally:
        report['finished_utc'] = datetime.now(timezone.utc).isoformat()
        (work/'results.json').write_text(json.dumps(report, indent=2)+'\n')
        print(work/'results.json')
if __name__ == '__main__':
    main()

A case marked PASS means the case observed its expected condition. That condition may be an intentionally rejected predicate or an expected command failure. Operational acceptance comes only from the decision rules and the complete report, not from counting green case labels.

Compare the legacy bucket with the frozen reference result

Case F2 runs the legacy predicate -mtime +7. With the wide-margin fixture, its expected membership is only eight-half.txt and the single newline-bearing file displayed as old\nrecord.txt. The 7.5-day file remains in bucket seven after truncation, so it does not satisfy “greater than seven.” The exact-cutoff boundary files also remain outside this relative result.

Case F3 runs ! -newer cutoff.ref. Because the reference carries the frozen cutoff and the predicate is negated, the expected membership is the five-name literal oracle. Both commands can return zero, write complete NUL-delimited output, and still answer different questions.

Case

Predicate

Expected names

Operational reading

F2

-mtime +7

eight-half.txt; old\nrecord.txt

Expected legacy result; policy mismatch

F3

! -newer cutoff.ref

seven-half.txt; eight-half.txt; old\nrecord.txt; equal.txt; earlier.txt

Matches inclusive frozen cutoff

REPAIR_PREDICATE: F2 can pass as a diagnostic control while the reviewed selection remains unacceptable for the authorized seven-period inclusive cutoff. F3 is the candidate repair.

Compare membership after parsing, not traversal order. The driver sorts basename records and compares the complete list with the expected list; sorting does not remove duplicates or excuse missing names. Basenames identify files unambiguously only in this flat fixture. Reusing this parser for nested directories would lose parent-path identity and require a different oracle. Here the contract is exact membership, not ordering.

Show the missing records rather than only different totals

A count of two versus five reveals a discrepancy but not its meaning. Reconcile names. The legacy bucket omits seven-half.txt, equal.txt, and earlier.txt. Those records show three distinct defects relative to the oracle: a plainly eligible 7.5-day file, equality at the policy boundary, and the immediately older side of that boundary.

This article stops at selection. The separate question of why adding a deletion action needs separate validation involves traversal and action semantics that are intentionally absent here. Do not import a deletion command into this fixture merely because the repaired predicate selects the intended names.

Make equality visible with complementary predicates

Case F4 runs the positive predicate -newer cutoff.ref. GNU find’s strict reference-time comparison succeeds only when the candidate modification time is more recent than the reference and fails at equality. Negating that strict test yields “not newer,” which includes both older timestamps and equality.

For the eight authored names, F4 must select exactly young.txt, later.txt, and future.txt. F3 and F4 should be disjoint, and their union should cover all eight names. That partition is a stronger boundary check than observing equal.txt in F3 alone because it also proves the equality record did not leak into the strict-newer side.

Complement reasoning helps establish coverage only when the intended inventory domain is known. Controlled creation establishes eight regular files; F1 checks their baseline count and authored mtimes. F3 and F4 must match the corresponding literal and complementary name lists. On an unknown or partially traversed tree, disjoint outputs alone would not prove coverage. A missing start point or traversal error could leave names outside both lists.

The independent literal oracle remains primary. Complementarity helps diagnose the boundary, but it cannot authorize inclusivity. If the product owner instead approves mtime < cutoff, equality must move to the ineligible side and the expected sets must be rewritten. Reusing this report while changing the word “inclusive” would be HOLD_POLICY, not a harmless documentation edit.

Keep timezone changes separate from epoch comparisons

Case F5 repeats ! -newer cutoff.ref with TZ=Pacific/Honolulu while keeping the same reference-file timestamp and the same authored files. The expected five-name membership does not change. The comparison is between stored modification-time instants, not between formatted local clock labels. Setting TZ may change how a reporting tool renders a human-readable timestamp, but it does not rewrite the reference mtime used by this predicate.

For durable evidence, keep epoch nanoseconds and timezone-qualified human timestamps side by side. The integer values support exact comparison; the rendered timestamps help people review chronology. A report that stores only a local wall-clock string can become ambiguous when copied to another zone or when an offset rule changes. Conversely, an integer alone is harder to inspect for obvious policy mistakes. The two representations serve different review needs and should not be conflated.

The control is intentionally bounded. It demonstrates that this exact instant-comparison workflow does not derive its selection from a local calendar zone. It does not prove that every surrounding system, scheduler, log parser, or policy document is timezone-neutral. The report records the environment used for each command so that a reviewer can distinguish the normal UTC cases from the Honolulu control.

The final check measures a net wall-clock difference, not clock stability. Python’s wall-clock documentation allows backward adjustments. A backward step makes this difference negative only when the final reading falls below the snapshot: thirty elapsed seconds offset by a twenty-second backward step would still produce ten seconds. Compensating jumps can also escape two endpoint readings. A negative result or a result of sixty seconds or more fails the check. Any known or suspected clock discontinuity, abnormal suspension, or unexplained timing requires HOLD_EVIDENCE even when the arithmetic passes. No independent monotonic-duration measurement is included in this driver.

A seven-day duration is not a local-calendar schedule

Seven groups of 86,400 seconds are a duration. Seven local dates are a calendar rule. Around daylight-saving transitions, a local day may not contain 86,400 elapsed seconds, and “retain through the seventh day” may require an end-of-day boundary rather than a precise duration from T. This fixture does not choose among those business meanings.

When the requirement mentions midnight, business days, local dates, month boundaries, or a scheduled batch window, stop and construct another policy oracle. F5 supports only the fixed epoch-timestamp comparison. It neither validates a calendar schedule nor tests the installation’s timezone database: passing TZ=Pacific/Honolulu is an environment control, not proof that a reporting tool rendered Honolulu time.

Treat a changed cutoff as a policy change

Case F6 creates a second reference file at cutoff + DAY, six 24-hour periods before the original snapshot. No candidate content or mtime changes. The expected inclusive set broadens to seven files, every authored file except future.txt. Both young.txt and later.txt become eligible although the original five-name decision excluded them. This is a changed policy boundary, not a repair to file data.

This is why a reference file is evidence, not merely a convenient command argument. Its identity and exact mtime belong in the review record. Replacing it changes the policy represented by the command. The driver preserves cutoff.ref and its original result, then records the broadened control separately with changed-cutoff.ref.

The reference file’s bytes are a human-readable label; its mtime is the comparison operand. The label fixed cutoff does not encode that timestamp. A reference with identical contents but another mtime represents another boundary. F6 checks that the second reference retains cutoff + DAY; its report row retains that reference’s path in argv and its selected names. The exact changed timestamp is recoverable from the recorded original cutoff and source formula, not from a separate readback field in that row.

Do not confuse this one-file-versus-policy comparison with a different source-versus-destination timestamp policy. An rsync decision can depend on the relationship between two copies and receiver-side options. This find laboratory compares each candidate mtime with one frozen policy marker and performs no copy.

HOLD_POLICY: If a reviewer cannot identify which reference timestamp was authorized, or if a new cutoff is substituted without a fresh expected set, the old PASS cannot support the new selection.

Preserve a newline-bearing filename as one record

Filenames are byte sequences subject to filesystem and interface rules; a newline is not a universal record separator inside a pathname. The fixture intentionally creates one regular file whose decoded name is old\nrecord.txt in display form. In the directory, that is one entry containing an actual newline between “old” and “record.txt.” A line-oriented preview can make it look like two records.

GNU find’s NUL-delimited pathname output keeps an embedded newline inside its pathname record. Case F7 checks that nonempty output ends in NUL, splits only on NUL bytes, then decodes complete records. The five selected names include the newline-bearing name once. The report retains stdout_hex as a lossless hexadecimal representation of captured bytes. A terminal NUL alone cannot detect output cut short exactly between records; command status and the literal membership oracle remain necessary.

The negative control joins the five decoded names, including the actual embedded newline, with newline separators and calls splitlines(). That naive representation produces six lines because it mistakes the embedded newline for another record boundary. It does not join the safely escaped display strings used in this article. The incorrect count demonstrates why “five lines” and “five pathnames” are different claims.

This fixture stays within ASCII names plus one newline, so it does not claim to qualify every possible undecodable pathname byte sequence. The use of byte capture and NUL framing is still important: decoding happens only after record boundaries are established. That order avoids asking a text decoder or a line parser to decide where one pathname ends. Broader pathname-encoding coverage would need additional authored cases and an explicit serialization policy for the JSON report.

Keep the actual pathname unchanged and escape it only for presentation in prose or tables. Renaming the file would remove the parser failure this case exposes. The driver rejects a nonempty stream without its terminal NUL before constructing names. When framing passes, the full expected-name comparison still has to establish membership; a plausible fragment is not an accepted inventory.

Keep an unreadable or missing scan from becoming empty success

Case F8 changes the start point to an intentionally absent path. GNU find is expected to write a diagnostic to standard error and return a nonzero status. Standard output may be empty, but that empty byte string is not an inventory of a successfully scanned empty directory. The case records HOLD_NOT_AN_EMPTY_SUCCESS to make that distinction explicit.

A failing traversal can emit some pathnames before it reports an error. That partial stream is more dangerous than an empty one because it can resemble a plausible inventory. The driver therefore does not accept a list merely because every parsed name is individually well formed or because the names happen to be a subset of the expected result. Process success and complete framing are preconditions for any membership comparison.

The GNU manual’s exit-status requirements say that nonzero status indicates an error and that the results should not be relied upon. Processing may stop before every starting point is examined. This fixture additionally requires empty stderr, valid NUL framing, and exact expected names for successful-selection cases. Empty stderr is the fixture’s stricter evidence rule, not a claim that every GNU find warning necessarily produces nonzero status.

This evidence rule shares a narrow lesson with preserving a failing command’s exit status: output is not enough when the producer failed. The implementation here avoids the separate Bash pipeline problem entirely by calling GNU find through argv and capturing its return code directly. It does not need PIPESTATUS or a shell option to recover the producer status.

HOLD_EVIDENCE: Use this decision for a missing root, an unreadable traversal that returns nonzero, unexpected stderr, a timeout, truncated NUL output, or any other condition that prevents a complete command record.

Inventory completeness requires process evidence as well as filenames

A completed probe record retains argv, return code, stderr text, stdout bytes as hexadecimal, parsed names, and timezone. Stderr uses replacement decoding, so it is diagnostic text rather than byte-exact evidence. The raw stdout remains recoverable. If timeout or parsing failure prevents those fields from being attached, the case is incomplete and held; the driver does not serialize every possible failure’s partial output. F8 specifically verifies the completed missing-path failure record.

F8 is a small negative control, not a comprehensive simulation of permission changes, network filesystems, I/O errors, disappearing entries, or every traversal fault. Its purpose is to block the most dangerous review shortcut: treating “no names on stdout” as proof that no files matched.

Confirm the selection left file bytes and mtimes unchanged

A read-only promise should be checked against the fields the protocol claims to preserve. Before the find cases, the driver inventories each authored file’s size, SHA-256 digest, and st_mtime_ns. Case F9 rebuilds that inventory and requires exact equality with the baseline. It also reads back the original cutoff.ref mtime to confirm that the policy marker did not move.

The before-and-after inventories are observations at two points, not continuous monitoring. A transient write that is later reversed might escape this check, and a deliberate actor could restore content and mtime. Those threat models are outside the owned, single-process fixture. The value of F9 is narrower: it catches changes introduced by this protocol or its immediate environment that remain visible at the final inventory.

The Python hashlib interface supplies the SHA-256 calculation. Matching digests and sizes provide evidence that the bytes read at the two inventory points are unchanged; the driver does not compare the two byte strings directly. Hash equality is not proof against every possible collision. It also does not authenticate who set the timestamp, establish historical content age, preserve every metadata field, or authorize deletion.

Hashing reads file data. Under Linux’s access-time update rules, reads may affect atime depending on filesystem and mount settings; atime is outside this contract. The case does not test concurrent writers, path replacement, symlink races, or the interval before a later action. Describe a passing result precisely: the authored files’ measured sizes, SHA-256 digests, and modification-time nanoseconds matched at the two inventory points.

Accept selection only after all nine checks agree

The nine cases form one qualification record. F1 establishes whether the fixture can express the one-nanosecond oracle. F2 confirms the legacy behavior that motivates repair. F3 is the repaired inclusive selection. F4 checks its strict-newer complement. F5 separates timezone presentation from the epoch comparison. F6 proves that replacing the policy timestamp changes membership. F7 protects pathname framing. F8 preserves failure evidence. F9 checks the read-only fields promised by the protocol.

Case

Qualification check

Expected evidence

Decision contribution

F1

Exact timestamp readback

8 files plus cutoff retain authored ns values

Else HOLD_EVIDENCE

F2

Legacy age bucket

2 names: eight-half.txt; old\nrecord.txt

PASS control, REPAIR_PREDICATE

F3

Inclusive frozen cutoff

Exact 5-name oracle

Candidate selection

F4

Strict-newer complement

3 names; disjoint union covers all 8

Boundary confirmation

F5

Timezone control

Same 5 names under Pacific/Honolulu

Instant comparison confirmed

F6

Changed policy marker

7 names; future excluded

Old PASS not reusable

F7

NUL framing

5 pathnames; naive line count 6

Framing and exact-name check

F8

Missing start path

Nonzero status plus stderr

HOLD_EVIDENCE; not empty success

F9

Read-only fields

Size, SHA-256, mtime_ns unchanged

Checked fields match at endpoints

Evaluate the matrix as one run, not as a menu of favorable rows. F3 cannot inherit F1 from an older filesystem check, F7 cannot substitute for a missing return code, and F9 cannot repair an incomplete selection. The source digest, snapshot, cutoff, case records, and final status tie the evidence together. Cherry-picking a five-name list from one report and a clean exit from another produces no qualified run.

The final decision is ACCEPT_SELECTION_ONLY only when the driver completes successfully, the saved report is readable and says PASS_9_CASES, all nine named cases pass, the digest identifies the reviewed source, runtime metadata matches the claimed scope, F1 confirms timestamp retention, F2–F8 have complete command records, the measured interval passes, and F3 matches the literal oracle. Known or suspected clock disruption still requires a hold; the interval check alone cannot certify stability.

Use REPAIR_PREDICATE for F2 under this policy even though its diagnostic assertion passes. Use HOLD_POLICY for unresolved inclusivity, cutoff ownership, or duration meaning. Use HOLD_EVIDENCE for rounding, suspect timing, missing roots, timeouts, unexpected status or stderr, malformed or incomplete output, or an unavailable report. F8’s expected failure validates the diagnostic control; it does not turn its absent-root scan into a successful selection.

ACCEPT_SELECTION_ONLY: Approval is limited to the demonstrated read-only membership rule for the captured cutoff and fixture conditions. It does not grant permission to remove, copy, archive, compress, or otherwise act on selected paths.

Keep deletion authorization outside this laboratory

This laboratory contains no destructive operation, cleanup loop, or scheduling recipe. Such a workflow would introduce separate questions about ownership, permissions, changing directory contents, path replacement, action ordering, retries, recovery, and the interval between preview and action. Those controls cannot be inherited from a read-only selection report.

Do not append a convenient deletion command after F3. That would change both the technical scope and the authorization claim, and it would overlap a different validation problem. The bounded conclusion is simply that the reference predicate selected the authorized synthetic membership under the recorded conditions.

Assign cutoff ownership and repeatable review responsibilities

The policy owner defines T, the seven-period duration, and whether equality belongs in the eligible set. The operator owns the captured reference file, the start path, the GNU find identity, the driver digest, and the actual command records. QA owns the literal expected membership, complement checks, delimiter control, failure control, timestamp preconditions, and the decision matrix.

Preserve the report whenever the cutoff, binary, filesystem, Python runtime, fixture source, or scan root changes. A previous PASS describes its own inputs. It is not a transferable certificate for a later directory or policy instant. Reviewers should be able to trace every accepted name back to the oracle and every held run to a specific missing precondition or evidence field.

A practical change record should answer five questions before reuse: who approved the cutoff, which reference file embodied it, which binary and source digest ran, what exact root was scanned, and where the complete report is retained. If any answer changes, rerun the qualification rather than editing the old decision label.

That ownership model is consistent with repeatable system-administration practice: automation should make reviewed intent and verification easier to reproduce, not hide a decision inside a familiar one-liner. Here, repeatability means keeping the source, reference timestamp, versions, raw command evidence, and expected names together.

Develop reliable Linux review habits through structured practice

For broader practice, Refonte Learning’s System Administration Program page describes Windows and Linux administration, command-line work, networking and security, backup and recovery, virtualization, cloud management, and a capstone project. It lists a weekly commitment of 10–12 hours and describes a Training Certificate and a Certificate of Internship upon successful completion. These are programme descriptions, not employment guarantees. The page does not establish that this specialized GNU find nanosecond laboratory is included.

For this selection, the operational boundary remains exact. Use REPAIR_PREDICATE when -mtime +7 conflicts with the approved inclusive cutoff. Use HOLD_POLICY when the meaning of seven days is unresolved. Use HOLD_EVIDENCE when the run cannot prove complete, precise membership. Use ACCEPT_SELECTION_ONLY only after all nine checks agree, and keep any later action outside that acceptance.