Repository navigation
Prepare v1.2.3 with native watcher, reclamation, and query fixes - #207
Merged
Shengyu Fu (shengyfu) merged 31 commits intoOct 18, 2026
Merged
Conversation
Reverify closed managed members against their original bounded block proofs under native write/delete exclusion. Keep the original seal, identity and ownership checks, and charge generation verification to its existing work permit. Reject timestamp-restored rewrites and unverified reobservations; add native metadata-only and protection regressions. Co-authored-by: Copilot App <[email protected]> Copilot-Session: cff18193-91c7-4186-9e2d-7dfa8433e29d
Reuse the existing WorkPermit deadline and checks for admitted Windows verification. Bound only unreserved observations with current policy, and cover a policy timeout reduction after admission. Co-authored-by: Copilot App <[email protected]> Copilot-Session: cff18193-91c7-4186-9e2d-7dfa8433e29d
Release extraction headroom and consumed input tables instead of retaining cumulative per-posting estimates. Admit hash-table growth with old and replacement capacity both charged, preserve content and mask semantics, and cover CRLF checkpoint/refresh under the existing budget. Co-authored-by: Copilot App <[email protected]> Copilot-Session: cff18193-91c7-4186-9e2d-7dfa8433e29d
Use bounded recursive-root subscriptions on Windows and macOS while retaining per-directory accounting elsewhere. Preserve native coverage after kernel-initiated watch retirement, expose subscription counts, and cover default-budget multi-worktree events, metadata, fallback, and cleanup. Co-authored-by: Copilot App <[email protected]> Copilot-Session: d2a0130e-af45-49cc-ac06-2c1da73d9d13
Update both first-party lockfiles and release instructions; preserve historical v1.2.1 deprecation and candidate evidence. Co-authored-by: Copilot App <[email protected]> Copilot-Session: cff18193-91c7-4186-9e2d-7dfa8433e29d Cherry-picked-from: 2f9d21f Co-authored-by: Copilot App <[email protected]> Copilot-Session: d2a0130e-af45-49cc-ac06-2c1da73d9d13
Size replacement growth after removing old entries from the unpublished clone. Track backing-capacity watermarks for retained maps, posting sets, and bookkeeping instead of releasing tombstone storage. Add deterministic red/green replacement and deletion regressions, rehash/pressure coverage, and changed-CRLF checkpoint parity. Co-authored-by: Copilot App <[email protected]> Copilot-Session: cff18193-91c7-4186-9e2d-7dfa8433e29d Cherry-picked-from: 6627cef Copilot-Session: d2a0130e-af45-49cc-ac06-2c1da73d9d13
Resume collection only from an evidenced bounded continuation, preserving exact reclamation accounting and native sharing assertions. Discard epoch-proven CLI fallback scans and wait within the existing managed busy budget before comparing indexed results. Add deterministic deadline and real native Git-exclude controls, and clarify the lifecycle contracts. Production code and the sealed 4e2a2a8 candidate are unchanged. Co-authored-by: Copilot App <[email protected]> Copilot-Session: d2a0130e-af45-49cc-ac06-2c1da73d9d13
Discard cached member authentication only after other shutdown blockers are clear. Preserve natural expiry, active verifier protection, and existing resource limits. Cover bounded stage reclamation, unchanged-cap readmission, restart persistence, and generated-store cleanup. Co-authored-by: Copilot App <[email protected]> Copilot-Session: 23051c7b-9d3c-4f54-9d4e-72897ee5a82a
Amortize rooted ancestor opens across consecutive sibling candidates without reordering results. Reuse reconciliation readers while preserving snapshot validation, generation and root pins, final ancestor checks, query deadlines, and response limits. Keep singleton candidates on the direct rooted path. Add local, legacy, and managed scan-parity coverage plus containment, failure-classification, and pin-lifetime regressions. Co-authored-by: Copilot App <[email protected]> Copilot-Session: 417710f4-d898-45a9-ade1-dd8f33592373
Send the effective line-count mode to servers and omit unused matching-line payloads for both count modes. Preserve original match statistics, binary visibility, multiline line-count semantics, legacy ordinary-server rendering, and existing response limits. Validate projected rows before scope filtering or output, and cover malformed replies, exact byte budgets, dense counts, and local/ordinary/shared route parity. Co-authored-by: Copilot App <[email protected]> Copilot-Session: 417710f4-d898-45a9-ade1-dd8f33592373
Integrate current main without rewriting the exact native watcher, reclamation, rooted query batching, count projection and qualification commits. Upstream's Windows publication/CRLF corrections are already present. Bump the workspace and first-party lock versions and release examples; apply rustfmt-only layout to the supplied reclamation tests. No production reimplementation, release workflow or freshness policy change. Co-authored-by: Copilot App <[email protected]> Copilot-Session: d2a0130e-af45-49cc-ac06-2c1da73d9d13
Preserve full-content response-cap rejection and compare fallback output byte-for-byte with a scan. Assert compact count replies stay on the shared daemon and preserve filename/count parity. Co-authored-by: Copilot App <[email protected]> Copilot-Session: d2a0130e-af45-49cc-ac06-2c1da73d9d13
macOS deliberately discards verified prefixes after each truncation. Use smaller temporary stages and adjust pinned generations only on macOS, preserving pressure/recovery assertions, the 128-pass bound, all per-pass/resource limits, and the original Windows/Linux fixture inputs. A conservative-prefix model reproduces the old failure and the corrected fixture still fails against the pre-fix idle behavior. Co-authored-by: Copilot App <[email protected]> Copilot-Session: d2a0130e-af45-49cc-ac06-2c1da73d9d13
An external on-access reader that opens a verified member breaks or refuses the collection's exclusive F_WRLCK lease. Reproduce both variants in-process on Linux, isolated in a child test process, and assert the observed fail-closed signatures followed by recovery once the reader is gone. Co-authored-by: Copilot App <[email protected]> Copilot-Session: d2a0130e-af45-49cc-ac06-2c1da73d9d13
A typed OwnerGuard::claim failure in `tgrep shared owner-hold` was flattened
to exit 2 plus stderr text, so a supervising client could not distinguish
retryable not-committed catalog contention from an unknown failure.
Before `holding:true`, write exactly one newline-terminated
`{"ok":false,"error":...}` stdout line carrying the managed failure as
returned, then keep the existing exit status and stderr diagnostic.
Claim-file read/size/parse errors stay unclassified and unframed, and no
failure after the handshake is framed. The success handshake, guard
lifetime and SQLite writer/busy limits are unchanged.
Add CLI-boundary regressions: a held catalog writer yields a busy/catalog-io
retryable not-committed frame, and the same claim then holds and exits on
stdin EOF; a live holder yields owner-guard-already-held; an ended lifetime
yields a non-retryable stale-identity frame; malformed claims emit no frame.
Co-authored-by: Copilot App <[email protected]>
Copilot-Session: d2a0130e-af45-49cc-ac06-2c1da73d9d13
Healthy native-watched managed views had no periodic full verification, so a silently missed watcher notification could leave results stale indefinitely. Once the existing --poll-interval has elapsed since a view's last completed full pass, the scheduler now queues one ordinary reconcile operation with verify: true, without invalidating the view. The verification runs under the unchanged operation deadline and CPU/input/handle budgets, reads and hashes every admitted file, and compares membership and visibility with the published snapshot while queries keep using it. If nothing differs it publishes nothing. The first detected difference marks the view stale, closing readiness, and the pass publishes a full repair. Deadline or cancellation leaves the view ready but unverified until the next interval; other errors, watcher uncertainty, explicit invalidation, concurrent events and stale policy keep their fail-closed semantics. Status adds last_verification and native verification policy fields, and hello advertises serving-native-periodic-verification. Docs are updated. Core tests cover unchanged availability, missed-change repair, concurrent invalidation, read failures, cancellation and policy races. CLI tests cover the interval boundary, mid-verification serving and missed nested-directory repair with scan parity. Co-authored-by: Copilot App <[email protected]> Copilot-Session: d2a0130e-af45-49cc-ac06-2c1da73d9d13
Co-authored-by: Copilot App <[email protected]> Copilot-Session: d2a0130e-af45-49cc-ac06-2c1da73d9d13
Cancellation, deadline and resource pressure keep a serving verification ready only when it observed no difference. Add a managed regression for both interleavings: a membership difference observed by discovery before cancellation at a paused batch, and a content difference observed by the full read before cancellation at publication. Both must leave the view not ready with a full refresh required. Co-authored-by: Copilot App <[email protected]> Copilot-Session: d2a0130e-af45-49cc-ac06-2c1da73d9d13
Managed v2 indexes can opt out of the binary-extension filename heuristic,
so text such as an ASCII RTF .doc is indexed and searched like ripgrep. NUL
classification, ignore, hidden, size and filename rules are unchanged.
- IndexingProfile gains an optional eligibility ("content-only"); the default
is omitted, so default keys, storage names and hello.profile are unchanged.
- Worktree verification applies the extension rule only for the default.
- Daemons advertise content-eligibility-profile; v1 leases reject it.
- `--content-eligibility content` searches use only a matching managed view.
The client and server refuse mismatches in either direction, and the client
scans with the requested rule. Ordinary servers and local indexes are
bypassed. index/serve reject the flag.
Co-authored-by: Copilot App <[email protected]>
Copilot-Session: d2a0130e-af45-49cc-ac06-2c1da73d9d13
Invalidation schedules automatic reconciliation even with --no-watch, so the explicit refresh may legitimately run a full pass after the hints are consumed; assert the preserved content-only profile instead (hint-only processing stays covered in tgrep-core). Housekeeping can remove a released owner record before re-hold, which reports owner-retired; both reason codes remain stale-identity, non-retryable and not-committed. Co-authored-by: Copilot App <[email protected]> Copilot-Session: d2a0130e-af45-49cc-ac06-2c1da73d9d13
Capture the original failed operation and one best-effort view status within its existing wait budget. Report stop-request, observed acceptance, and post-accept exit timing without changing the shared shutdown deadline. Add deterministic diagnostic-output and loopback controls; no production changes or whole-test retries. Co-authored-by: Copilot App <[email protected]> Copilot-Session: d2a0130e-af45-49cc-ac06-2c1da73d9d13
ripgrep records each file's bytes_printed before writing its end message, and the summary sums those per-file stats. tgrep added the end line to the running file total after serializing it, so summary.bytes_printed exceeded the sum of end records by every end line on scan, local-index, server and managed v2 routes. Stop counting the end line and keep bytes_printed in the JSON parity comparisons; only elapsed timing remains volatile. Co-authored-by: Copilot App <[email protected]> Copilot-Session: d2a0130e-af45-49cc-ac06-2c1da73d9d13
Carry per-candidate work receipts from the server matching boundary, including candidates that emit no rows. Populate JSON summary searches/bytes_searched and each file's bytes without extra reads or scan-equivalent guesses. Validate receipts before output and charge them to existing shared response budgets. Keep elapsed conventions unchanged and document backend-work versus result counters. Co-authored-by: Copilot App <[email protected]> Copilot-Session: d2a0130e-af45-49cc-ac06-2c1da73d9d13
…ttach The native watcher test assumed every initial views.attach succeeds on its first operation. The managed contract documents a terminal failed result with both commitment fields not-committed, category busy, reason view-preparation-invalidated and retryable=true; the sealed 272c059 run hit it. Factor the managed_faults exact-state recovery helper into managed_attach.rs with an explicit per-call-site allowance. Catalog callers keep the unchanged catalog-io allowance and messages. The watcher call site alone permits the exact preparation-invalidation outcome: it inspects the old operation, replays the old token unchanged, recovers the same root/profile/pending identity, uses a fresh token only after the old terminal result is resolved, keeps the original 30-second deadline shared across attempts and the existing 8-attempt ceiling, and requires one lease bound to the first operation. Any other outcome still fails with view diagnostics. No product code changes. Scripted controls cover recovery, rewritten replays, extra leases, exhaustion, deadline, rejected outcomes/identity/responses and the unchanged classifier. Co-authored-by: Copilot App <[email protected]> Copilot-Session: d2a0130e-af45-49cc-ac06-2c1da73d9d13
Add a managed-test-hooks-only fault at the post-checkpoint preparation boundary. Its bounded invalidation action advances and invalidates the real view slot for each of three preparation attempts, producing the actual retryable, not-committed view-preparation-invalidated operation receipt. Production builds do not compile the action or its preparation handling. Exercise the watcher recovery helper against a real managed daemon: verify the failed receipt and three fault hits, then require token 2 to complete against the same pending view and original lease, with native readiness, one lease, indexed search, scan parity, and normal cleanup. Co-authored-by: Copilot App <[email protected]> Copilot-Session: d2a0130e-af45-49cc-ac06-2c1da73d9d13
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Critical stale-view and unverified-output defects, moderate search/verification handling issues, and scope/compatibility documentation discrepancies remain unresolved.
3 open issues
What changed in this PR
Prepares tgrep v1.2.3 with shared-index verification, query optimizations, lifecycle fixes, and protocol updates.
Changes:
- Adds periodic native-view verification and content-eligibility profiles.
- Batches sibling reads, compacts count replies, and updates JSON search statistics.
- Preserves collection progress, frames owner-hold failures, and updates tests, documentation, and release versions.
| File | Description |
|---|---|
tgrep-core/tests/managed_lifecycle.rs |
Tests batch pins and collection continuation. |
tgrep-core/src/worktrees/tests.rs |
Tests verification, batching, and eligibility profiles. |
tgrep-core/src/worktrees.rs |
Implements serving verification and candidate batches. |
tgrep-core/src/rooted/tests.rs |
Tests batch bounds, lifetimes, and ancestor validation. |
tgrep-core/src/rooted.rs |
Adds scoped sibling-read APIs. |
tgrep-core/src/managed/views.rs |
Integrates verification into managed reconciliation. |
tgrep-core/src/managed/maintenance.rs |
Preserves verification progress during busy idle probes. |
tgrep-core/src/managed/faults.rs |
Adds preparation-invalidation test hooks. |
tgrep-core/src/managed/collection.rs |
Adds reclamation and Linux lease regressions. |
tgrep-core/src/generations/mod.rs |
Adds content-eligibility profiles. |
tgrep-cli/tests/shared_daemon/managed_live.rs |
Tests periodic native verification. |
tgrep-cli/tests/shared_daemon/managed_faults.rs |
Reuses attach-retry helpers. |
tgrep-cli/tests/shared_daemon/managed_diagnostics.rs |
Adds bounded failure diagnostics. |
tgrep-cli/tests/shared_daemon/managed_clients.rs |
Tests owner-hold failure frames. |
tgrep-cli/tests/shared_daemon.rs |
Expands batching, count, and response-limit coverage. |
tgrep-cli/tests/ripgrep_parity.rs |
Tightens JSON byte-accounting comparisons. |
tgrep-cli/tests/ripgrep_compat.rs |
Tests statistics and compact-count compatibility. |
tgrep-cli/src/serve/shared/server.rs |
Batches reads and returns search-work statistics. |
tgrep-cli/src/serve/shared/protocol.rs |
Supports eligibility-aware view discovery. |
tgrep-cli/src/serve/shared/protocol_v2.rs |
Frames typed pre-handshake claim failures. |
tgrep-cli/src/serve.rs |
Projects compact counts and search statistics. |
tgrep-cli/src/search.rs |
Updates routing, batching, and response validation. |
tgrep-cli/src/output.rs |
Corrects JSON byte accounting. |
tgrep-cli/src/main.rs |
Adds eligibility options and verification help. |
SHARED_WORKTREE_INDEXES.md |
Documents lifecycle and protocol changes. |
SHARED_INDEX_BENCHMARKS.md |
Clarifies verification measurement boundaries. |
README.md |
Documents search, verification, and watcher behavior. |
fuzz/Cargo.lock |
Updates the core release version. |
CONTRIBUTING.md |
Updates release examples and watcher-test guidance. |
Cargo.toml |
Bumps the workspace to 1.2.3. |
Cargo.lock |
Updates first-party release versions. |
AGENTS.md |
Updates operational contracts and examples. |
Review effort: Max
Co-authored-by: Copilot App <[email protected]> Copilot-Session: d2a0130e-af45-49cc-ac06-2c1da73d9d13
Linux procfs can report ESRCH when a task exits after its stat file is opened. Treat that observation as task disappearance in the test-only assert_dead helper while preserving live-state deadlines and unrelated errors. Cover the real open/exit/read boundary and scripted live/error observations. Co-authored-by: Copilot App <[email protected]> Copilot-Session: d2a0130e-af45-49cc-ac06-2c1da73d9d13
open_generation takes the SQLite catalog writer lock with the fixed 250 ms wait, so a concurrent writer can legally return retryable, uncommitted busy/catalog-io (SHARED_WORKTREE_INDEXES.md). The legacy-reader test and the guarded reader subprocess assumed first-attempt success with a bare unwrap. Route both through the existing exact-class owner-claim retry helper, made type-generic with its unchanged 10 s fixture bound and 20 ms spacing. Add a real-boundary control that holds an owned BEGIN IMMEDIATE writer: it fails with the old bare unwrap (same payload as CI run 39065427720), passes with the helper, keeps an expired bound at one attempt, and stops on a non-Busy error. No production changes. The historical lock holder in the CI sample remains unknown. Co-authored-by: Copilot App <[email protected]> Copilot-Session: d2a0130e-af45-49cc-ac06-2c1da73d9d13
Drive the real on-disk indexed CLI route with pipe backpressure so the search is held inside the first sibling while the test replaces their parent. The former batch loop printed the next sibling through the replaced parent before failing ancestor verification; the streaming route resolves it again and prints only the replacement. Unix-only: Windows pins an open parent against replacement. Co-authored-by: Copilot App <[email protected]> Copilot-Session: d2a0130e-af45-49cc-ac06-2c1da73d9d13
Add a hooks-gated daemon integration test that pauses a real collections.start operation at object-withdrawn, object-pending-deletion, member-intent-saved, member-before-io and collection-before-complete, services operations.cancel while the worker is parked, and then releases it. The cancelled receipt must be terminal with no delete credit after the cancel point, unchanged checkpoint members for pre-IO points, a discarded verification context, namespace retained memory back at its pre-collection baseline, and an idempotent replay. Fresh operations then resume from the saved cursor and finish collection with scan parity. Removing the cancellation recheck after MemberBeforeIo makes the test fail with 1475 bytes credited after cancellation at member-intent-saved. Co-authored-by: Copilot App <[email protected]> Copilot-Session: d2a0130e-af45-49cc-ac06-2c1da73d9d13
A bounded collection pass may complete before an armed member/object barrier: an escaped reader of the object skips it without error, or the pass exhausts its elapsed budget and returns a resumable cursor. Windows CI run 39092617708 hit this in the member-before-io iteration of collection_crash_boundaries_preserve_live_views_and_resumable_progress (hook armed with 0 hits, one completed pass, 0 errors, 0 verification pages). The log cannot distinguish the two causes. Fault tests now start continuation passes with fresh tokens only after a classifier accepts the short pass (completed, uncancelled, errorless, no omitted details, target detail limited to object-readers-active, or an exceeded budget with a cursor). Pass completion is observed through the in-memory maintenance counters so the poller never reads the catalog while a pass may hold it at the credit barrier. Recovery assertions keep their committed expectations, accepting an interrupted, uncommitted continuation only at removal barriers after earlier passes deleted every member. Adds a classifier unit test and a witness that drives both legitimate paths deterministically. No production or budget changes. Co-authored-by: Copilot App <[email protected]> Copilot-Session: d2a0130e-af45-49cc-ac06-2c1da73d9d13
Shengyu Fu (shengyfu)
marked this pull request as ready for review
October 18, 2026 05:33
Shengyu Fu (shengyfu)
deleted the
shengyfu-large-worktree-native-watching
branch
October 18, 2026 05:34
Comment on lines
+2266
to
+2271
| if matches!( | ||
| error.category, | ||
| crate::managed::ErrorCategory::Cancelled | ||
| | crate::managed::ErrorCategory::Deadline | ||
| | crate::managed::ErrorCategory::ResourcePressure | ||
| )) |
|
|
||
| /// Operation budget exhaustion carries no evidence about checkout content. | ||
| fn is_budget_failure(error: &WorktreeError) -> bool { | ||
| matches!(error, WorktreeError::Index(crate::Error::Managed(error)) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Summary
Prepare v1.2.3 with the qualified native corrections missing from main:
holding:truehandshake,tgrep shared owner-holdwrites one{"ok":false,"error":...}stdout line carrying a typed claim failure, so supervising clients can act on catalog contention without parsing stderr. Exit status, stderr, the success handshake and SQLite limits are unchanged.The Windows publication/CRLF accounting and replacement/allocation-watermark corrections from #205 are already in main and are not duplicated. Original qualified commits and the current main remain in the history.
Preserved contracts
No downstream invocation or resource-policy change is required for native watching. The default watch/max-view policy, worker/resource limits, operation/query deadlines, 64-MiB response cap, ownership/authentication, containment, and reader/root-handle drain remain unchanged. Genuine native errors or exhausted OS resources still trigger fallback.
The owner-hold change adds no retry, timeout, writer serialization or collection change. The 250-ms catalog writer wait, the owner guard and its lifetime, exit status 2, the stderr diagnostic and the success handshake are unchanged. Clients that accept only
holding:truestill fail closed.Healthy native views remain event-driven and also receive serving periodic full verification for SHARE-7. Once
--poll-intervalseconds (default 120) have elapsed since the last completed full pass, the scheduler queues averify: truereconciliation under the existing operation, deadline and resource budgets. Queries continue using the published snapshot while an unchanged pass runs. A detected difference closes readiness and publishes a repair; deadline exhaustion or cancellation leaves the view ready but unverified and retries after the next interval. This recurring full-tree read is an intentional behavior and resource change in this PR, not part of the immutable benchmark. The downstream real-corpus witness remains a separate qualification gate. Explicit polling, watcher-failure fallback, explicit refresh, and--no-indexretain their documented meanings.Follow-up corrections
Current head is
300b1c6ce5995a4ae31ded6419b18be5c034032d. Its delta from the sealed-source integration commit1877ee647ad72f3bd933025783c570dc59eed4cais three test-only commits plus one production protocol change (300b1c6, described in the next section). The sealed1877ee6executable does not contain it:d123abc01765a5b11a772830af4b9dce16dd76de: the old oversized-response test required-cto scan even though compact counts intentionally remain indexed. Preserve the raw full-content cap error and byte-for-byte CLI content fallback parity, then separately require compact indexed counts and exact filename/count parity. The original failed assertions on all three CI platforms are retained.586c6e5b2d9ca2a71c0e42c23db27250eaa12619: correct the macOS-only staging fixture. macOS deliberately discards verified prefixes after each authorized truncation. Re-verifying the original 3/2-MiB stages after every 64-KiB truncation exceeds the fixed 128-pass allowance; this caused the d123abc macOS failure, with zero stages removed instead of two.288c08770ea90129507045888c74235a7986ed51: add two Linux-only regressions that deterministically reproduce the local lease failures described below (no production change).300b1c6ce5995a4ae31ded6419b18be5c034032d: frame typed owner-hold failures before the handshake (production change, below).The corrected macOS fixture uses 768/512-KiB temporary stages and two 2.5-MiB pinned generations. All assertions, concurrency, the 128-pass bound, the 200-ms/4-page/256-KiB per-pass bounds, and the 8-MiB staging/16-MiB storage allocation stay unchanged. The original Windows/Linux inputs are unchanged. It still proves initial pressure, retained progress across busy probes, complete reclamation, exact byte accounting, pinned-generation integrity, reopened admission, and restart persistence.
A conservative-prefix diagnostic reproduced the old macOS fixture's zero-versus-two failure and passed the corrected geometry. Restoring the original idle bug still failed both prefix-preservation assertions. Those diagnostics are not substituted for native macOS qualification.
Owner-hold pre-handshake failure frame
A downstream Windows integration run killed and replaced a managed daemon. An independent client's
shared owner-holdthen lost the catalog writer race:OwnerGuard::claimtakesBEGIN IMMEDIATEwith the fixed 250-ms busy wait and returned a typedbusy/catalog-io/retryable/not-committederror. The CLI reduced that to exit 2 andtgrep: catalog-io: database is locked. A client that retries only structured retryable, not-committed failures, and does not text-match stderr, correctly treated it as unknown and stopped. Released v1.2.2 has the same path.300b1c6keeps the typed classification. WhenOwnerGuard::claimfails before the handshake, owner-hold writes exactly one newline-terminated compact JSON line to stdout, using the managed RPC failure envelope, then exits as before:{"error":{"category":"busy","committed_state":"not-committed","detail":"database is locked","reason_code":"catalog-io","retryable":true},"ok":false}errorobject is the managed failure as returned (category,reason_code,retryable,committed_state,detail, plusoperation_id/current_versionwhen present). Retryability is never inferred. Claim acquisition writes nothing to the catalog.busy/catalog-io,busy/owner-guard-already-held(another live holder),busy/namespace-admission-<state>, and non-retryablestale-identityfailures such asowner-lifetime-ended.holding:true. Write errors on the frame are ignored; exit 2 and stderr remain.retryable:truewithcommitted_state:"not-committed", after confirming that helper exited, within its existing bounds. A missing or unparsable line, or any other classification, is a failed handshake.Local CLI-boundary evidence on Windows (actual source of
300b1c6):cargo fmt --all -- --check; Clippy fortgrep-cliwith all targets, test hooks,-D warningsowner_holder_frames_catalog_contention_and_the_same_claim_then_holdsholds an SQLite immediate transaction on the fixture's own catalog until the failed helper exits, asserts the single busy/catalog-io/retryable/not-committed frame with no holding line, then releases it. The same claim file then gets the normal handshake, registers, releases, and exits successfully on stdin EOF.owner_holder_frames_typed_refusals_without_inferring_retry_safetycovers a live second holder (owner-guard-already-held, holder unaffected), a non-retryableowner-lifetime-endedframe after the guard ended, and malformed/unknown-field claims with empty stdout.Actual-head validation
All GitHub checks on
300b1c6pass after one Windows test-job re-run; the attempt-1 failure is retained and unexplained (see the table). The local Linux lease failures below are attributed to an external on-access reader on the local WSL host. This PR remains draft because of the release boundaries at the end.300b1c6: full CI matrix, Linux/macOS/Windows testsmanaged::faults::control_unlink_crashes_preserve_sibling_proofs_and_credit_only_the_ended_owner:shared_daemon/managed.rs:728, "stopped daemon did not exit" (82 passed, 1 failed). That assertion's 10-s budget starts at the firststop-if-idlerequest, so the log cannot separate busy-retry time from exit time. The test claims owners in process and never runsshared owner-hold; the daemon shutdown path is unchanged from v1.2.2 and288c087. The message does not appear in the previous 40 failed CI runs. Attempt 2 passed (83 passed). The attempt-1 failure is retained and not explained here.300b1c6: Clippy, formatting, Windows release build and all three lifecycle measurement jobs300b1c6actual source, local Windows: the six owner-hold CLI tests288c087: full CI matrix, Linux/macOS/Windows testsmanaged::bounded_ignore_inputs_preserve_scan_parity_and_leave_sibling_views_serviceable(shared_daemon/managed.rs:664, attachoperation-deadline). The new tests are Linux-only, so Windows built the same code as586c6e5. Attempt 2 passed. The attempt 1 failure is retained and not explained here.288c087: Clippy, formatting, Windows release build and all three lifecycle measurement jobs586c6e5: full CI matrix, Linux/macOS/Windows tests586c6e5: native macOS core suite, including the corrected staging regression586c6e5: all three native lifecycle measurement jobs586c6e5: Windows Rust 1.88 release build, Clippy, formatting, code analysis and CLA checks586c6e5actual source: local Windows original two-thread core selection586c6e5actual source: local Linux original two-thread core selection1877ee6: broader focused Windows qualification1877ee6: broader focused Linux qualification1877ee6: exact unsigned Windows executable without test hooksThe immutable local executable remains attributed to
1877ee6, not the newer heads; it does not contain the300b1c6owner-hold frame. It reports CLI/FileVersion/ProductVersion1.2.3(fixed version1.2.3.0), SHA25651d30a8de3e9234766300d65c72b0339a11fc2f3bf03694ccea61a237f6014e4. Its strict native fixture used three worktrees with 1,281 directories each and subscription counts[1, 2, 2]. It is a local unsigned PR build, not an official release; no sealed artifact was rebuilt or overwritten.Local Linux lease failures: external on-access reader
On
1877ee6, the original two-thread union failedmanaged::collection::tests::busy_idle_probe_preserves_cached_proof_for_external_workatcollection.rs:463, the resumed collection'serrors == 0assertion. The error wasmember-verification-invalidated: "the exclusive file lease was broken or revoked", with zero verification/deletion credit and discarded cached proof. One586c6e5run failed the same assertion and also failedcompeting_control_cleanup_keeps_the_selected_object_pending_and_resumablewithmember-writer-or-mapping-active. Neither failure exceeded its elapsed budget.These errors are the product's mappings of the two kernel outcomes when another process opens a freshly written file while the product holds, or tries to take, its Linux
F_WRLCKverification lease. A read open downgrades the lease toF_RDLCK, andcheck_protectionrejects it as broken. An open file description present at acquisition makesF_SETLEASEreturnEAGAIN, which is reported as busy. In both cases the product fails closed as designed. The broken-lease failure was directly correlated below; theEAGAINfailure matches the probe's refused acquisitions but was not separately correlated in the test selection.Evidence (the test-selection runs kept the original two-thread concurrency, assertions and bounds):
/tmp, 169/200 on/dev/shmtmpfs and 108/200 on a private user-namespace tmpfs. All but one anomaly coincided with that foreign read. Delaying the lease by 4, 8, 16, 32, 64 or 128 ms (100 trials each) or 1 s (60 trials) after the write produced no anomalies; the foreign read then completed before acquisition. The reader is outside the distro's PID namespace and adds newly created mounts to its watch set. This is consistent with the host's endpoint-protection real-time scanning for WSL; the attribution is circumstantial.F_RDLCK) 62 ms after acquisition. The only other-process events in that run wereFAN_OPEN/FAN_CLOSE_NOWRITEon that exact inode and generation. The open event is delivered after the break because Linux emitsfsnotify_openonly afterbreak_leaselets the blocked opener proceed, once the holder releases the broken lease. The failing test spawns no process, and no helper-spawning test is scheduled that early.d123abcand586c6e5. The three-platform1877ee6failures were the obsolete oversized-count CLI assertion.No fixture-global collision or product descriptor-lifetime defect was found. Accepting a downgraded lease, retrying until success, serializing tests, raising budgets or changing host security policy would weaken the protection or the qualification, so none was done. The failing evidence is retained. Local two-thread Linux qualification on hosts with on-access scanning remains environment-limited; hosted Linux CI is the clean actual-head evidence.
Deterministic CI reproduction
288c087reproduces both kernel outcomes in-process on Linux, with no scanner, delay or retry. Each test re-runs itself in a child test process (like the existing writer-break lease test) so that descriptors inherited by concurrently forked tests cannot interfere. A non-blocking read-only open stands in for the external reader:foreign_reader_breaks_the_cached_lease_and_collection_fails_closed: after a retained 4096-byte cached prefix, the foreign open fails withEWOULDBLOCK(proof of the live exclusive lease) and downgrades the lease. The next pass reproduces the observed signature:errors == 1,member-verification-invalidated, categorystale-identity, detail "the exclusive file lease was broken or revoked", zero verification and reclaimed bytes, cached proof discarded, and payload bytes intact. A fresh pass then removes the object with exact byte credit.foreign_reader_at_lease_acquisition_keeps_collection_busy_and_retryable: an open reader at acquisition reproducesmember-writer-or-mapping-active, categorybusy, retryable, with zero verification/reclamation. The object staysPendingDeletionand the payload remains on disk. After the reader closes, the next pass removes it with exact byte credit.These are passing regressions that assert the observed fail-closed signatures and recovery; they do not make CI fail. On the local scanner host, 20/20 runs of both tests passed with CI's two test threads. An earlier draft used a 128-KiB payload for the broken-lease test, and its recovery pass failed in 11 of 15 isolated runs. In a separate 10-run diagnostic batch, 9 runs failed the same way; in each, the recovery pass's lease break was followed by an other-process
FAN_OPENon the exact inode and generation, during chunked truncation. In the passing run, the foreign open fell between leases. That is the same external reader, so the committed test uses an 8193-byte payload to shorten that window; no assertion was removed. The full localmanaged::collectionandauthentication_nativeset passed (20 tests).Original selection:
cargo test --locked -p tgrep-core --features managed-test-hooks --lib -- \ --test-threads=2 rooted::tests:: live::tests:: worktrees::tests:: \ managed::work::tests:: managed::collection::tests:: \ managed::maintenance::tests:: idleRelease boundaries
This PR remains draft. The local Linux lease failures are classified as external-environment interference, not a product defect. The Windows attempt-1 failures on
288c087(attachoperation-deadline) and300b1c6(daemon exit afterstop-if-idle) remain unexplained. Downstream consumption of the owner-hold frame is not part of this PR. Serving periodic verification is implemented and covered by generated tests, but the parent-owned real-corpus witness is not qualified: the recorded run observed a completed verification receipt and later failed its strict readiness assertion withready=false; the invalidation trigger was not established. This remains an open qualification boundary, not evidence that green CI closes the corpus gate. Real-corpus parity/performance and retained-cache outcomes remain parent-owned as well.No release workflow was changed or dispatched. Only a normal PR branch was pushed, without tags; signing remains opt-in. This PR does not merge, create a tag, sign binaries, or publish v1.2.3. Maintainer review and the normal release/signing process remain necessary.