Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/nornir

.nornir/freshness.md

Working-tree freshness (AUT6 / funnel n-003)

The problem a SHA cannot solve

index_status already reports each git member's last_seen_sha. A commit SHA identifies committed state. It cannot detect uncommitted edits: two different working trees can sit on the exact same HEAD. So a thin client that only compares SHAs can think the cached warehouse is fresh while the live tree has unstaged/uncommitted changes the index never saw.

The freshness signal adds a working-tree content digest that does move when the tree changes — even with no commit.

What we compute

nornir::gitio::worktree_freshness(root) -> WorktreeFreshness { dirty, digest }

<tag> <rela-path> <size>:<mtime_nanos>

where <tag> is S staged, M modified, U untracked, R rewrite, and the size:mtime come from stat-ing the on-disk file (so a content edit moves the digest even when the set of changed paths is unchanged). Missing/deleted files contribute -.

Where it surfaces

Authority gate (always fresh)

The release-blocking gate runs against the working tree, so it is always fresh by construction (it reads the live files, not the index).

Per-member status (the staleness badge)

registry::MemberState gains worktree_digest + worktree_dirty. The monitor poll loop (fetch_workspace) computes them for the server's clone right after a successful fetch and persists them in the registry redb (best-effort: a digest error never fails the fetch). They flow out over gRPC (WorkspaceMember.worktree_digest / worktree_dirty).

The index_status MCP tool reports, per member:

A remote thin client with no local source simply trusts the server's clean/dirty (it cannot diff a tree it does not have).

Viz

WorkspaceInfo.freshness carries (member, dirty, digest). The ℹ info panel renders a ⚠ uncommitted marker next to a dirty member. WorkspacePanel exposes a state_json() ({ member: { dirty, digest } } + any_dirty) folded into the app's top-level state_json under workspace_freshness, so the headless matrix asserts the signal without scraping pixels (LAW #6).

Tests (inject-assert, LAW 1)

src/gitio.rs :: freshness_tests:

Flow (plain text, no mermaid)

server clone ──(monitor fetch)──► gix status ──► (digest, dirty)
                                                      │
                                                      ▼
                                      registry.redb (MemberState)
                                                      │ gRPC
                                                      ▼
thin client ──(local source?)──► local gix status ──► compare ──► staleness
    │ no local source                                              (fresh /
    ▼                                                               local_uncommitted /
trust server's clean/dirty ────────────────────────────────────►  server_dirty)