Skip to content

Make ordinary status honor no-op workspace freshening consistently with --check #5227

Description

@Widthdom

Summary

After a checksum-reused no-op index refresh, ordinary status says index stale while status --check says index fresh for the same database and workspace.

The ordinary summary ignores the successful workspace-freshening timestamp that check mode uses.

Reproduced with an isolated fixture using locally built cdidx v1.45.1 at 81861a14c9396cd74190edc91392ab11abfa86f8.

Reproduction

Touch an indexed file without changing its contents, then run an incremental index:

touch <project>/src/Program.cs

dotnet ./src/CodeIndex/bin/Debug/net8.0/cdidx.dll index <project> \
  --db <db> --json --notify none

dotnet ./src/CodeIndex/bin/Debug/net8.0/cdidx.dll status --db <db> \
  --fields api_version,summary,indexed_at,last_workspace_freshened_at,latest_modified \
  --json

dotnet ./src/CodeIndex/bin/Debug/net8.0/cdidx.dll status --db <db> --check \
  --fields api_version,summary,index_matches_workspace,indexed_at,last_workspace_freshened_at,latest_modified \
  --json

Observed timestamps:

  • indexed_at=2026-08-30T11:20:20Z;
  • latest_modified=2026-08-30T11:20:35.039277Z;
  • last_workspace_freshened_at=2026-08-30T11:20:35.413414Z.

Actual:

  • ordinary status summary: index stale;
  • check status summary: index fresh;
  • index_matches_workspace=true.

Expected behavior

The two status modes must not produce contradictory freshness summaries from the same trusted metadata. A successful freshening newer than the latest observed modification should be honored consistently, or ordinary status should report an explicitly different unverified state rather than stale.

Existing issue relationship

This is a regression/residual case of #3238, which introduced the no-op indexing and last_workspace_freshened_at freshness contract.

Root-cause pointers

  • src/CodeIndex/Cli/QueryCommandRunner.Status.cs:1374-1388 builds the ordinary freshness label without LastWorkspaceFreshenedAt.
  • The summary is assembled in the same file around lines 1483-1491.
  • src/CodeIndex/Models/QueryResults.cs:1565-1571 documents the persisted freshening stamp.

PR scope and implementation guidance

  • Centralize freshness classification so ordinary status, check status, workspace health, and MCP status do not independently reinterpret the same timestamps.
  • Respect the trust conditions attached to the freshening stamp; do not blindly compare wall-clock values without the matching workspace/head context.
  • Preserve explicit distinctions among fresh, stale, unknown/unverified, head-changed, and clock-skew cases.
  • Keep --stale-after threshold behavior and check-only audit fields intact.
  • Do not force content reindexing for checksum-identical files merely to make the timestamps align.

Acceptance criteria

  • The reproducer no longer reports simultaneous stale and fresh summaries.
  • A checksum-identical no-op refresh remains cheap and records a usable freshening fact.
  • Real content changes after the freshening stamp still report stale.
  • Clock skew, missing stamps, changed HEAD, and unverifiable workspaces remain conservative.
  • CLI, workspace, JSON, human, and MCP freshness signals agree.

Tests, documentation, and changelog

  • Add a deterministic timestamp-controlled no-op fixture.
  • Cover modification before/after freshening, missing stamp, clock skew, and changed HEAD.
  • Test ordinary, --check, workspace, and MCP surfaces.
  • Run the complete net8.0 and net9.0 test suites.
  • Update freshness documentation if the ordinary-status trust contract changes.
  • Add the required bilingual changelog.d/unreleased/ fragment referencing status should clarify no-op update timestamps versus indexed_at #3238.

Metadata

Metadata

Assignees

No one assigned

    Labels

    .NETPull requests that update .NET codebugSomething isn't workingdocumentationImprovements or additions to documentationtests

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions