Skip to content

fsmonitor: preserve proofs and avoid repeated recovery scans - #59

Open
ttaylorr-oai wants to merge 432 commits into
codex-unstablefrom
tb/codex/fsmonitor-hardlink-inodes-unstable
Open

fsmonitor: preserve proofs and avoid repeated recovery scans#59
ttaylorr-oai wants to merge 432 commits into
codex-unstablefrom
tb/codex/fsmonitor-hardlink-inodes-unstable

Conversation

@ttaylorr-oai

@ttaylorr-oai ttaylorr-oai commented Aug 15, 2026

Copy link
Copy Markdown
Collaborator

Current candidate: 4e82cbf14855

This closes the temporary watch-limit backoff gap found in review. Clean add --refresh, diff's optional stat refresh, and stash's optional canonical-index writes preserve the existing physical proof. A real staging write still publishes the requested contents, but retains only authenticated historical state: an all-dirty FSMN bitmap, an unbound FSCF manifest, and pending untracked candidates. Same-path replacements recheck root and ancestor attributes; unsafe mutations revoke that history. No clean proof can be issued until a fresh provider boundary and ordinary revalidation agree. Explicit core.fsmonitor=false behavior is unchanged.

The four new regressions fail for their intended reasons on immutable f7be5b1d18 under both hashes. The exact clean successor builds on macOS and passes 52 focused SHA-1/SHA-256 assertions, including all ten backoff tests, existing closure/recovery controls, sidecar identity attacks, and daemon compatibility. Unit results are 401 passed and one platform skip. Two later-successful-cookie checks correctly skip on the unhealthy native provider; they are not counted as successful native delivery coverage. The initial sandbox-denied native runs are retained, and the separately approved private-fixture rerun passes all mandatory cases. Both independent source reviews pass. Exact-head Linux normal, no-pthreads, LeakSanitizer, and ASan/UBSan qualification is next; fresh PR CI and push CI are running.

Release hold remains: this Git-only writer fix does not repair the primary Mac's native cookie delivery or establish an FSEvents ordering guarantee. Keep PR59 draft and out of the live metadata plan. Exact-head human review, real-checkout correctness and warm performance, and explicit Mac promotion clearance remain required. No og/notify patch or og-preview publication is part of this update. All source-specific results below are historical.

Earlier candidate: f7be5b1d18f9

This removes only the a8d82e3790 asynchronous-flush backport. Upstream has retracted that topic, and our matched native experiment and large-worktree test did not establish a performance repair. The author's precise reason for withdrawal has not been verified. The resulting source tree is byte-identical to 68abca224f29; all earlier correctness, daemon-compatibility, and bounded-retry fixes remain.

The exact clean head builds on macOS and passes all unit tests and 34 focused SHA-1/SHA-256 assertions. Exact-head Linux qualification is complete: 74 normal suite arms (3,848 successes), 12 no-pthreads arms (218 successes; six native-only suites correctly skipped), 12 LeakSanitizer arms (470 successes), and 10 ASan/UBSan arms (249 successes). Each build also passes 392 unit tests with 10 skips. There are no unexpected failures. Only the no-pthreads build uses two scoped exemptions for inherited unused-parameter and unused-function warnings; all other developer warnings remain errors. Both PR CI and push CI passed 45 jobs with two expected skips; style and whitespace checks also pass. Independent exact-head review and the Mac release gates remain open.

Release hold remains: the primary large worktree still loses native cookie boundaries. Keep this PR draft and out of the live metadata plan. A private trace-only experiment confirms that some cookies arrive after the one-second deadline. A matched three-second-deadline experiment did not improve the timeout rate and is not included. Neither a longer wait nor a flush establishes the missing FSEvents ordering guarantee. No Mac release or og-preview publication is authorized. All source-specific results below are historical.

Earlier candidate: a8d82e379088

This update backports upstream 08b12d90, which prompts Darwin FSEvents to deliver buffered notifications before the existing one-second cookie wait. The asynchronous call avoids blocking the listener on the daemon lock. The timeout and failed-cookie behavior are unchanged.

The preceding follow-ups make failed-cookie token retirement an explicit daemon compatibility requirement, bind that requirement to the actual query response, and preserve older client protocols without adding IPC to a marked warm query. Status also records a precise TRIVIAL result from its fast-sidecar probe and falls back after two consecutive closing resets, while preserving recovery from one transient reset.

The exact clean head builds on macOS and passes all unit tests and 34 focused SHA-1/SHA-256 assertions. The preceding 798fb21ad8 passed all four Linux variants and the actual old/new/old client interoperability test; 68abca224f passed all 74 normal Linux suite arms. Exact-final-head Linux qualification and hosted CI are running. A matched 48-query native delivery experiment still observed 12 timeouts in each 24-query arm. A controlled switch of the primary large-worktree target to the exact patched server also failed its first closing query after one second; the guarded status was stopped before another full-tree scan. The backport is therefore not an accepted performance repair on that machine.

Release hold remains: keep this PR draft and out of the live metadata plan. The asynchronous flush does not establish an FSEvents ordering guarantee. Actual-target performance, exact-head review, and the existing Mac promotion decision remain open. All source-specific results below are historical.

Earlier candidate: bd208f35619f

Four further commits repair the repeated-refresh path and the hosted
test failures. A status-owned write receipt now binds a rewritten
index.skipHash index to the exact lockfile inode, records its identity
before post-index-change, and rechecks it after the hook. This permits
safe clean-sidecar republication without accepting a foreign replacement
or an in-place hook edit. A provider reset also schedules republication.
An exhausted token-closure retry no longer starts a full refresh that it
cannot close; the conservative fallback remains. Two test-only changes
fix shell-function environment export and a zero Windows permission bit.

The exact clean head builds on macOS and passes all unit tests plus 22
focused SHA-1/SHA-256 assertions, including same-inode and atomic-replace
hook attacks. All four Linux build variants pass; their complete normal,
reftable, no-pthreads, LeakSanitizer, and ASan/UBSan tests are running.
Fresh hosted CI and exact-head review are pending. The source-specific
results below are historical and do not replace these gates.

Release hold remains: replacing an old daemon with the current Git
did not restore cookie health on the primary performance target. The
bounded follow-up probe stopped before another full-tree scan. These
fixes do not claim to repair the separate Darwin provider-ordering issue
or establish acceptable real-checkout warm performance. Keep this PR
draft and out of the live plan until the existing promotion hold is
explicitly cleared.

Earlier correctness and recovery follow-up

The successor to aaa01b4ec1 adds focused fixes for failed provider
boundaries, optional-index recovery, and avoidable metadata work:

  • Carry a failed clean-sidecar probe into the ordinary index refresh,
    and retire the daemon token after a failed synchronization cookie.
  • Restore authenticated external-only proofs before commit and real
    checkout-index updates. Keep unchanged checkout operations no-write.
  • Recognize equivalent index-only and bounded reader requests without
    rebuilding worktree metadata they do not need.
  • Preserve the physical index during temporary watch-limit backoff;
    bound nested-attribute invalidation and reuse authenticated manifests
    for wider directory deltas. Tolerate unrelated sibling churn only
    around a held regular exclude source, retaining strict absent-source
    and namespace checks.
  • Treat damaged, replaced, or blocking optional index witnesses as
    recoverable misses. Keep corruption of the actual index fatal.
  • Close raw-index bitmap ownership gaps and encode Darwin root events
    without reading past the path terminator.
  • Decode index-extension signatures as unsigned integers, including
    speculative EOIE probes into high-bit payload bytes.

The earlier candidate was
be3eacfd884d229ea7d859e77f4624e5c69f29fa. Its normal Mac build, unit
tests, and controlled witness tests pass. Both new high-bit-signature
regressions reproduce the actual UBSan failure on the preserved previous
binary, and the corrected Linux ASan witness suite passes. The previous
head passed all 70 Linux normal-suite arms and all eight LeakSanitizer
arms. The complete exact-head Linux, sanitizer, compatibility, and hosted
CI matrix is still in progress. Results recorded below for aaa01b4ec1
and earlier commits remain historical; they do not qualify this head.

Hosted CI: pull request
and topic push.

Release hold: this PR is draft so the protected controller cannot
admit it while macOS promotion is held. A separate, pre-existing Darwin
provider-ordering issue can acknowledge a synchronization cookie before
an earlier worktree event is delivered. The provider-level failure is
confirmed; this update does not claim an end-to-end status reproduction
or a repair for that ordering issue. Promotion requires explicit Mac
clearance or a separately reviewed and authorized release-policy change.

Replacement-ref batching, additional index-offset-table optimizations,
and streaming-index work are deferred from this release candidate.

Problem

Ordinary index writers and provider resets can discard authenticated
clean-status history even when the tracked changes are safe. For example,
a cache-tree miss in write-tree can retain FSMN and FSUC while
weakening a fully bound FSCF proof. Subsequent status, snapshot, and
hook commands then rebuild the complete attribute manifest.

Recovery can also duplicate work: after discarding an unauthenticated
legacy untracked cache, read-only status can scan the worktree in bulk
and then walk the same directories again to collect untracked files.
An index writer must also preserve pending untracked-directory events:
serializing the advanced provider token without those invalidations can
make the next status omit a newly created untracked file.

Changes

  • Preserve authenticated configuration and worktree history through
    physical write-tree, ordinary index writers, reset, merge, rebase,
    replay, stash, and real primary-index lockfile hooks. Keep temporary
    indexes, active filters, attribute changes, conflicts, and unsafe index
    aliases on their existing fail-closed paths.

  • Let a genuine private copied index with partial manifest history use
    the existing conservative stat fallback. Strongly invalidate tracked
    and untracked hints without rebuilding a full worktree manifest; keep
    canonical index, lockfile, inode-alias, attribute, and filter guards.

  • Give narrowly classified cached and scoped readers a conservative
    bootstrap path. Invalidate hints instead of certifying incomplete
    metadata, avoid the full manifest and stale-checkpoint digest, and
    suppress optional index writes only when that fallback is used.

  • Repair provider-reset history before diff publishes its index. Reuse
    pinned observations, keep the index lock unheld during traversal, and
    recheck the physical index and all proof bindings before publishing.

  • Reuse a checked semantic manifest across authenticated, scoped directory
    deltas. Bound reuse by the affected attribute sources, and fall back for
    changed ignore rules, lost events, races, or incomplete proofs.

  • Share verified normalization of identical ignore-file contents, avoid an
    unnecessary reopen of anchored regular sources, and admit clean
    sidecars when configured filters are proven inactive. Preserve the same
    correctness and progress behavior without pthreads.

  • Let whole-worktree, read-only status recover discarded populated or
    empty legacy untracked caches using one complete provider-authenticated
    bulk scan. Select bulk preload automatically only for this recovery;
    explicit core.preloadIndexBulk=false still opts out. Writable status
    continues to rebuild durable authenticated untracked metadata.

  • Serialize unresolved fsmonitor directory events as invalid UNTR nodes.
    Preserve paired history across authenticated same-path regular-file
    replacements and scoped stash push/apply, without resurrecting dirty
    cache entries. Mixed or unsafe patch batches remain conservative.

  • Initialize authenticated history before am first reads or refreshes
    the index. Preserve fully bound proofs at an unchanged provider token;
    attribute-changing patches and temporary three-way indexes retain the
    existing fail-closed behavior.

  • Normalize a command-scoped submodule.recurse=false only when the
    effective setting is already false. A guarded checkout can then leave
    directly reusable linked-worktree proofs without making ordinary diffs
    repeatedly hash an old checkpoint.

  • Fix the pre-existing one-byte SHA-256 stack overflow in
    do_store_stash(). Give oid_to_hex_r() room for its terminating NUL,
    and exercise an explicit SHA-256 repository in the ordinary stash suite
    so the default SHA-1 sanitizer jobs cover the failing path.

  • Test the sidecar suite's native-provider prerequisite with a real
    daemon query. A successful status may deliberately preserve the physical
    index and publish external history instead, so the prerequisite must not
    require a physical FSMN extension.

  • Give the external-exclude FIFO tests private parent directories.
    Unrelated temporary-file churn must not invalidate their initial exclude
    proof before the tests reach the intended race barrier. Keep the
    production proof checks and all FIFO assertions unchanged.

  • Defer the full source-index digest for narrowly bounded Linux
    status --untracked-files=no calls with current, fully paired proofs.
    Keep normal content checks and the whole-index racy check. A real stat
    repair reconstructs the original digest from the pinned reader FD and
    follows the ordinary external-history and physical-index writer paths.
    Zero-checksum indexes remain bound to a process-local physical epoch;
    a changed source rolls back our lock without publishing or writing.

  • Release the temporary FSMN dirty bitmap after raw source-index
    recapture. The raw reader intentionally skips normal fsmonitor
    post-processing, so its shadow must free the bitmap before releasing
    the index. Keep the proof and physical-write decisions unchanged.

These paths do not treat directory mtimes as proof that tracked-file
contents are unchanged.

Verification

Current hold: aaa01b4ec19c0c269d0af240cdbe0a01d128b491 fixes
the 56-byte raw-index repair leak found by both workflows' files and
reftable LeakSanitizer jobs on cb6cbd4f5e. The temporary index owns an
fsmonitor_dirty bitmap that release_index() does not free. The
failing job
identifies t7519 test 102 and the allocation stack. The fix frees and
clears that bitmap at the common cleanup label, following the existing
parsed-index cleanup pattern.

A matched Linux SANITIZE=leak build reproduces the exact 40-byte
direct and 16-byte indirect leaks on unmodified cb6cbd4f5e. With only
the three-line cleanup, test 102 passes under SHA-1 and SHA-256 with both
files and reftable refs, with no sanitizer report files or deduplication
tokens. All 386 unit tests pass. An independent audit rechecks the raw
TAP, counts, exits, leak logs, build hashes, and each fixture's actual
object and ref formats.

The clean aaa01b4ec1 normal Linux build passes all 386 unit tests
and both complete 102-case status suites. Four real-provider cases cover
primary and linked worktrees under both object formats. An independent
raw audit verifies 24 status traces: eight dirty calls skip the source
digest and physical write, four clean repairs restore the original
source, and four foreign-writer followers recover read-only. All owned
fixture daemons were stopped.

The clean Mac build passes 386 unit tests, check-meson, test-lint,
nine focused status regressions per object format, two explicit-SHA-256
stash controls, and 12 real-provider sidecar assertions. The no-pthreads
build passes 386 units and 206 shell assertions; ASan/UBSan passes
386 units and all 86 selected assertions. The complete native matrix
passes 725 assertions with 11 expected skips. The full normal runtime
was restored and independently checked for sanitizer contamination.
Four write-denied comparisons on the actual checkout agree with a fresh
strong-stat oracle and leave its index, sidecars, and daemon unchanged.
Those calls missed the clean cache, so they are not a warm-hit benchmark.
The compatibility matrix passes all 154 suite/hash runs: 4,390
successes, 36 expected skips, 212 existing expected failures, and no
unexpected failures. Four final 2,050-entry real-daemon cases cover
primary and linked worktrees under both hashes, with eight verified
recovery writes and eight immutable read-only followers. The strict
ten-part gate and independent final attestation pass. Local qualification
is complete; exact-head review and protected release integration remain
pending.

The preceding clean cb6cbd4f5e build also passed the ordinary
explicit-SHA-256 stash regression under both defaults and six
real-provider sidecar cases per hash. Its no-pthreads build passed
386 units and 206 shell assertions; its ASan/UBSan build passed
386 units and all 86 selected assertions; its complete native matrix
passed 725 tests with 11 expected skips. Those results remain tied to
cb6cbd4f5e; they do not clear its hosted Linux leak.

The six files added in cb6cbd4f5e match the independently reviewed and
qualified Linux candidate byte for byte. The new Linux-only regression
fails for the intended missing optimization on immutable e94acd7cc2
and passes on the candidate under SHA-1 and SHA-256. It covers primary
and linked worktrees, a real index.skipHash zero-checksum index, a
genuinely dirty zero-stat entry, clean stat repair with external-history
publication, selected and unselected racy entries, active and required
filters, and a deterministic zero-checksum foreign-writer race.

That Linux source also passes four real-daemon primary/linked worktree
and hash-format cases, plus 19 full regression suites under both hashes:
1,713 successes, 121 prerequisite skips, eight existing expected
failures, and no unexpected failures. A separately audited large-index,
scripted-provider comparison confirms that the selected dirty command
avoids the full logical digest while preserving the independent status
oracle, physical index, and checkpoints. Real-daemon correctness and
synthetic scale performance are separate results. The index read and
whole-index racy check remain; this is not a constant-time status path.

Earlier source-specific regressions and matched controls

e94acd7cc2c7087a6e097e2b87251b04c374e59d was the preceding
review candidate. Its clean build passes 386 unit tests, check-meson
and test-lint, and nine focused status, copied-index, race, dirty-UNTR,
linked-precommit, mail-application, and native linked-checkpoint
regressions under both SHA-1 and SHA-256. The new ordinary stash test also
passes under both default hash modes; it creates a SHA-256 repository in
either mode. The corrected native sidecar prerequisite and six selected
sidecar cases, including both external-exclude FIFO tests, also pass under
both hashes, with actual provider-query evidence. The preceding 284a8ae410 head also passed all nine scoped-stash
cases and the bounded-reader matrix in both object formats with the
complete linux-TEST-vars environment.

The exact stash correction has paired ASan/UBSan evidence. An immutable
fec9824447 build passes ordinary SHA-1 stash operations, but the new
explicit SHA-256 test reproduces the stack-buffer-overflow under both
default hash modes. With only the one-byte buffer correction and that
regression, the precommit build passes 386 units and the complete ordinary
and scoped stash suites under both hashes: 308 successes, four existing
known breakages, two expected skips, and no unexpected failures. This
precommit result is not a substitute for the clean successor's final
sanitizer qualification.

The clean 687de436f1 no-pthreads gate passed 386 units and 206
assertions. Its sanitizer build passed 386 units, then the selected
sidecar suite exposed an inherited prerequisite false negative. Matched
native controls on immutable predecessor and candidate builds reproduce
the failure under both hashes: non-racy status stores external history,
leaves the physical index unchanged, and the old prerequisite incorrectly
reports no fsmonitor. Checking the real daemon response passes all four
controls while preserving that no-write behavior. The corrected
prerequisite also passes all eight selected sidecar sanitizer cases.
ef3af6c978 changes only this test setup; production is unchanged from
687de436f1.

The clean ef3af6c978 no-pthreads gate passes 386 units and 206
assertions. Its ASan/UBSan build also passes 386 units and all 82 selected
assertions under both hashes, including the ordinary explicit-SHA-256
stash regression and all eight native sidecar cases. Its hosted sanitizer
job also passes the full ordinary stash suite. These results remain tied
to ef3af6c978.

The clean e94acd7cc2 no-pthreads gate passes 386 unit tests and 206
assertions. Its ASan/UBSan build passes 386 units and all 86 selected
assertions across 12 suite/hash runs. Both default hashes execute the
explicit SHA-256 stash regression and all six native sidecar cases,
including both FIFO tests. The authoritative counts, exact sanitizer
binary and helper hashes, raw logs, and build provenance are retained.
That head subsequently passed all 12 native suite/hash runs (725
successes and nine expected skips) and all 154 compatibility runs
(4,390 successes). These remain e94 results.

The external-exclude fixture correction has a matched causal control.
Unrelated sibling churn reproduces the exact pre-barrier fast-excludes
fallback on immutable parent and candidate builds under both hashes. The
exclude file itself remains unchanged. All four controls pass when the
fixture uses its own parent directory, and still perform the real FIFO
replacement and require fast-excludes-raced. The original quiet control
also passes. The proposed test-only change then passes all 12 selected
native sidecar assertions under the pinned ef3af6c978 sanitizer build.
The specific parent mutation in the hosted failure was not recorded; the
controlled comparison establishes the mechanism without weakening it.

The new am fix passed 100 affected suite/hash runs before commit:
1,296 successes, ten existing known breakages, and no unexpected failures.
The same-token positive and changed-attributes negative pass under both
object formats. The clean 810ab59f8d commit also passed four real-daemon
primary/linked SHA-1/SHA-256 cases: immediate fully paired physical proofs,
correct commit and worktree contents, and eight immutable read-only
followers with no manifest scans or index writes. The subsequent
fec9824447 commit changes only test setup; 687de436f1 adds the separate
stash buffer correction described above. ef3af6c978 corrects the
sidecar prerequisite, and e94acd7cc2 isolates the two FIFO fixtures.

The test-only follow-ups register the new suites with Meson, correct
negative grep assertions, and give the same-stat and required-filter
controls an unchanged-staged-content scratch index that forces content
inspection. The staged-sibling fixture also clears inherited split-index
mode in the subshell that actually runs the scenario. Its original
failure and the one-line correction were reproduced under both object
formats; the full-proof assertions remain unchanged. The linked-history
fixture also ages its newly staged file before adding it. A native
future-mtime control reproduces the original racy-index checkpoint
rejection on current and parent-equivalent builds in both hashes; the
corrected fixture preserves every checkpoint and proof assertion.

The exact 284a8ae410 source also passed all 154 compatibility suite/hash
runs: 4,388 successes, 36 skips, and 212 existing known breakages, with
no unexpected failures. Its complete native matrix passed all 12
suite/hash runs: 721 successes, nine expected skips, and no failures.
An earlier restricted run stopped before establishing its initial
provider-bound proof. The unchanged regression passes on both immutable
baseline and candidate builds, under both object formats, with normal
platform permissions; that earlier restricted-run trigger remains unknown.
The exact fec9824447 head subsequently passed all 154 compatibility
suite/hash runs (4,388 successes), all 12 native runs (725 successes), and
the no-pthreads gate (204 successes). Its sanitizer gate then exposed the
pre-existing SHA-256 stash overflow, so it is not release-qualified.
Those results remain specific to their recorded source heads.

The scoped-stash source at 428d829240 passed 1,832 assertions across
120 suite/hash runs, including all nine new independent-oracle stash and
indexed-apply cases, the apply and stash compatibility suites, and
required-filter, mixed-patch, alternate-index, and pending-untracked-event
controls. The subsequent config-normalization source passed the same
nine stash cases, the bounded-reader matrix, and six focused status
regressions in both object formats.

The earlier complete recovery qualification remains specific to
3fcb4c5a12b6c699f1a85cc3f5601d6c6578d530:

  • 385 unit tests.
  • Native suites under SHA-1 and SHA-256: 697 successes, nine expected
    skips, no failures.
  • Compatibility suites: 1,195 successes, 14 skips, 91 existing known
    breakages, no unexpected failures.
  • No-pthreads status coverage: 176 successes. Selected ASan/UBSan
    coverage: 50 successes.
  • Real-daemon recovery and immutable read-only followers across both
    object formats and primary/linked worktrees, with independent
    offline-change oracles.
  • Guarded large-repository comparisons confirming complete single-pass
    read-only recovery, identical status output, and unchanged physical
    index, sidecars, and daemon.

Review and release scope

The cb6cbd4f5e PR CI run
and push CI run
reproduced the LeakSanitizer failure described above. Its style and
whitespace checks passed. The corrected aaa01b4ec1
PR CI run and
push CI run
each passed 45 jobs with two expected skips. All four files/reftable
leak-sanitizer jobs, ASan/UBSan, style, and whitespace checks pass.
The only initial PR failure was Debian's package-mirror download reset
before checkout. The same-head push Debian job passed, and one
infrastructure-only retry
passed the complete job. The original failed log remains retained.
Independent approvals for dfc55f903ca1 and e94acd7cc2 remain tied to
those commits; fresh approval and platform CI must cover aaa01b4ec1.

Release qualification still includes temporary-index commands, scoped
readers, stash push/apply, coherent packaged writers, and linked
worktrees on macOS and Linux. The release remains held until the
exact-head tests, independent review, and protected controller's
reviewed integration are complete.

The new Linux optimization does not admit other platforms, broad or
implicit pathspecs, unsafe filters, incomplete proofs, or changed
physical index epochs. Those cases retain the existing conservative
path. The separate experimental writable directory-stat reuse change
was dropped after its controlled performance comparison.

Earlier hosted CI and fixture investigations

Hosted CI on 116bc02570 found missing Meson registrations and
test-greplint errors. The next head reached substantive tests and exposed
the stale-stat required-filter control and inherited split-index fixture.
The corrected 284a8ae410 head passed all 92 executed hosted checks,
with seven intentional skips. Both complete CI workflows passed, including
all previously failing configurations, leaks, and sanitizers. The next
810ab59f8d head added the separately tested am initialization fix.
Its complete PR workflow passed; the parallel push workflow exposed the
pre-existing linked-checkpoint timestamp race described above.
fec9824447 fixes that fixture; its complete
PR CI run and
push CI run each
passed 45 jobs with two expected skips. Those workflows did not combine
SHA-256 with sanitizers. The 687de436f1 head adds the buffer correction
and an explicit SHA-256 regression that closes that gap. Its complete
push CI run
passed 45 jobs with two expected skips; the sanitizer job executed the
new ordinary stash test. The test-only ef3af6c978 prerequisite fix then
passed its complete PR CI run.
Its parallel push CI run
passed 44 jobs and failed only the existing raced-exclude-FIFO fixture on
macOS GCC, with two expected skips. The same exact head and compiler
passed in the PR workflow; both Meson jobs passed all 50 sidecar tests.
The retained failure is a successful conservative fast-excludes
fallback before the intended barrier, not a blocked FIFO read. The
matched parent-churn controls above reproduce that failure and validate
the fixture-only correction in e94acd7cc2. Its
PR CI run and
push CI run
each passed 45 jobs with two expected skips. The PR workflow's initial
macOS Clang run failed an existing fsmonitor event assertion; one
same-head rerun passed the full job. The original failure remains
retained, and its precise trigger is unproven. No production check was
weakened.

The preceding b208bc4f2298bdd37279f3b8e8835a07b82254fb head passed
all 92 hosted checks, with seven intentional skips. That includes both
workflows' AlmaLinux, linux32, and Linux test-variable jobs, which confirm
the optional-lock isolation and successful-cleanup-trap fixes.

ttaylorr and others added 30 commits August 12, 2026 17:31
In collapsed-directory mode, an untracked-cache parent may represent
an entire directory by one descendant witness. If that witness becomes
invalid or disappears, removing it without inspecting the directory can
also hide another unvisited child that remains untracked.

Compute cached validity from descendants upward after preload and
invalidate collapsed ancestors when a required child proof fails.
Before removing a stale collapsed witness, rescan its directory and
retain the parent as untracked whenever another child survives.

A focused untracked-cache test removes the cached witness while leaving
a sibling present and verifies that status still reports the collapsed
directory.

Signed-off-by: Taylor Blau <ttaylorr@openai.com>
An external attributes file can change conversion without changing a
worktree attribute manifest. Content alone is also insufficient: an
ancestor or linked target can be replaced, and a missing source is safe
to reuse only while its containing namespace remains stable.

Capture the normalized absolute-path namespace with S07/P09
before and after observing each enabled source. For a present source,
require nonblocking-open support, a regular singly linked file below
the attribute-file limit, and matching descriptor, pathname, and target
identities. Read the entire file into one allocation.

Record source configuration and contents in one framed digest, and
component and target identities in a separate namespace digest.
Recheck the complete namespace for stable missing sources. Enabled
sources inherit the namespace capture's fail-closed identity check;
disabled sources remain unobserved and safely digestible. Reject
instability rather than publishing an incomplete fingerprint.

Register the fingerprint library and Clar suite in both Make and Meson.
Tests separate content from metadata changes, detect an altered
ancestor of a missing source, preserve disabled-source digests, and
exercise both object formats. This does not select repository attribute
sources or integrate fingerprints into status.

Signed-off-by: Taylor Blau <ttaylorr@openai.com>
An fsmonitor socket is selected through the Git directory, so separate
worktree paths can reach the same daemon when they share that directory.
A client in the second worktree can then consume change history from a
daemon that watches the first, incorrectly treating changed files in its
own worktree as clean.

Hash the canonical worktree path together with its device and inode,
plus birth time and generation on Apple platforms. Cache the resulting
64-character SHA-256 identity in the daemon and attach it to every
client query. Check the identity before interpreting the requested
token; reject missing or mismatched bindings with a
cookie-synchronized trivial response that forces the ordinary refresh
path.

The protocol change must also tolerate a daemon left running by an
older Git. Such a daemon treats a bound query as an opaque token and
can return a plausible trivial response. After that exact response,
query an unbound capability command. If the daemon does not advertise
query-v1, serialize replacement through a per-socket restart lock,
stop it, and start the invoking Git executable before retrying the
bound query.

Keep quit, flush, and capability control commands unbound. Bound
daemon lifecycle retries, and fail the query instead of trusting
history when the root cannot be identified or an incompatible daemon
cannot be replaced.

Regression tests cover shared-gitdir worktree aliases, replacement of
a legacy daemon, and acceptance of a daemon that advertises a
capability superset. The replacement test also verifies that the next
status neither refreshes tracked entries nor starts another daemon.

Signed-off-by: Taylor Blau <ttaylorr@openai.com>
Even after every directory and ignore input has been validated,
collapsed-directory traversal still reopens cached subtrees that are
known to contain no untracked paths. That walk repeats work the
successful preload has already established.

Record recursive validation and whether each cached subtree contains
untracked output. In collapsed-directory mode, skip reopening a subtree
only when its directory, descendants, check-only mode, and ignore
inputs remain valid and no cached untracked entry exists. Clear the
recursive proof when directory or ignore state is invalidated.

The untracked-cache status test verifies that an unchanged empty
subtree visits no directories and that a changed descendant still
falls back to traversal and reports the new untracked path.

Signed-off-by: Taylor Blau <ttaylorr@openai.com>
S08 depends on S07 and S05. S05 now contains the former S01/P06
prerequisite, so join only the two independent histories before
applying S08.

Keep every fsmonitor test registration in sorted order in Makefile and
t/meson.build. In builtin/commit.c, wt-status.c, and wt-status.h, start
the attribute snapshot and UNTR preload before the token-closing
refresh, and preserve each state through cleanup.
A clean-status configuration digest cannot establish which index it
describes while it remains detached from the repository and index that
will consume it. External attribute content and namespace must also be
recorded before an index can reuse conversion-dependent history.

Attach a finalized, repository-bound digest at the beginning of
do_read_index(), fingerprint the system, global, and info attribute
sources, and store the resulting state on the index. Ignore an
unfinalized digest, another repository's digest, and a second
attachment. Release the state with release_index().

Extend the existing clean-status configuration unit suite to exercise
repository binding, one-shot attachment, semantic and attribute hashes,
unsafe-filter state, and index-lifetime cleanup. Register the new
production object with both Make and Meson.

Signed-off-by: Taylor Blau <ttaylorr@openai.com>
Index attachment cannot recover the configuration seen by git status or
git commit if their callbacks finish without recording it. A separate
configuration pass could also bind a different stream from the one
that established the commands' existing behavior.

Wrap each existing status or commit callback so the original callback
and clean-status digest consume the same key, value, and context.
Finalize and stage the digest after the existing configuration pass
and before either command reads its index.

Preserve determine_whence(), advice_enabled(), the original callback,
configuration order, and option handling. The index-owned attachment
and its existing configuration unit coverage are supplied by
S08/P01; this patch adds no command-specific regression.

Signed-off-by: Taylor Blau <ttaylorr@openai.com>
An index with a null trailing checksum cannot be bound to the file that
was actually read unless the platform supplies a durable file identity.
Treating a directory, multiply linked file, or unsupported platform as
equivalent would turn identity comparison into an unwarranted
correctness guarantee.

Add clean_status_identity_from_stat() for single-link regular files and
make clean_status_identity_is_durable() return true only on Apple
platforms. Keep unsupported platforms explicitly ineligible instead
of inferring durability from stat fields alone.

Register the identity object and its unit suite with Make and Meson.
The tests reject directories and multiply linked files, accept a
single-link regular file, and check the appropriate platform result.
Actual null-checksum index verification remains a separate change.

Signed-off-by: Taylor Blau <ttaylorr@openai.com>
With index.skipHash enabled, a null trailing checksum cannot prove that
verify_index_from() reopened the index that do_read_index() parsed.
Replacing the pathname between those operations can otherwise make an
unread index appear valid.

Record the identity from the index reader's existing fstat() result.
When verifying a null-checksum index on an Apple platform, compare it
with the identity from the verifier's existing file observation.
Reject an absent, nonregular, multiply linked, or replaced identity.

Leave checksummed indexes and platforms without durable identities on
their existing paths. Reuse the identity classification from
S08/P03 without adding an index-read system call. Register
the new object and unit suite with Make and Meson; the unit test
replaces the index pathname and checks the unsupported fallback.

Signed-off-by: Taylor Blau <ttaylorr@openai.com>
S05 depends on S04, all of S01 through P11, and S03. Join those
histories once before applying S05.

S05's final patch combines S01/P11's recursively valid empty UNTR
subtrees with the paired FSUC token closed by S05. S03 and S04 add
tests at the same location in t/t7527-builtin-fsmonitor.sh; keep S03's
startup and worktree-binding tests before S04's invalidation tests.
A filesystem-monitor token does not establish that saved configuration,
conversion rules, attribute inputs, or their complete manifest still
describe the current index. Accepting duplicate, stale, or partially
bound history could let status trust cached worktree state under
different semantics.

Recognize the FSCF index extension and delegate malformed-record
rejection to the bounded clean-proof parser from S07/P07.
Publish its token, configuration and semantic hashes, attribute hash,
and manifest only after the complete record validates. Reject
duplicate records, and adopt a manifest only when the current token,
hashes, complete proof flags, and filter policy all agree. Record
stronger semantic mismatches and withhold incoherent history.

Integrate validation into post_read_index_from(), release all owned
record and manifest storage with the index, and document the extension
layout. Register the history object and unit suite with Make and Meson.
A SHA-1 fixture rejects duplicate records; a SHA-256 fixture accepts
coherent history and detects a changed semantic hash.

Signed-off-by: Taylor Blau <ttaylorr@openai.com>
The index reader consumed an optional FSMN token and EWAH bitmap
before checking their complete framing. A truncated or duplicate
record could publish partial monitor state; an impossible bitmap
length could allocate out of bounds or cover nonexistent index
entries.

Validate both FSMN versions against the extension bounds, cap
version-2 tokens at 4 KiB, and check EWAH word counts, run lengths,
padding, and the final running-length word. Reject a bitmap wider
than a non-split index. Publish the token and bitmap only after every
check succeeds, and clear all existing FSMN state on failure.

Extend the read-cache helper to exercise valid records, duplicates,
truncation, invalid literal and set-bit runs, nonzero padding, and
an invalid final running-length-word pointer. Register the helper
regression in t/t7519-status-fsmonitor.sh. Malformed optional state
falls back without making the worktree appear clean.

Signed-off-by: Taylor Blau <ttaylorr@openai.com>
Reading a validated FSCF record is not enough to preserve it during a
generic index rewrite. Writing fresh token or stat bindings before the
current provider token is revalidated would claim a semantic proof that
the index has not established.

Write a newly bound FSCF extension only when configuration, attributes,
the complete manifest, the valid provider token, and its revalidated
token all agree. Otherwise preserve an existing validated record with
its token and stat bindings cleared; never serialize malformed or
missing history.

Add the extension to the existing index writer. Extend the history
unit tests to distinguish closed proofs from preserved unbound
manifests. Add a test-tool round trip and t7519 coverage that read,
write, and reread a coherent FSCF record through a real index.

Signed-off-by: Taylor Blau <ttaylorr@openai.com>
FSMN identifies the token associated with tracked fsmonitor state,
but the independently serialized UNTR extension cannot identify the
provider boundary associated with its directory snapshot. The mere
presence of both extensions cannot prove that their states agree.

Define and document FSUC as a versioned optional index extension
containing one NUL-terminated provider token. Register its reader
with index-extension dispatch; reject empty tokens, tokens longer
than 4 KiB, duplicate records, unsupported versions, truncation,
and trailing data before publishing state. Provide the matching
serializer and release the retained token with the index.

Add a read-cache helper regression for a valid record, serializer
round trip, duplicate, and truncated record. Register that helper
in t/t7519-status-fsmonitor.sh. The format is independently
testable; deciding when its token authenticates UNTR is a separate
change.

Signed-off-by: Taylor Blau <ttaylorr@openai.com>
move_index_extensions() transfers extensions to a replacement index,
but index-owned FSCF history would otherwise remain on the old state.
A generic rewrite could silently discard a validated manifest, while
sharing its storage would create a lifetime hazard.

Copy only a parsed, valid serialized record into independently owned
destination storage. Reload the saved manifest through its validated
parser, copy the existing token and hashes, and leave an absent or
invalid source untouched. Invoke the transfer from
move_index_extensions() so ordinary index release owns each copy.

Extend the existing history unit suite with a real extension transfer.
Verify the copied record and manifest, invalidate the source, reject a
second transfer from that source, and confirm that the independent
first destination remains valid.

Signed-off-by: Taylor Blau <ttaylorr@openai.com>
A well-formed FSMN bitmap and a well-formed FSUC record still do not
prove that a populated untracked-cache root and tracked entries were
observed at the same provider boundary. Trusting mismatched tokens
can suppress the directory validation needed to detect a change.

After all index extensions have been read, trust a populated
untracked-cache root only when a valid on-disk FSMN token matches
its FSUC token. An absent cache or root needs no token pairing.
Clear the untracked proof when either extension is invalid, and
write FSUC beside FSMN only when an untracked cache, a current FSMN
token, and valid untracked state are present.

Extend the existing read-cache parser regression to check matching
and mismatched tokens and to verify that a rejected FSMN clears
tracked-token validity. An invalid pair continues through ordinary
untracked-cache validation.

Signed-off-by: Taylor Blau <ttaylorr@openai.com>
Fingerprinting an external attribute file while reading the index does
not prevent the attribute parser from reopening a replaced file during
preload or status collection. Cached stat data could then be evaluated
with conversion rules that the original fingerprint did not cover.

Capture the system, global, and info attribute bytes and namespace once
and keep the immutable snapshot active from untracked-cache preload
through collection. Parse snapshot lines with the ordinary attribute
rules, including byte-order marks, embedded NULs, and line endings.
End the snapshot and release its bounded source buffers with status.

Make a failed capture or changed attribute content sticky and
invalidate fsmonitor validity and the untracked cache before ordinary
refresh. Preserve hook-provider behavior when semantic history is
absent or only the namespace changes: hooks have no closing query and
retain their reported-path contract. An observed content change still
invalidates hook-derived state.

Add t7531 integration coverage for file-parser parity, missing
attribute history, an observed hook-time attribute change, and the
hook missing-history exception. Update the existing history unit test
to exercise the public strong-mismatch predicate. The namespace-only
hook branch has no dedicated regression in this patch.

Signed-off-by: Taylor Blau <ttaylorr@openai.com>
The builtin fsmonitor client interpreted an IPC reply as unbounded
C strings. A truncated token or pathname could read past the reply;
an empty pathname could enter invalidation code expecting at least
one byte; and a slash response could be confused with a real path.

Parse the complete reply into an explicit error, delta, or trivial
outcome before exposing a builtin token or path. Require a bounded
builtin-prefixed token and fully terminated, nonempty, worktree-
relative path records. Reserve an exact single slash for a trivial
reply and retain the separate double-slash global invalidation
marker. Route malformed replies through the existing scan fallback.

Add unit coverage for valid paths, trivial and global responses,
missing delimiters, oversized tokens, empty records, absolute paths,
parent traversal, and malformed separators. Register the new unit
suite in both the Makefile and t/meson.build. Hook parsing and token
adoption remain unchanged.

Signed-off-by: Taylor Blau <ttaylorr@openai.com>
An fsmonitor token can mark an entry valid even when the index has no
coherent history for the configuration and attributes that determine its
content. With minimal stat checks, a same-size rewrite can then be
reported as clean.

Rebuild the attribute manifest for expanded indexes during IPC
bootstrap. Compare it with the current in-process or retained on-disk
manifest, invalidate only the tracked and untracked scopes whose
attribute sources changed, and preserve the last complete manifest when
a rebuild fails.

A legacy index with no FSCF extension is different from a mismatched
proof: it contains no claim about semantic history to disprove. When it
also has a valid nontrivial FSMN token with core.trustctime enabled and
full core.checkStat, clear FSMN validity and seed a forward baseline
through ordinary configured stat checks. This avoids hashing every
tracked file solely because the index predates FSCF.

The baseline still needs to finish in the bootstrap command. Preserve
the freshly-proven FSMN-valid bit on entries replaced by that refresh,
so that the accepted token does not defer the same migration work into
the next status.

Keep strong global invalidation for semantic or attribute mismatches,
weak stat settings, a present FSCF without complete manifest history,
provider reset or failure, manifest rebuild failure, and fresh indexes
without a prior nontrivial FSMN token. Retain ordinary provider handling
when reliable file identity is unavailable. The migration exception has
ordinary Git stat semantics rather than a content-proof guarantee;
same-size changes hidden by the platform's configured stat identity can
remain hidden at that boundary.

Add coverage for the forward-baseline lane, the weak-stat same-size
rewrite, and the refreshed baseline FSMN bits, along with unit coverage
for coherent, manifest-only, missing, and present-without-manifest
history.

Signed-off-by: Taylor Blau <ttaylorr@openai.com>
A validated builtin daemon response already contains complete,
nonempty, NUL-terminated path records. Sending those records through
the hook-oriented byte-by-byte offset scanner repeats framing work
and obscures the distinction between builtin and hook protocols.

Introduce apply_fsmonitor_paths() and call it immediately from the
builtin branch of refresh_fsmonitor(). Walk the already validated
path buffer, invalidate each reported path exactly once, and retain
the resulting path count.

Preserve hook token offsets, malformed-response handling, and global
invalidation. The parser and unit tests from S05/P04 provide
the bounded input; this refactor adds no separate benchmark or test
execution claim.

Signed-off-by: Taylor Blau <ttaylorr@openai.com>
An exact clean-result sidecar must remain bound to one physical index,
but resumable fsmonitor history needs to survive a format-only
rewrite by another Git implementation. It cannot use the index checksum
or file identity as its cross-implementation key.

Promote the checksummed path snapshot operations needed by an
external store. They open the named index without following its final
symlink and retain the descriptor, then require the descriptor and
current pathname to identify the same valid index. Null checksums remain
ineligible for durable snapshot pins. When fstat identity is reliable,
retain the validated reader descriptor for process-local proof epochs
only; generic certification and persisted CSHS still require a non-null
checksum.

Define a canonical digest of the ordered logical entries.  Include the
entry count and each path, stage, object ID, mode, CE_VALID,
skip-worktree, and intent-to-add state, while excluding index encoding,
cached stat data, and acceleration-only flags.  Unsupported transient
state rejects the digest rather than disappearing with the process.

Add the checksummed CSHS codec and a local-APFS-only, nofollow,
atomically-replaced store bounded to eight 16-MiB namespace slots.  This
commit has no status caller; the following history patch restores and
saves complete checkpoints through this persistence layer.

Cover both object formats, malformed and null-checksum snapshots,
pathname replacement, logical-entry bindings, malformed and independent
checkpoint namespaces, bounded retention, and idempotent writes.

A checkpoint may contain only the required FSMN and FSCF payloads.
Skip absent optional payloads rather than handing a NULL source and
zero length to memcpy(). Extend the malformed-checkpoint unit test to
round-trip that minimal valid form before its rejection cases.

Signed-off-by: Taylor Blau <ttaylorr@openai.com>
A version-2 fsmonitor hook can return consecutive NUL delimiters
after its token. The hook parser passed the resulting empty record
to pathname invalidation, whose callback inspects the last byte of a
nonempty path.

Skip zero-length hook records and count only pathnames that actually
reach fsmonitor_refresh_callback(). Preserve valid reported paths,
the existing treatment of a final unterminated hook record, and the
separately validated builtin response path.

Add a t/t7519-status-fsmonitor.sh regression whose hook emits an
empty record before a modified tracked path. Require status to
report the real modification without processing the empty pathname.

Signed-off-by: Taylor Blau <ttaylorr@openai.com>
A collapsed sparse index cannot enumerate every tracked path needed
for a complete attribute manifest. Expanding the live index would
discard the sparse representation that status is supposed to preserve.

Pin the named index with S09/P02, reread the verified index
into a scratch index, and expand only that scratch copy. Build the
complete manifest from the expanded scratch index. Check that both
the parsed scratch state and original live state still match the held
descriptor and stored trailer checksum; discard the manifest if
either check fails.

Add a sparse-checkout regression that checks the collapsed outside
entry before and after status while detecting a same-size tracked
rewrite. Extend the existing index unit case with a parsed
A-to-B-to-A mismatch. Failed snapshot validation retains ordinary
full-invalidation fallback.

Signed-off-by: Taylor Blau <ttaylorr@openai.com>
The fsmonitor failure path cleared tracked validity bits and
disabled untracked-cache monitoring, but left the separate
fsmonitor_untracked_valid proof intact. An untrusted cache token
could therefore outlive the tracked state it was meant to certify.

Extract invalidate_all_fsmonitor() and call it from the existing
failure branch. Clear every CE_FSMONITOR_VALID bit, revoke the
untracked-token proof, disable fsmonitor use for the untracked
cache, and set FSMONITOR_CHANGED only when a tracked validity bit
actually changed.

The new helper has an immediate production consumer. It neither issues
nor closes a provider token and introduces no independent benchmark.

Signed-off-by: Taylor Blau <ttaylorr@openai.com>
A clean provider response closes only the filesystem interval after
its starting token. It cannot certify a refresh that started before
the named index, configuration, attributes, and manifest were
captured, or one whose semantic inputs subsequently changed.

Capture the proof epoch before each refresh whose provider token may
be accepted. Pin the named index, starting token, repository
configuration, external attribute fingerprint, and complete
full-index manifest. Recheck those inputs after the closing query.
Record semantic history only for the accepted token; reject missing
or changed inputs and fall back to a complete refresh.

For a null-checksum index, let only the proof-epoch pin use the
process-local reader descriptor retained by the preceding patch. The
proof-only exception rechecks both the retained source descriptor's
original stat identity and the current named path when pinning and
closing the epoch. Generic certification and persisted CSHS continue
to reject the null trailer.

Always rebuild the manifest when physical history is unavailable, even
if the stored semantic configuration already matches. Without that
manifest, a trivial response invalidates the old binding and leaves
the closing query with no complete epoch to bind, so each later status
repeats the fallback.

Teach this lifecycle to restore and save complete external history
checkpoints through the preceding CSHS store. A restore validates the
logical index and all FSMN, UNTR/FSUC, and FSCF sections in scratch
state, then rechecks the pinned index before installing them together.
A save requires the same logical entries before and after status and a
closed, writable proof. Keep both paths dormant until a later patch
enables them only for a normal top-level status.

A retry inside a captured epoch can also lose a freshly acquired
CE_FSMONITOR_VALID bit when replace_index_entry() applies its generic
conservative invalidation. Mark proof-epoch refreshes explicitly and
restore only a validity bit acquired by the replacement itself. Changed
or rejected closures still invalidate those provisional bits before
falling back.

Register clean-status-epoch.c in Make and Meson alongside its first
production consumer in wt-status.c. Add scripted regressions for
capture-before-refresh ordering and recovery from unbound physical
history. Add unit coverage for the complete full-index manifest, the
restricted post-status logical-digest exception, retained-descriptor
lifetime, a stat-visible same-inode size change, and atomic path
replacement. Later activation patches cover external checkpoint
recovery and the immediate warm run.

Signed-off-by: Taylor Blau <ttaylorr@openai.com>
Untracked status used one helper both to traverse the worktree and
to copy untracked and ignored entries into status output. Checking
whether a traversal actually validated the repository's UNTR cache
requires that directory walk without copying results or recording
user-facing timing.

Factor the walk into wt_status_collect_untracked_1() with an explicit
collection flag. Return whether the traversal used the index's own
untracked cache, and populate the result lists and advice timing
only when collection is requested.

Retain wt_status_collect_untracked() as the collecting wrapper.
Every existing production caller still requests collection, so
status output and ordinary traversal behavior remain unchanged.
Token adoption and validation-only production use are not added
by this preparatory patch.

Signed-off-by: Taylor Blau <ttaylorr@openai.com>
A provider event can change a tracked .gitattributes file after
status captures its conversion inputs. Accepting the resulting token
against the previous manifest can incorrectly reuse tracked validity
when a complete status would report a content change.

Record when provider invalidation expires the current manifest and
semantic proof. Before retrying token closure, rebuild that manifest
and recapture external attribute sources when their content changes.
Keep the response token pending until the new scan and current
attribute epoch are both closed. Preserve ordinary provider handling
when file identity is unreliable.

Preserve reusable manifest history across ordinary index rewrites
without retaining expired bindings. Extend the history and manifest
unit cases and the index round-trip helper. Add a scripted regression
for tracked attribute changes. Manifest or snapshot failure still
forces a complete scan.

Signed-off-by: Taylor Blau <ttaylorr@openai.com>
A provider token obtained before a tracked or untracked scan does not
cover worktree changes racing with that scan. Publishing it as an FSMN
or FSUC proof can make a later status trust an index or untracked-cache
snapshot that was never valid at that boundary.

Keep bootstrap tokens pending while tracked entries are refreshed and
any rooted untracked cache is traversed. For builtin providers, query
again after the scans, apply intervening paths, and repeat the affected
scans until a clean boundary is found or three closing queries are
exhausted.

Treat a trivial closing reply as complete invalidation followed by
another scan; accept its replacement token only after a later clean
reply. Reject provider errors, incomplete cache proofs, and exhausted
retries with strong invalidation and complete fallback scans. Hook
providers cannot perform a closing IPC query, so accept their token
only after a complete tracked and applicable untracked collection;
reject failed or trivial hook replies.

A matching on-disk FSUC token can now authorize replay of recursive UNTR
validity established by S01. Reconstruct that validity only after the
entire extension has decoded, and only for directories without a cached
per-directory exclude digest. This lets a warm status prune known-empty
subtrees while still rechecking a changed .gitignore, including changes
made through an unwatched hardlink alias. Trust an indexed exclude's
metadata alone only when its identity is reliable and it has exactly
one link; otherwise retain the complete content-hash check.

Route both status collection and commit index refresh through the shared
closure. Preserve ordinary behavior for existing paired state,
path-limited requests, and ignored-mode collection. Cover clean and
changed closures, trivial replies, retry exhaustion, provider errors,
on-disk FSMN/FSUC publication, warm empty-subtree pruning, descendant
events, and cached exclude changes.

Signed-off-by: Taylor Blau <ttaylorr@openai.com>
Ordinary index preload assigns existing paths directly to workers. A
physical directory walk instead discovers new tasks while it runs, so
an unbounded queue can exhaust descriptors or strand tasks when worker
creation fails.

Add a directory-task queue that retains parent and child identities,
budgets descriptors against RLIMIT_NOFILE, and tracks queued as well as
in-flight work. Reserve at most 128 task descriptors, leave up to 16
for the rest of the process, and run a worker synchronously when extra
threads cannot start.

Register the common queue for Darwin in Make, CMake, and Meson. No bulk
scan is invoked from preload_index(), so existing behavior is unchanged.

Signed-off-by: Taylor Blau <ttaylorr@openai.com>
A bulk directory worker must locate each observed tracked path and
decide whether a directory has tracked descendants. Plain pathname
ordering cannot answer either question correctly for sparse indexes.

Add sparse-aware entry and descendant lookups with unseen, clean,
content-check, and fallback states. Compare observed metadata with
ie_match_stat(), and make duplicate observations fall back through an
atomic compare-and-exchange or the existing queue mutex.

Skip staged, intent-to-add, skip-worktree, removed, and otherwise
ineligible entries. Register the index classifier in Make, CMake, and
Meson without introducing deletion outcomes or content proofs.

Signed-off-by: Taylor Blau <ttaylorr@openai.com>
4e82cbf (fsmonitor: retain suspended history across backoff writers,
2026-08-17) rejects suspended history whenever GIT_INDEX_FILE is set.
But run_commit_hook() exports that variable even for an as-is commit
using the main index. A clean add --refresh in such a hook rewrites the
index and discards FSMN and FSUC. Repeated staging loses the historical
boundary needed for later recovery as well.

Authenticate the selected index against the worktree-specific gitdir's
physical index, independently of the environment-selected index path.
Accept normalized names only when they identify the same singly linked
regular file, and bind the existing proof-epoch descriptor to that file.
Carry the actual lock destination into pending-FSUC serialization so a
private output cannot inherit this exception. Reuse the same check for
the optional cache-tree write. Live-IPC admission is unchanged.
Suspended history still needs revalidation before publishing a full
proof.

Exercise genuine primary and linked-worktree hooks, normalized canonical
names, repeated staging, and an invalid cache tree. Seed the temporary
indexes used by commit -a and partial commits with an authenticated
proof and require them to reject it. Also check copied, symlinked, and
hardlinked index aliases without letting the probes rewrite the main
index.
4e82cbf (fsmonitor: retain suspended history across backoff writers,
2026-08-17) preserves authenticated index history while a watch-limit
marker temporarily disables the provider. The apply entrypoints still
enable that history only for a live IPC provider, and the patch writer
requires a currently paired token. Consequently, even a same-path patch
discards FSMN and FSUC. In git am, the initial index refresh can discard
them before the patch changes any staged content.

Attach history before the first index read during authenticated backoff.
Let the existing same-path regular-file replacement path accept a
suspended semantic baseline with pending untracked candidates. Keep the
live-token checks unchanged, and retain the whole-patch-list preflight
so any structural or otherwise unsafe patch revokes preservation for the
batch. The index writer still emits only historical FSCF flags and
an all-dirty tracked bitmap until a fresh provider boundary closes.

Cover consecutive apply --index and am operations, a mixed structural
batch, immediate index and tree checks, and recovery through a genuine
TRIVIAL response followed by a successful closure.
0d366ab (fsmonitor: recognize the main index in commit hooks,
2026-08-17) keeps temporary indexes from admitting main-index history.
That distinction is necessary, but a normal commit passes its real
index lock to pre-commit and publishes that same file on success. A
hook which refreshes the temporary index can therefore discard the
suspended history which the parent was about to publish.

Capture a historical-only checkpoint while the parent owns the main
index lock, and seal the entries actually written before running any
post-index-change hook. At successful publication, restore the history
only if the hook's final entries, canonical source, configuration, and
attribute inputs still match. Rewrite the pinned hook output through a
nested lock and the normal index serializer, preserving its stat data
and the usual racy-entry handling.

The restored tracked bitmap remains entirely dirty, and the untracked
cache still requires revalidation. No current clean proof is granted.
Partial commits and hooks which change entries or semantic inputs keep
their own output unchanged. Cover successful no-op and refresh hooks,
partial commits, and real hook mutations in both worktree layouts.
The successful-hook fixture deliberately starts with staged changes to
sibling. Its removal hook must therefore pass -f to git rm; otherwise
Git correctly refuses the removal before the publication assertions can
run. Keep the independent expected tree and the no-restoration checks.
2d2e9d0 (commit: retain suspended history across successful hooks,
2026-08-18) guards checkpoint capture with the required descriptor
operations, but its recording and restoration paths still compile on
Windows. Their direct calls to geteuid() break the native Windows build.
Using Git for Windows' placeholder getuid() would not authenticate the
owner of an index file.

Keep the ownership predicate with the checkpoint code and enable it only
where anchored file verification is available. Unsupported platforms
decline the checkpoint. Check the hook's final owner through the same
pinned descriptor when preparing the restore, so the index writer no
longer needs its own POSIX-only call. The supported-platform ownership
checks and historical-only proof semantics are unchanged.
Interactive add and scoped stash can refresh and rewrite the main index
before enabling external fsmonitor history. During watch-limit backoff,
even quitting an interactive prompt can then discard the pending token
and untracked history. The apply and am entrypoints also reject every
GIT_INDEX_FILE, including the canonical index passed by ordinary hooks.

Centralize the pre-read setup around the existing physical-main-index
check. Admit only the trusted repository namespace and an IPC provider
or authenticated watch-limit backoff, then attach the repository config
digest before reading the index. Reuse this setup in interactive add,
scoped stash, apply, and am. Allow apply's existing history-preservation
predicate to accept an explicitly selected canonical index, without
loosening its patch-shape, attribute, or filter checks.

This only prepares authenticated history reads; it does not grant
current cleanliness or authorize temporary, alternate, or aliased index
outputs. Cover scoped stash, genuine hook-invoked apply, canonical-index
am, and the corresponding temporary-index rejection cases.
A successful commit -p writes its selected entries through a private
index. That index cannot inherit proof authority from the main index, so
the interactive child correctly discards fsmonitor history. Publishing
the result then loses history even when the selection only changes the
contents of existing regular files.

Capture the parent's authenticated historical checkpoint before handing
the temporary index to the interactive child. After selection, compare
the held original descriptor with the parent's current index and the
newly opened selected file. Advance the checkpoint only when entry
names, modes, stages, and persistent flags agree, and every changed
object passes the existing same-path attribute and filter checks.
Recheck the original main index and repository configuration before
sealing the selected entries.

The ordinary successful-commit writer can then restore historical-only
state at final publication. Subsequent hooks must leave the selected
logical entries unchanged. The private index never gains authority,
structural changes still revoke history, and every tracked entry remains
dirty until a genuine token closure succeeds.

Add quit and accepted-hunk interactive regressions, private-index and
structural-selection controls, and a worktree-attribute recovery case.
The attribute test permits historical retention but requires
authoritative status and later recovery to detect the changed conversion
rules.
Replacing a regular file while fsmonitor history is suspended poisons
its cached stat data and sets CE_CONTENT_CHECK_REQUIRED. The zeroed stat
tuple survives an index write, but that flag is intentionally
memory-only. A later process can therefore mistake the poisoned tuple
for an ordinary stat mismatch. In a scoped stash, add -u stages the
correct worktree blob, then apply --index -R rejects that content-equal
entry.

Teach the shared stat/content-check wrapper to recognize a fully zeroed
stat tuple in an authenticated suspended epoch. Use the existing
ie_modified() path to verify content and refresh the stat data only
after a match. Keep the gitlink bypass and ordinary unmarked zero-stat
behavior unchanged. This recovers an obligation to check content, not
authority to declare the worktree clean.

The scoped-stash regression exercises the process boundary. Existing
zero-stat and fsmonitor-content-recovery tests cover the ordinary and
explicitly invalidated cases.
Interactive add exports the repository's selected index path as-is. In a
primary worktree that can be .git/index rather than the absolute path
used by the publication oracle. Derive the expected child spelling with
rev-parse --git-path index, keeping the physical-index and private-lock
assertions unchanged.

The worktree-attributes fixture runs in a subshell, where test_hook
cannot register its cleanup. Write the hook directly into the disposable
repository instead. Both corrections pass against the unchanged
candidate binary; neither changes the production behavior or proof
assertions.
0d1e07f (fsmonitor: prepare history before canonical index reads,
2026-08-18) teaches scoped stash pushes to retain suspended fsmonitor
history, but limits that setup to an ordinary push. A patch selection
also refreshes the real index before prompting. During watch-limit
backoff, even quitting the prompt can therefore discard FSMN and FSUC
without changing the logical index.

Stash apply and pop have the same problem when GIT_INDEX_FILE explicitly
names the real main index. Their initial refresh rejects that spelling
before any of the requested changes are applied.

Use the shared physical-main initializer before either operation reads
the index. Keep read admission independent of the eventual stash mode;
the existing writers still validate their changes, and whole-worktree
operations retain their conservative invalidation. Cover cancelled and
accepted patch selections, canonical apply and pop, and private-index
rejection.
217941b (dir: capture ignore sources beneath anchored parents,
2026-07-21) records a present but empty ignore file with a NULL buffer
and zero length. The proof-capture interface accepts that representation
but passes it to hash_object_file() after checking the source metadata.
The block SHA-256 implementation then passes NULL to memcpy() while
finishing the buffered blob header. UBSan rejects that call even though
there are no content bytes to copy.

Use the repository hash algorithm's empty-blob object ID when the
captured size is zero. Keep the descriptor and namespace checks, the
distinction between an empty file and a missing file, and the rejection
of NULL with a nonzero size. Exercise those cases with both SHA-1 and
SHA-256.
60077b9 (status: preserve semantic history across scoped and index
changes, 2026-08-11) invalidates the current clean-status proof before
saving a whole-worktree stash. That is too early for operations which
may select nothing. Quitting an unscoped stash -p, or using --staged
when only unstaged changes exist, drops the pending untracked history
without changing the index or worktree.

Keep the initial refresh authenticated, and defer this invalidation for
patch and staged-only stashes until a nonempty selection succeeds. A
successful patch selection leaves its private index in memory, so lock
and reread the original selected index before invalidating and writing
it. Real whole-worktree mutations retain their conservative invalidation
before the stash is published or the worktree is changed.

Cover both no-op paths, accepted selections with an independently staged
sibling, and copied private indexes in primary and linked worktrees.
3d7c419 (status: preserve semantic history across scoped and index
changes, 2026-08-11) taught unpack_trees() to transfer a current clean
proof to a semantically equivalent index. During a watch-limit backoff,
the index has only pending historical state, so that transfer rejects
it. A clean, same-path stash merge then drops the untracked history even
though its logical entries and subsequent recovery are correct.

Capture the authenticated main-index source before unpacking and allow
an explicit historical-only transfer after moving its extensions. Keep
the original pending untracked cache only when the complete result has
the same names, modes, stages, and persistent flags. Changed object IDs
must also pass the existing attribute and filter checks. Any unsafe
mutation permanently abandons the capture. The result remains all-dirty
and cannot claim a current tracked or untracked proof.

A checksum-disabled stash merge also replaces the source inode before
its final index-only reset. Reread that committed index through the
normal admission path instead of weakening the source-identity check.
Cover canonical and implicit apply/pop, unsafe replacements, and
successive zero-checksum publications with both object formats.
bf9f887 (unpack-trees: preserve suspended history across safe
replacements, 2026-08-18) lets a clean stash merge retain suspended
fsmonitor history. Its source must still identify the canonical index.

With index.skipHash enabled, stash's initial refresh can replace that
source inode. Restoring a distinct staged snapshot already rereads the
index after reset_head(), but ordinary apply/pop and --index without a
distinct snapshot skip that path. The merge then rejects the stale
descriptor and drops pending untracked-cache history, even though the
logical result is correct.

Reread the suspended zero-checksum index before merging when the staged
snapshot path has not already done so. Keep the existing post-merge
reread and physical-source checks unchanged. Nonzero-checksum indexes,
live fsmonitor operation, and the already-rebound staged-snapshot path
do not incur another index read.

Cover default apply/pop and the empty staged-snapshot path in both
worktree layouts, including zero-trailer publication checks and copied
private-index rejection.
3c98588 (status: reissue clean proofs after repository inputs change,
2026-08-13) lets ordinary status replace an otherwise valid sidecar when
its repository fingerprint changes. A mismatch in the separate
configuration digest returns earlier without requesting a replacement.

A persistent nonsemantic change such as status.relativePaths=false can
therefore leave a physically current but unusable sidecar in place. An
ordinary status may validate the worktree and save resumable history
without rewriting the index or issuing a new sidecar. Later invocations
then repeat the index-reading fallback.

Carry a configuration mismatch through the existing reissue signal. The
old proof still fails validation. Issuance still requires an ordinary
writable clean query and all existing proof checks. Do not treat the
mismatch as a provider reset.

Cover the transition with a zero-checksum index and scripted provider
responses. Check that read-only status leaves both files alone, writable
status changes only the sidecar, and the next identical command hits it
without reading or writing the index.
5890aef (status: reissue clean proofs after config changes,
2026-08-18) lets ordinary status replace a sidecar whose configuration
digest is stale. A temporary status.relativePaths override changes that
digest too, so alternating ordinary status with
"git -c status.relativePaths=false status" replaces the same sidecar on
each transition. On a 1,160,465-entry checkout, the final ordinary
status went from a 74 ms cache hit to a 341 ms index-reading fallback
without changing the output or index.

Exclude only command-scoped status.relativePaths from the proof digest.
This is a presentation choice: fast status builds a fresh wt_status and
prints with the current prefix instead of reusing cached output. Keep
persistent configuration, missing scope metadata, other keys, and the
legacy config-epoch admission unchanged. Flush pending filter settings
before omitting the key so separated fragments cannot become a complete
disabled-filter override.

Cover the scope and filter boundaries with both hash algorithms. Retain
the persistent-config repair test, require temporary A/B/A invocations
to hit without index I/O or sidecar replacement, and compare dirty
subdirectory output with independent status oracles.
@ttaylorr-oai
ttaylorr-oai marked this pull request as ready for review August 19, 2026 04:19
@ttaylorr-oai
ttaylorr-oai marked this pull request as draft August 19, 2026 05:40
5890aef (status: reissue clean proofs after config changes,
2026-08-18) lets a writable status replace a proof after persistent
configuration changes. A temporary color.ui or core.quotePath setting
also changes the digest, so it replaces the default proof. The next
default status then reads the index again and replaces that proof in
turn. Before the reissue change, the temporary command missed but left
the default proof usable.

Extend the command-scoped presentation exemption from 44402f3
(status: ignore command-scoped relativePaths in clean proofs,
2026-08-18) to these two exact keys. A clean proof stores no rendered
output. The ordinary configuration parser and current status printer
still apply each setting. Keep persistent and unknown-scope entries in
the digest, flush incomplete filter overrides before the exemption, and
leave legacy tracked-policy admission unchanged.

Cover writable default/override/default sequences without index I/O or
sidecar replacement, and compare actual color and pathname quoting
against the ordinary status path. Retain the persistent-config reissue
and filter-boundary controls.
2c6c284 (status: retain clean proofs across presentation overrides,
2026-08-19) puts environment assignments directly before test_must_fail.
That form is not portable for shell functions, so test-lint rejects the
new invalid-configuration cases.

Use test_env to export the settings in a subshell. Keep the expected
parser failures, empty output, and unchanged index and sidecar checks.
@ttaylorr-oai
ttaylorr-oai marked this pull request as ready for review August 19, 2026 18:38
@ttaylorr-oai
ttaylorr-oai force-pushed the codex-unstable branch 2 times, most recently from f906482 to 7e54125 Compare August 24, 2026 18:44
@ttaylorr-oai ttaylorr-oai added kind:review-only Reviewed topic; do not merge this pull request build:codex-unstable Preview Codex Git build codex:integrated Exact planned head is in the published build labels Aug 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

build:codex-unstable Preview Codex Git build codex:integrated Exact planned head is in the published build kind:review-only Reviewed topic; do not merge this pull request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants