I am hoping to get back into blogging more consistently, maybe as an excuse to do a bit more journaling around technical topics, because I think I am starting to see some more value in getting thoughts put down. There is a lot of refinement of ideas and thoughts that takes place when doing this, and it is also quite a relaxing process.

Usually when left to my own devices i am constantly falling into a pattern of researching and following whatever is attention-seeking, and that's a lot less healthy compared to taking notes on and refining my own stuff, be that research or hobbies or whatever.

So to support this more of a journal use for articles in the blog, and not to interfere with the existing and still frankly quite useful and natural directory categorization system, I want to implement some sort of chronological feed listing navigation page in addition to that.

I also want to keep in the back of my mind yet another paradigm that i might want to move toward (or at least emphasize more strongly) later, which is instead of putting articles into directories that own them, or in addition to doing so, assigning tags to articles.

OK. So today's exploration is about how to add a chronological feed to the blog site so that as it sprawls out with more articles there is a clean chronological navigation page that viewers can look through to see what's been on my mind most recently.

Of course, for my own purposes I can just take a look inside Git history.

So the super easy first stab at this would be to take all the files sorted by modified date and render them as links into a new listing and call it done. A second step would be to have that ordering in mind and inject more back/forward nav links inside each page so that maybe i could offer two modes of going back and forward, one constrained to the directory, and one globally.

That's okay as a first pass but having 4 left/right arrow navigation buttons on each page certainly sounds ridiculous. Also, I definitely will have, even if it isn't very common, pages that were written months and years ago that i go back in to fix typoes or something and don't want to pollute their timestamps for the purpose of this sort. I also don't want to ever establish a system of manually tracking dates because I will never reliably manage that... I think one thing i might be doing, but which i need to confirm, is that i take the first day that an article was created and treat that as its chronology date. This gives each article a canonical date that is immutable, and i also recall that i am pretty sure i went to lengths to make sure that even if do something like rename an article, the git method i use to work out this date should be able to follow it back to the beginning of the article. now, if that is the case, then i should simply use that date to do the sort, as the semantics would be perfect. Since I'm coming back into this blog engine after some time I'm a bit fuzzy on the details...


Response I got from Codex (GPT5.4) after pointing it at this article:

Yes. The engine already does exactly that.

In scripts/stage/structure.ts#L34, getGitCreationDate() runs:

    git log --follow --format=%aD "path/to/file" | tail -n1

That means each non-index.md article gets createdAt set to the oldest Git history entry reachable for that path, with --follow enabled, and sibling pages are then sorted by that createdAt in scripts/stage/
structure.ts#L143.

So the answer to the question in the article is: use the first Git-tracked creation date as the chronology date. The code already does that, and it is the right semantic choice for the feed you want.

The caveats are small but real:

- `--follow` is good for renames of a single file path.
- It is not a perfect “content lineage” system if a post was split, copied, or heavily rewritten into a different file.
- You are using %aD (author date), so history rewrites could affect it, but normal typo-fix commits will not.

So for a first chronological interface, you do not need manual dates and you do not need filesystem mtime. You already have the canonical per-article date source you were hoping you had.

This also serves as a test of the use of markdown formatting code blocks from within markdown. There are probably reasons i avoided that earlier when i came up with the idea of using the callouts to insert this type of content. Let's see.


Right, so the problem i guess is that like any other code block it renders markdown as fixed width code style <pre> rendering, so while it looks fine it's not a "rendered" as markdown. Okay. That's... fine.


I also found two more issues today.

  1. An article chronology sort issue. Apparently it is this:

    The sort comparator only uses chronology when both sides have createdAt:
    
    - If both a.createdAt and b.createdAt exist, it sorts by date.
    - Otherwise it falls back to filename sorting.
    
    But explicit index.md pages are assigned createdAt: undefined in scripts/stage/structure.ts#L122.
    
    So when the sort compares:
    
    - adding-chronological-interface.md vs index.md, it falls back to name sort
    - index.md vs video-files-failing-to-play.md, it also falls back to name sort
    

    That wasn't the clearest... basically I was doing something sloppy by having something not properly sortable that fell through the cracks into the list of items being sorted, which had the effect of causing subtle errors occasionally crop up from the sort order while it mostly stays sorted. That's what I saw today. I was attuned to that owing to the topic of this post. That was an easy fix.

  2. This problem: giscus broke!

    Turns out, it's just me opening the page under a general ipv6 addressing scheme, based off of me clicking in the terminal this output from my dev build script:

    Serving HTTP on :: port 4000 (http://[::]:4000/) ...
    

    That's definitely pretty silly. Problem went away once i use http://localhost:4000 to reach the dev server. So it was not something I broke.


I also started to explore addressing what is clearly the biggest source of tech debt in this system now, which is the fact that the dev server is indeed now sluggish and takes a few seconds to regenerate and render the entire blog site each time I save a markdown file as I edit. It's still not too slow to be okay, but it's definitely wasting a lot of work to re-render the entire website when clearly only the file i'm editing needs to be dealt with in the typical case.

As per usual in 2026, I would dump out markdown of the chat I used to begin to work on this, but I'm currently using codex and it inexplicably has no export command, and its logs are in jsonl structured format, so I'm just going to drop in the markdown around the current state of the plan:

# Watch Optimization Plan

## Goal
Make local watch rebuilds meaningfully faster without risking bad site output or replacing the current stable workflow too early.

## Status
- Phase 1: Complete
- Phase 2: In progress
- Phase 3: Not started
- Phase 4: Not started

## Phase 1: Instrumentation Only
Status: Complete

Implemented:
- Added concise timing summaries to the metabuild step.
- Added concise timing summaries to the dev/stage render pipeline.
- Added explicit reporting for git creation-date lookup count and wall-clock time.
- Kept build/render semantics unchanged.

What we learned:
- TypeScript transpilation is cheap.
- The dominant current cost is the blog structure scan, especially repeated `git log --follow` lookups.
- Full blog page rendering is much cheaper than the git-history scan on a typical dev run.

## Phase 2: Safe Waste Removal
Status: In progress

Implemented:
- Added `npm run build:watch` so watch flows can transpile without also reinstalling hooks.
- Added `npm run watch:smart` as an opt-in experimental watcher.
- `watch:smart` currently preserves full site renders for content changes.
- `watch:smart` only reruns transpilation when build-code files change.

Remaining for this phase:
- Decide whether to keep the current Node watcher approach or fall back to a smarter `watchexec` shell wrapper if the daemon proves annoying in real use.
- Verify `watch:smart` in a normal local environment, since sandboxed validation cannot fully exercise long-lived watch/server behavior.
- If desired, cache git creation dates so repeated content-only full renders stop paying the full `git log --follow` cost.

## Phase 3: Low-Risk Targeted Incrementality
Status: Not started

Planned scope:
- Special-case static resource edits first.
- Copy only the changed resource file on resource edits.
- Keep full site renders for markdown and structural changes.

Why this is next:
- It improves edit latency without touching nav/date correctness.

## Phase 4: Partial Markdown Rebuilds
Status: Not started

Planned scope:
- Rebuild only a changed markdown page plus clearly affected dependents.
- Fall back to a full rebuild on ambiguous create/delete/rename/draft-state cases.

Guardrails:
- Do not start this phase until earlier phases are stable in real usage.
- Compare partial rebuild output against a fresh full dev build before trusting it.

## Current Recommendation
Next safest step:
- Use the new timing output for a bit.
- Test `npm run watch:smart` in your normal local shell.
- If the workflow feels sound, the best next optimization is probably caching git creation dates rather than jumping straight to partial markdown rebuilds.

Being a pretty small change, the in-planning-doc-work-tracking is perfectly usable.


Then we had a pretty good finding right off the bat thanks to the decision to make measurements:

› Timing summary (development) blog structure scan: 2874.5ms
  Timing summary (development) ensure target dir: 0.0ms
  Timing summary (development) dev files symlink check: 0.1ms
  Timing summary (development) clear target output: 8.9ms
  Timing summary (development) render blog pages: 262.5ms
  Timing summary (development) copy resources: 0.9ms
  Timing summary (development) render standalone pages: 0.8ms
  Timing summary (development) total: 3151.7ms
  Timing detail rendered markdown pages: 88


  that should update our strategy, don't you think? i'm impressed rendering is so small a part of the total delay.
• Yes. That output changes the priority materially.

  The plan is now stored in WATCH_OPTIMIZATION_PLAN.md, and it’s committed locally in d043409. I also committed the code changes in 22765ab. I did not push the plan commit.

  On status: Phase 1 is complete. We also did the first safe slice of Phase 2, but the timings show the main bottleneck is not rendering at all. The real problem is the blog structure scan, especially the per-file git
  history lookup path in scripts/stage/structure.ts:40. So I would update the strategy like this:

  - Stop prioritizing partial markdown rebuilds for now.
  - Focus next on eliminating or caching git log --follow work.
  - Treat the smart watcher as secondary until the structure-scan cost is under control.

  In practice, the next best move is probably:

  - add a persistent cache for creation dates keyed by source path, or
  - replace git log --follow on every file with a cheaper precomputed source of truth.

  That should attack the ~2.9s directly, whereas partial page rendering would only save a few hundred milliseconds at most.
› it still feels to me like adding a cache is pretty risky here. the correct behavior right now is pretty clean. I think a few seconds is also still not a problem, it's only a problem during rapid document editing.
  If we jump straight to only updating markdown file re-rendering of the specific file that changed, that flow eliminates both the 2.9s and 98% of the 259ms rendering time by going from rendering all articles to
  rendering only the one that changed.
• I think that’s a reasonable update, with one important constraint: make it a very narrow dev-only fast path.

  A persistent git-date cache is riskier because it introduces a second source of truth that can drift. A one-file markdown rerender path is narrower and easier to reason about if we limit it to the cases where
  structure cannot have changed.

  The safe version of that idea is:

  - Only fast-path edits to an existing blog/**/*.md or blog/**/*.deep.md file.
  - Only when the path did not change and it is not a create/delete/rename.
  - Exclude index.md, because directory landing pages are more coupled to tree state.
  - Exclude draft-name changes, directory moves, and anything under scripts/, resources/, pages/, or root index.md.
  - Fall back to the current full dev rebuild for every excluded case.

  That gives you the rapid-edit win without introducing a persistent cache. The watcher can keep an in-memory snapshot from the last full build just to resolve nav/context for the current file. That is still
  “stateful,” but much lower risk than storing cached git metadata on disk and trying to keep it coherent across sessions.

  So I’d revise the next step to:

  1. Keep current full build as the source of truth.
  2. Add a dev-only fast path for “existing non-index markdown file content changed.”
  3. Re-render only that file into dev/.
  4. Force full rebuild on anything structurally ambiguous.

  That should eliminate almost all of the 3.1s during normal typing, while keeping the correctness story clean: “fast path only when structure definitely didn’t change.”

  If you want, I can implement that next and update WATCH_OPTIMIZATION_PLAN.md to reflect the new strategy.

This continued on quite a bit, won't bore you with the details. I bet this work deserves its own article too, but quite frankly the AI assistance is so robust and productive now that it's more work to describe it than to just get it done and summarize it succinctly later.


Meanwhile the above was all a large series of diversions from the original objective, which I had kinda just dropped.