Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Tracking a toy project

This notebook walks the five steps of the OS resource-tracking milestone in PROJECT_PLAN.md:

  1. initialize Trail in a directory

  2. explicitly register files as resources

  3. change registered files from outside the notebook

  4. check on demand what Trail recorded

  5. report what changed, identifying each resource by its stable ID

It builds a throwaway toy-project/ in the kernel’s working directory and leaves it there for inspection, so re-running the notebook from the top is safe. Trail must be importable, e.g. python -m pip install -e . from the repository root.

A toy project on disk

Four small files stand in for the datasets of a geospatial project. One of them is never touched, so the log below has an untouched resource to stay silent about.

1. Initialize Trail in a directory

Trail(root) opens the project record at root/.trail, creating it when it is absent. The project ID is generated once and persisted; reopening the same directory replays the log instead of minting a new identity.

2. Explicitly register files as resources

trail.register returns an Entry per path. Each one carries a random hex ID that is independent of the path, so the identity survives a rename. Registration is appended to .trail/events.jsonl as an AddEntryEvent, which samples the size and modification time of the resource as the baseline a later comparison against the filesystem is measured from.

Adding an entry also wakes the observer. A notebook kernel runs an asyncio loop, so the watchdog starts on its own and watches the directory holding each registered file, non-recursively.

3. Change registered files from outside the notebook

The three edits below run in a separate process, so nothing in this kernel observes them directly. Trail learns about them only because the operating system reports them.

measurements.csv is overwritten, sidewalks.geojson is renamed, streets.geojson is deleted, and boundary.geojson is left alone.

4. Check what Trail recorded

Nothing needs to be scanned: the observer appended what it saw to .trail/events.jsonl as it happened, and trail.events is that log.

5. Report what changed, by resource ID

Every event names the resource it belongs to. The rename is the point of the exercise: sidewalks.geojson and sidewalks-2026.geojson report the ID assigned at registration, and boundary.geojson is absent because nothing happened to it.

The registration baseline

Because AddEntryEvent recorded a size and a modification time, the log can be held against the filesystem. boundary.geojson matches what was registered; measurements.csv was overwritten, and streets.geojson is gone.

The record outlives the session

Stopping the observer and reopening the directory replays the log: same project ID, same resource IDs, in the same order, with the renamed file at its current path.

The deleted file is still a registered resource: losing the file does not release its ID.

Where the record lives, and what is missing

toy-project/.trail/ is left on disk:

  • project.json holds the persistent project ID

  • events.jsonl holds the append-only record

Gaps against PROJECT_PLAN.md:

  • there is no Trail.status() and no CLI; reading trail.events stands in for both.

  • only registration records size and modification time; the watchdog events that follow do not, so a comparison can only be made against the registration baseline.

  • detection is push-based, through the watchdog, rather than the on-demand os.stat sweep the plan describes; a resource changed while no Trail is running is not noticed.