> ## Content Index
> Fetch the complete content index at: https://www.livain.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# File timestamps can't see staleness that comes from outside the folder: what an agentic housekeeping routine needs that mtime doesn't give it
- URL: https://www.livain.com/blog/file-timestamps-staleness-outside-folder-agentic-housekeeping-routine/
- Published: 2026-10-13T07:00:00.000Z
- Updated: 2026-10-13T06:59:59.000Z
- Description: My weekly routine rebuilds stale project summaries by comparing file modification times. It ran clean — and missed a summary made wrong by a decision taken in a different folder. Local change detection is not correctness.
- Author: Remco Livain
- Tags: AI, Claude, Agentic Workflows, Automation, Knowledge Management

My weekly housekeeping routine checks just under forty project folders for stale summaries. The test is the obvious one: is any file in this folder newer than the folder's own `_summary.md`? If yes, the summary is behind and gets rebuilt. If no, skip it.

It ran clean this week. Six folders refreshed, the rest skipped, nothing missed — according to the routine. And then I found a summary that was badly out of date and had been skipped, because every file in its folder was older than the summary. The thing that had changed was in a different folder entirely.

A pricing decision had been taken in one project. It superseded the pricing in a second project's summary — a mandate quote, a PDF, an outreach document. The second folder hadn't been touched in weeks. Its timestamps were honest. Its content was wrong.

### What mtime can and cannot see

File modification time answers exactly one question: has this file been written since the last time I looked? That's a local question. Staleness is not a local property. A document is stale when something it *depends on* has moved on, and the dependency usually doesn't live in the same directory as the document.

Build systems have known this for decades, and the taxonomy is worth borrowing. In [Build Systems à la Carte](https://blogs.ncl.ac.uk/andreymokhov/build-systems-a-la-carte/?ref=livain.com), Mokhov, Mitchell and Peyton Jones separate out two things that casual tooling conflates: the *scheduler* that decides what order to rebuild in, and the *rebuilder* that decides whether a given thing is out of date at all. Timestamp comparison — the Make model — is the weakest rebuilder on the list. It's cheap, it's local, and it only works if the dependency graph you care about happens to match the file tree. Bazel went the other way and built around [explicit declared dependencies and content hashing](https://bazel.build/basics/hermeticity?ref=livain.com), specifically so that correctness doesn't rest on a clock.

My routine was running the Make model over a knowledge base whose real dependency graph is nothing like its folder structure. Of course it had a blind spot. The blind spot was in the design, not the run.

> A timestamp tells you a file was written. It never tells you a file became wrong.

### The two false signals in the other direction

While I was looking, the same check turned out to be noisy in both directions, which is the usual sign that the test is measuring the wrong thing.

First, macOS leaves artefacts. `Icon` resource files and `.DS_Store` get rewritten when nobody has opened a document in months, so a folder looks changed every single week and the routine rebuilds a summary that didn't need rebuilding. That's not harmless: a routine that cries wolf weekly trains you to stop reading its output, and I've already written about [a scheduled check that could never run](https://www.livain.com/blog/i-scheduled-a-weekly-check-that-could-never-run/) and went unnoticed precisely because nobody expected much from it.

Second, there's a lovely edge case I hadn't anticipated. A Google Drive stub file — a `.gdoc` — counts as a change by every filesystem measure and contains no local content whatsoever. Its mtime is real. Its bytes are a pointer. And in this instance its frontmatter named a *different* project than the folder it was sitting in, so the decision it recorded belonged in two summaries, neither of which the folder test would have found on its own.

So: a change signal with no content, in the wrong folder, affecting two documents elsewhere. No amount of tightening the timestamp comparison fixes that.

### What an agentic housekeeping routine actually needs

The honest answer is that the routine needs a dependency graph, and I have to supply it, because the filesystem can't infer it. Three concrete changes, in order of how much they pay back.

Declare the links. The notes already carry frontmatter — `project`, `tags`, wiki-style links between documents. That is a dependency graph nobody was reading. If a decision note in project A names project B, B's summary is a downstream artefact of that note and should be marked dirty when the note changes, regardless of where either file lives. This is the cheap version of what Bazel does with declared deps, and it costs one pass over frontmatter.

Compare content, not clocks. Hash the substantive body — frontmatter and OS noise excluded — and store the hash alongside the summary. Then `Icon` files and `.DS_Store` stop generating work, and a file that was touched but not meaningfully edited stops counting as a change. A verifying trace, in the build-systems vocabulary, instead of a dirty bit.

Let the agent look for contradictions, not just for change. This is the part that only works because there's a model in the loop. A deterministic check can tell me a file is newer. It cannot tell me that two summaries now state different prices for the same mandate. That comparison is cheap for a language model and impossible for a timestamp, and it's the only one of the three that would have caught this week's miss on its own. Same lesson as when I [tested a Claude skill against a control group](https://www.livain.com/blog/tested-claude-skill-against-baseline-control-group-eval/): the point of putting a model in a routine is the judgement, not the automation.

### Why I'm writing this down rather than just patching it

Because the failure is general, and I keep meeting it in client work under different clothing. A feed importer that re-syncs on modified date. A cache invalidated by file time. A reporting job that refreshes a dashboard when its source table was written, while the business rule that made the metric meaningful changed in a document nobody indexes. Every one of those is the same bug: local change detection standing in for global correctness.

And it is exactly the kind of bug that gets worse as you add automation, because the routine's clean report is now the thing you trust instead of looking. A freshness check that can't see outside its own folder doesn't just miss staleness — it [certifies it](https://www.livain.com/blog/the-same-task-three-days-running-idempotency/).

Mine ran green all week. The summary was wrong the whole time.

### Sources & further reading

**External**  
[Build Systems à la Carte](https://blogs.ncl.ac.uk/andreymokhov/build-systems-a-la-carte/?ref=livain.com) — Andrey Mokhov, Neil Mitchell, Simon Peyton Jones (ICFP 2018)  
[Hermeticity](https://bazel.build/basics/hermeticity?ref=livain.com) — Bazel documentation  
[Dependency management](https://bazel.build/basics/dependencies?ref=livain.com) — Bazel documentation

**Related posts**  
[I scheduled a weekly check that could never run](https://www.livain.com/blog/i-scheduled-a-weekly-check-that-could-never-run/)  
[The same task, three days running](https://www.livain.com/blog/the-same-task-three-days-running-idempotency/)  
[I tested a Claude skill against a baseline control group](https://www.livain.com/blog/tested-claude-skill-against-baseline-control-group-eval/)  
[My scheduled Claude Code routine failed three weeks running](https://www.livain.com/blog/scheduled-claude-code-routine-failed-agentic-workflows-context/)