# Observing File Organisation

## Introduction

Imagine importing a set of photographs from a camera, smartphone, or scanner. Initially the files arrive as a flat collection with sequential filenames. During review, a person organises them into meaningful folders representing subjects, projects, or collections.

In this vignette we use nine tiny text files as stand-ins for photographs. The example demonstrates how `fscontext` records observations before and after this organisation, allowing the contextual changes to be reconstructed

    library(fscontext, quietly = TRUE)
    library(dplyr, quietly = TRUE)

    vignette_folder <- file.path(tempdir(), "vignette")
    camera <- file.path(vignette_folder, "camera")
    photos <- file.path(vignette_folder, "photos")

    unlink(c(vignette_folder, camera, photos), recursive = TRUE)

    dir.create(vignette_folder)
    dir.create(camera)
    dir.create(photos)

    for (i in 1:9) {
      writeLines(as.character(i),file.path(camera, paste0("P", i, ".txt")))
    }

    dir(camera)
    #> [1] "P1.txt" "P2.txt" "P3.txt" "P4.txt" "P5.txt" "P6.txt" "P7.txt" "P8.txt"
    #> [9] "P9.txt"

Let’s take a snapshot as the files arrive from a camera or stored on a memory card.

    before <- snapshot_storage(path = camera,
                               root = vignette_folder,
                               person_id = "photographer")
    #> Starting scan_storage() on: C:/Users/DANIEL~1/AppData/Local/Temp/RtmpUNOJJ9/vignette
    #> Scanning 9 files.
    #> Signatures computed. Detecting repositories and Git status...
    #> Files scanned: 9
    #> Files in Git repos: 0
    #> Files tracked by Git: 0
    #> Skipped approximately 0 inaccessible files
    #> scan_storage completed in 0.12 seconds
    #> Saved: C:\Users\DANIEL~1\AppData\Local\Temp\RtmpUNOJJ9/vignette/camera/scan_local-storage_c_vignette_20260731-110920_d4b01c.rds

Summarise the files:

    summary_before <- readRDS(before) |>
      dplyr::select(person_id, rel_path, stem, size)

    print(summary_before)
    #>      person_id      rel_path stem size
    #> 1 photographer camera/P1.txt   P1    3
    #> 2 photographer camera/P2.txt   P2    3
    #> 3 photographer camera/P3.txt   P3    3
    #> 4 photographer camera/P4.txt   P4    3
    #> 5 photographer camera/P5.txt   P5    3
    #> 6 photographer camera/P6.txt   P6    3
    #> 7 photographer camera/P7.txt   P7    3
    #> 8 photographer camera/P8.txt   P8    3
    #> 9 photographer camera/P9.txt   P9    3

Now let’s organise the photos into three groups: photos that are made inside of the house, in the garden, and photos that are not worth keeping and designated for deleting.

    dir.create(file.path(photos, "house"), recursive = TRUE)
    dir.create(file.path(photos, "garden"))
    dir.create(file.path(photos, "delete"))

    file.rename(
      file.path(camera, c("P1.txt","P2.txt","P5.txt")),
      file.path(photos, "house",
                c("P1.txt","P2.txt","P5.txt"))
    )
    #> [1] TRUE TRUE TRUE

    file.rename(
      file.path(camera,
                c("P3.txt","P4.txt","P6.txt","P7.txt")),
      file.path(photos, "garden",
                c("P3.txt","P4.txt","P6.txt","P7.txt"))
    )
    #> [1] TRUE TRUE TRUE TRUE

    file.rename(
      file.path(camera,
                c("P8.txt","P9.txt")),
      file.path(photos, "delete",
                c("P8.txt","P9.txt"))
    )
    #> [1] TRUE TRUE

The resulting file structure:

    photos/

        house/
            P1.txt
            P2.txt
            P5.txt

        garden/
            P3.txt
            P4.txt
            P6.txt
            P7.txt

        delete/
            P8.txt
            P9.txt

The second snapshot records the reorganised collection. Although the files themselves have not changed, their contextual location within the filesystem has.

    after <- snapshot_storage(path = photos,
                              root = vignette_folder,
                              person_id = "archivist")
    #> Starting scan_storage() on: C:/Users/DANIEL~1/AppData/Local/Temp/RtmpUNOJJ9/vignette
    #> Scanning 10 files.
    #> Signatures computed. Detecting repositories and Git status...
    #> Files scanned: 10
    #> Files in Git repos: 0
    #> Files tracked by Git: 0
    #> Skipped approximately 0 inaccessible files
    #> scan_storage completed in 0.19 seconds
    #> Saved: C:\Users\DANIEL~1\AppData\Local\Temp\RtmpUNOJJ9/vignette/photos/scan_local-storage_c_vignette_20260731-110921_d1b645.rds

We can see the new summary:

    summary_after <- readRDS(after) |>
      dplyr::select(person_id, rel_path, stem, size)

    print(summary_after)
    #>    person_id                                                        rel_path
    #> 1  archivist camera/scan_local-storage_c_vignette_20260731-110920_d4b01c.rds
    #> 2  archivist                                            photos/delete/P8.txt
    #> 3  archivist                                            photos/delete/P9.txt
    #> 4  archivist                                            photos/garden/P3.txt
    #> 5  archivist                                            photos/garden/P4.txt
    #> 6  archivist                                            photos/garden/P6.txt
    #> 7  archivist                                            photos/garden/P7.txt
    #> 8  archivist                                             photos/house/P1.txt
    #> 9  archivist                                             photos/house/P2.txt
    #> 10 archivist                                             photos/house/P5.txt
    #>                                                    stem size
    #> 1  scan_local-storage_c_vignette_20260731-110920_d4b01c 1160
    #> 2                                                    P8    3
    #> 3                                                    P9    3
    #> 4                                                    P3    3
    #> 5                                                    P4    3
    #> 6                                                    P6    3
    #> 7                                                    P7    3
    #> 8                                                    P1    3
    #> 9                                                    P2    3
    #> 10                                                   P5    3

Now compare the two observations.

The files are matched using their filename (stem) and file size. The comparison reveals how each file moved from the original camera import into its new contextual location.

    summary_path_change <- summary_before |>
      rename(
        rel_path_before = rel_path,
        person_before = person_id
      ) |>
      left_join(
        summary_after |>
          rename(
            rel_path_after = rel_path,
            person_after = person_id
          ),
        by = c("stem", "size")
      ) |>
      select(
        stem, size,
        person_before, rel_path_before,
        rel_path_after, person_after
      ) |>
      filter(grepl("txt$", rel_path_before))

    print(summary_path_change)
    #>   stem size person_before rel_path_before       rel_path_after person_after
    #> 1   P1    3  photographer   camera/P1.txt  photos/house/P1.txt    archivist
    #> 2   P2    3  photographer   camera/P2.txt  photos/house/P2.txt    archivist
    #> 3   P3    3  photographer   camera/P3.txt photos/garden/P3.txt    archivist
    #> 4   P4    3  photographer   camera/P4.txt photos/garden/P4.txt    archivist
    #> 5   P5    3  photographer   camera/P5.txt  photos/house/P5.txt    archivist
    #> 6   P6    3  photographer   camera/P6.txt photos/garden/P6.txt    archivist
    #> 7   P7    3  photographer   camera/P7.txt photos/garden/P7.txt    archivist
    #> 8   P8    3  photographer   camera/P8.txt photos/delete/P8.txt    archivist
    #> 9   P9    3  photographer   camera/P9.txt photos/delete/P9.txt    archivist

For example, photograph P1 was originally observed in the camera import and later observed in the house folder.

    summary_path_change |>
      filter(stem == "P1") |>
      select(
        person_before,
        rel_path_before,
        rel_path_after,
        person_after
      ) |>
      tidyr::unite(
        col = "transition",
        rel_path_before,
        rel_path_after,
        sep = " → "
      )
    #>   person_before                          transition person_after
    #> 1  photographer camera/P1.txt → photos/house/P1.txt    archivist

The transition

`camera/P1.txt → photos/house/P1.txt`

records an observable change in filesystem context.

From the perspective of `fscontext`, this is evidence that the file became part of the contextual group house. The package deliberately does not infer why this happened. The folder may represent the subject of the photograph, a temporary workspace, or another organisational decision known only to the user.

The important point is that the filesystem itself records a human curation activity. By comparing two observational snapshots, `fscontext` reconstructs this activity as reproducible evidence.

The resulting comparison can serve as the starting point for later semantic review. A subsequent workflow may interpret the movement into `house`, `garden`, or `delete` as candidate semantic assertions, record the provenance of those assertions, and subject them to human review before they become part of a knowledge graph or archival description.