Skip to content

fsmonitor: recover status history across mixed writers - #47

Open
ttaylorr-oai wants to merge 14 commits into
codex-unstablefrom
tb/codex/status-preview-unstable
Open

fsmonitor: recover status history across mixed writers#47
ttaylorr-oai wants to merge 14 commits into
codex-unstablefrom
tb/codex/status-preview-unstable

Conversation

@ttaylorr-oai

Copy link
Copy Markdown
Collaborator

Ship the six follow-up fsmonitor/status commits ending at
fca61fdceec3017dff2f5d4b945f6c154290dfdd in the next codex-unstable
release. The reviewed source is a fast-forward merge of the existing
enrolled topic and the exact tested prototype; its tree is byte-identical
to fca61fdcee, and no protected workflow or controller files change.

  • Recover authenticated status history after another Git binary rewrites
    the index without FSMN, FSCF, or FSUC. The reported
    git add x; rm x; git checkout -- x; git reset; git status sequence
    remains bounded and reports ? x correctly.
  • Revalidate cached directories after fsmonitor provider resets, including
    scoped status requests, without serially reopening the entire worktree.
  • Reduce attribute-manifest allocation and parent-revalidation overhead;
    reissue clean proofs when repository fingerprint inputs change.
  • Reject unsafe global invalidations, changed attributes, external hardlink
    mutations, and unauthenticated history.

Validation:

  • t7527: 156/156 with SHA-1 and SHA-256.
  • t7519: 77/77 with SHA-1 and SHA-256.
  • t7530: 47/47 with SHA-1 and SHA-256.
  • t7063: 64/64 with SHA-1 and SHA-256.
  • t7529: 36/36 with SHA-1 and SHA-256.
  • 374 unit tests, developer build, and shell test lint.
  • Actual OpenAI worktree: root status 524 ms, scoped status 143 ms;
    provider-reset scoped recovery improved from 13.25 s to 5.21 s.

A full worktree-attribute manifest refresh probes one potential
.gitattributes source per tracked directory. On the OpenAI monorepo
that means approximately 237,000 candidates, almost all absent.

Allocate candidate state in one contiguous block, collect indexed
attribute sources during the existing index walk, and avoid sorting
when candidate order is already monotonic. On Darwin, revalidate a
pinned parent with fstatat() instead of reopening and closing it. The
complete anchored namespace identity remains unchanged; Linux keeps
its existing openat2-based validation.

Cover unusual path ordering, both object formats, indexed fallback,
and replacement of an anchored parent with a symlink.
Losing a filesystem-monitor boundary currently discards every cached
untracked directory, even when its prior state was authenticated by a
paired full worktree proof. Rebuilding that cache enumerates roughly
237,000 directories in the OpenAI monorepo.

Preserve a complete, previously authenticated untracked cache only
when a successful provider reset leaves configuration, attributes, and
conversion semantics unchanged. Demote the cache to ordinary directory
validation, then reuse the existing parallel directory-stat and ignore
file revalidation before closing the new provider token.

Global invalidation, changed attributes, missing proofs, dirty caches,
and ambiguous directory identities retain destructive fallback. Add
coverage for nested untracked files, changed ignore rules, and provider
global invalidation.

On the real OpenAI checkout, full recovery improves from 30.79 seconds
to 8.58 seconds; subsequent status calls retain a 31.86 ms median.
A full manifest rebuild on the OpenAI checkout enumerates roughly 237,000 possible .gitattributes paths. Allocating and freeing every candidate separately adds avoidable allocator traffic to an already expensive recovery path.

Store candidate names in a stable memory pool while retaining the existing anchored lookup, sorting, deduplication, and worker-lifetime guarantees. Cover a newly appearing nested attribute source under SHA-256 so pooled storage cannot turn an earlier negative lookup into stale proof.
After provider history is lost, a pathspec previously disabled threaded untracked-cache revalidation. The scoped command still needed a globally closed provider token, so it instead validated the entire cache serially before filtering its output.

Permit the existing ordinary whole-tree preload for this narrow authenticated recovery state, then retain the original pathspec filtering and provider closure. Allow bounded test sweeps through sixteen recovery workers while keeping the normal six-worker default. Exercise changed files outside the requested cone, changed nested ignore rules, global invalidation, and every supported worker count.
A clean-status certificate includes repository inputs such as the active locale. When those inputs changed, validation correctly rejected the existing certificate, but ordinary status saw a safely pinned sidecar and declined to replace it. Every subsequent command repeated the slower index-reading path.

Remember the specific repository-input mismatch and allow ordinary clean status to reissue an otherwise safe, single-link sidecar after complete normal validation. Preserve the optional-locks boundary and leave the physical index untouched. Cover both successful locale-change repair and the read-only case.
Unstaging an ordinary newly added file invalidated the entire fsmonitor proof despite each removed entry already passing the semantic-safety check. Worse, an installed Git reset can remove FSMN, FSCF, and FSUC altogether while changing the index relative to the last authenticated external checkpoint. The next status then rebuilt every attribute candidate and refreshed the entire index.

Preserve proofs for individually safe removals and recover a missing provider boundary from a fully authenticated checkpoint only when its old token yields a safe delta. Start with every current entry dirty, restore only matching checkpoint-clean entries, and force content checks for remaining untrusted entries so externally modified hardlinks cannot become false clean.

Exercise the exact add, delete, checkout, reset, status sequence for both the built-in reset and an actual foreign Git. Reject global invalidations, changed attributes, unsafe logical changes, and same-timestamp hardlink mutations.
Advance the enrolled unstable topic without changing the exact tested metadata-recovery tree. Preserve the previously reviewed source history as the first parent and the complete six-commit prototype ending at fca61fd as the second parent.
The global invalidation fixture assumes its clean priming status physically writes the paired FSUC extension. On macOS, external history can satisfy the same query without rewriting the index, so that prerequisite intermittently fails before the intended global invalidation is exercised. Pin GIT_INDEX_FILE for the priming command, matching the existing neighboring fixtures and forcing deterministic physical proof materialization.
Advance the reviewed unstable topic with a one-line test-only correction to force physical FSUC materialization during priming. Preserve the exact six-commit fca61fd production tree and existing reviewed topic history; the only added delta is the deterministic regression-test fixture.

@dreynaud-oai dreynaud-oai left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independently reviewed exact head 046008d. Verified its only delta from the previously qualified production tree is one test-only fixture line, and reproduced the formerly failing provider-reset test successfully under SHA-1 and SHA-256. Real dirty macOS and Linux monorepo checkouts show matched-build steady-state performance parity, 836 adversarial macOS checks pass, and all eight Linux fsmonitor watch-failure cases pass.

A dry run can refresh fsmonitor state while reading the index, which makes the common exit path write an otherwise unchanged index. When configured filters prevent semantic history from being preserved, that write invalidates the clean-status proof and makes the next status rebuild worktree metadata. Roll back the index lock for dry runs instead. They must not update the index or run index-change hooks.
A configured clean or process filter does not make every index change semantically unsafe. In particular, a global Git LFS configuration was invalidating the authenticated worktree proof after adding or unstaging ordinary, unfiltered files. The following status then rebuilt the entire manifest and untracked cache. Preserve history when an already-authenticated filter scope exists and the changed path selects no active clean filter. Continue rejecting actual filtered paths, changed attribute sources, unsafe index shapes, and mismatched provider tokens. Cover ordinary staging, unstaging, dry runs, nested additions, and active-filter invalidation.

@dreynaud-oai dreynaud-oai left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independently verified exact dad9e3b on the real dirty 858,001-entry Linux monorepo with matching Git client and daemon. New-file add, existing staged-file add, unstage, and dry-run preserve coherent FSMN/FSUC/FSCF and reduce subsequent status from ~19 s to 0.91-1.25 s. Separate live-daemon SHA-1/SHA-256 gates verify unfiltered staging and dry-run preserve history while actual filtered paths fail closed; attribute, text-conversion, and clean-filter safety scenarios also pass.

The og wrapper enables bulk index preloading through command-scoped configuration, while direct Git invocations may not. Hashing those transient acceleration settings into the authenticated worktree proof made the two clients disagree about repository configuration. When an unused clean filter was configured, alternating clients repeatedly discarded the full metadata manifest and rebuilt it. Exclude only command-scoped core.preloadIndex and core.preloadIndexBulk from the proof digest. Continue hashing persistent preload settings, semantic conversion settings, tracked-file policy, and filters. Cover both hash algorithms, every persistent scope, alternating wrapper and direct invocations, and fail-closed filemode changes.

@dreynaud-oai dreynaud-oai left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independently approved exact f92dd57. Real dirty Linux monorepo (858,001 paths, actual system LFS filter): 21 plain/og/preload transitions, 0.95-0.99 s steady status, zero manifest scans and coherent proofs; add/re-add/commit dry-run/unstage remain 0.9-1.23 s. Real dirty macOS monorepo (1,161,336 paths, existing .658 daemon preserved): 21 transitions, 0.25-0.28 s steady status. Active clean-filter fail-closed staging passes 28 scenarios each for SHA-1 and SHA-256. Production source blob independently matched c224fe3876961513c813f18187e3e046fe37ec49; only exact command-scoped acceleration keys are excluded.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants