Require creator receipts for all storage cleanup (#642) #744

Closed
kayg wants to merge 5 commits from fix/storage-exact-file-quarantine-642-4 into dev
Owner

A cleanup path could check one object and later unlink a replacement written by another program. All storage removal and overwriting replacement now go through fsio::quarantine. The module atomically claims the entry, requires the creator's device/inode/size/BLAKE3/name receipt, flushes that existing proof, and verifies content while holding a read lease before unlinking. A pathname, pending marker, equal length or journal match cannot create new ownership proof.

Unknown or changed bytes return as visible numbered conflict copies. Startup drains abandoned transactions, including those without a pending marker, and respects active directory locks. Streaming writers maintain hashes from their input; cleanup verifies large files through a fixed 64 KiB buffer. Interrupted prefixes without exact proof are retained, and the upload regression verifies their bytes rather than demanding unsafe deletion.

The workspace lint bans direct removals, recursive removal, overwriting rename entry points and raw unlink wrappers outside this module. Test fixtures and synthetic benchmarks have explicit exceptions for their own scratch trees.

Refs #642. This PR supersedes #742. A new branch is necessary because this forge ignores updates to open PR branches.

Call sites now using the shared removal and replacement boundary:

  • fsio: write_with error cleanup and metadata replacement; write_bulk_temp error cleanup; replace_many replacement and error cleanup; remove_file; exclusive moves; Prepared::Drop; guarded-create alias cleanup; after_swap displaced-image cleanup and rollback; Folder::remove; Folder::remove_staging; TempFile::Drop and failed persist; upload publication; PreparedDirectory::Drop; failed directory preparation; clean_prepared_directories; recover_staging; empty staging-directory cleanup.
  • Notes: Trash note/details/link purge and restore detail cleanup through the existing remove_file entry point; deletion::unlink through that entry point; trash::links::remember, discard and repair_file; bulk::creates::place rollback; MetaDb::open_with cache reset. Trash details now use a recorded atomic writer rather than a plain write.
  • Attachments: Pending::Drop, sweep_at marker cleanup, remove_temp through Folder::remove_staging. The existing startup Home enumeration also drains abandoned private transactions without adopting a new identity, including when a pending marker is absent.
  • Other callers covered by the workspace-wide ban: Cards::remove_many, backdrop Cache::trim, search schema::open cache reset, InviteKey::open temporary-key cleanup and admin::start_inner stale socket cleanup. Nonregular or unrecorded entries are retained as conflicts rather than unlinked.

Creation proofs are written by RecordedFile, including the upload Receiver's bounded chunks, and by the anonymous bulk and lock-file creation paths. Device, inode, size, BLAKE3 and the public-name binding travel with the inode in a durable xattr. Exclusive app moves can change the binding; an external move cannot transfer deletion authority. A held read lease covers the final verification and unlink. The verifier flushes an existing creator receipt before using it, including cancellation before the writer reached its final flush. Unknown bytes return to exclusively numbered visible conflict names. Locked live private directories are skipped by startup recovery.

The generic preservation property covers 14 entry points at BeforeCheck, BeforeMove, AfterMove and AfterCheck, with independent Python writers, generated binary payloads, inode replacement and completed in-place rewrites. In-place aliases are tested both before acquisition and as lease breakers at the final check; the alias is removed after the writer finishes so it cannot conceal a missing survivor. Every path also uses an explicit equal-length payload. The same 14-entry table also runs abrupt child-process kills at all four pauses. Tests separately cover an app-created replacement carrying another valid receipt, unknown import-cache bytes, live prepared-image exclusion, all three #742 repros and the ordinary startup binary with and without markers.

The retained private namespaces stay on the source filesystem inside each Home. Unsupported receipt or lease operations fail conservatively. These Linux tests cover process death and ordinary outside writers, not hostile changes to private application metadata or block-device power cuts. The designated performance VM cannot be resolved through either its direct or NetBird hostname, so there is no before/after latency claim.

Validation used build-slot.sh and cargo -j3, with one sequential final gate script. The full workspace suite, 14-path generated writer property, 14-path process-stop property, three reviewer repros, ordinary startup repros, nested recovery race, bounded upload memory and settled 1,000-stop loop pass. Workspace performance-budget tests remain intentionally ignored; the designated perf VM cannot be resolved. There is no latency-improvement claim. Receipt writing requires inode xattrs; unsupported receipt/lease operations retain bytes or fail safely. Destructive verification and upload publication were validated on Linux.

Head: c19aef3072

Gate output, verbatim:

fmt-write exit=0
fmt exit=0
clippy exit=0
nested-recovery exit=0
fsio exit=0
property exit=0
reviewer-repros exit=0
binary-repros exit=0
upload-memory exit=0
workspace exit=0
binary exit=0
durability-1000 exit=0
diff-check exit=0

Stop-loop result, verbatim:

Check 5 PASS: 1000/1000 abrupt stops and restarts completed: 250 saves, 250 renames, 250 Trash operations, 250 restores. All 1000 acknowledged text markers survived exactly once after every restart; final canonical bytes equal notes.get; no staging leftovers after restart and explicit save barriers in any round. Randomized SIGKILL timing does not prove a particular syscall was interrupted; deterministic boundary checks supply that coverage.

No evidence, logs, run data or screenshots are committed. No merge or deployment was performed.

A cleanup path could check one object and later unlink a replacement written by another program. All storage removal and overwriting replacement now go through fsio::quarantine. The module atomically claims the entry, requires the creator's device/inode/size/BLAKE3/name receipt, flushes that existing proof, and verifies content while holding a read lease before unlinking. A pathname, pending marker, equal length or journal match cannot create new ownership proof. Unknown or changed bytes return as visible numbered conflict copies. Startup drains abandoned transactions, including those without a pending marker, and respects active directory locks. Streaming writers maintain hashes from their input; cleanup verifies large files through a fixed 64 KiB buffer. Interrupted prefixes without exact proof are retained, and the upload regression verifies their bytes rather than demanding unsafe deletion. The workspace lint bans direct removals, recursive removal, overwriting rename entry points and raw unlink wrappers outside this module. Test fixtures and synthetic benchmarks have explicit exceptions for their own scratch trees. Refs #642. This PR supersedes #742. A new branch is necessary because this forge ignores updates to open PR branches. Call sites now using the shared removal and replacement boundary: - fsio: write_with error cleanup and metadata replacement; write_bulk_temp error cleanup; replace_many replacement and error cleanup; remove_file; exclusive moves; Prepared::Drop; guarded-create alias cleanup; after_swap displaced-image cleanup and rollback; Folder::remove; Folder::remove_staging; TempFile::Drop and failed persist; upload publication; PreparedDirectory::Drop; failed directory preparation; clean_prepared_directories; recover_staging; empty staging-directory cleanup. - Notes: Trash note/details/link purge and restore detail cleanup through the existing remove_file entry point; deletion::unlink through that entry point; trash::links::remember, discard and repair_file; bulk::creates::place rollback; MetaDb::open_with cache reset. Trash details now use a recorded atomic writer rather than a plain write. - Attachments: Pending::Drop, sweep_at marker cleanup, remove_temp through Folder::remove_staging. The existing startup Home enumeration also drains abandoned private transactions without adopting a new identity, including when a pending marker is absent. - Other callers covered by the workspace-wide ban: Cards::remove_many, backdrop Cache::trim, search schema::open cache reset, InviteKey::open temporary-key cleanup and admin::start_inner stale socket cleanup. Nonregular or unrecorded entries are retained as conflicts rather than unlinked. Creation proofs are written by RecordedFile, including the upload Receiver's bounded chunks, and by the anonymous bulk and lock-file creation paths. Device, inode, size, BLAKE3 and the public-name binding travel with the inode in a durable xattr. Exclusive app moves can change the binding; an external move cannot transfer deletion authority. A held read lease covers the final verification and unlink. The verifier flushes an existing creator receipt before using it, including cancellation before the writer reached its final flush. Unknown bytes return to exclusively numbered visible conflict names. Locked live private directories are skipped by startup recovery. The generic preservation property covers 14 entry points at BeforeCheck, BeforeMove, AfterMove and AfterCheck, with independent Python writers, generated binary payloads, inode replacement and completed in-place rewrites. In-place aliases are tested both before acquisition and as lease breakers at the final check; the alias is removed after the writer finishes so it cannot conceal a missing survivor. Every path also uses an explicit equal-length payload. The same 14-entry table also runs abrupt child-process kills at all four pauses. Tests separately cover an app-created replacement carrying another valid receipt, unknown import-cache bytes, live prepared-image exclusion, all three #742 repros and the ordinary startup binary with and without markers. The retained private namespaces stay on the source filesystem inside each Home. Unsupported receipt or lease operations fail conservatively. These Linux tests cover process death and ordinary outside writers, not hostile changes to private application metadata or block-device power cuts. The designated performance VM cannot be resolved through either its direct or NetBird hostname, so there is no before/after latency claim. Validation used build-slot.sh and cargo -j3, with one sequential final gate script. The full workspace suite, 14-path generated writer property, 14-path process-stop property, three reviewer repros, ordinary startup repros, nested recovery race, bounded upload memory and settled 1,000-stop loop pass. Workspace performance-budget tests remain intentionally ignored; the designated perf VM cannot be resolved. There is no latency-improvement claim. Receipt writing requires inode xattrs; unsupported receipt/lease operations retain bytes or fail safely. Destructive verification and upload publication were validated on Linux. Head: c19aef30723397e355adf1d3e82c9e24e862466c Gate output, verbatim: ```text fmt-write exit=0 fmt exit=0 clippy exit=0 nested-recovery exit=0 fsio exit=0 property exit=0 reviewer-repros exit=0 binary-repros exit=0 upload-memory exit=0 workspace exit=0 binary exit=0 durability-1000 exit=0 diff-check exit=0 ``` Stop-loop result, verbatim: ```text Check 5 PASS: 1000/1000 abrupt stops and restarts completed: 250 saves, 250 renames, 250 Trash operations, 250 restores. All 1000 acknowledged text markers survived exactly once after every restart; final canonical bytes equal notes.get; no staging leftovers after restart and explicit save barriers in any round. Randomized SIGKILL timing does not prove a particular syscall was interrupted; deterministic boundary checks supply that coverage. ``` No evidence, logs, run data or screenshots are committed. No merge or deployment was performed.
fix: recover interrupted note renames and staged saves
Some checks failed
CI / server (pull_request) Successful in 57s
CI / rust (pull_request) Successful in 20m26s
CI / web (pull_request) Failing after 2h59m10s
66f27ce3cd
Preserve unjournaled staging bytes across canonical file races
Some checks failed
CI / server (pull_request) Successful in 1m18s
CI / rust (pull_request) Successful in 15m56s
CI / web (pull_request) Has been cancelled
e46ce1c306
Saved by the coordinator after the job stopped early; gates not yet run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013Cj2Q1hCskP8Frsk6X7EHw
test(storage): verify quarantine cleanup at concurrent replacement boundaries
Some checks failed
CI / server (pull_request) Successful in 1m18s
CI / rust (pull_request) Successful in 21m5s
CI / web (pull_request) Has been cancelled
ccc7a2b790
fix(storage): require durable creator receipts for all cleanup
Some checks failed
CI / server (pull_request) Successful in 1m26s
CI / rust (pull_request) Failing after 30m1s
CI / web (pull_request) Has been cancelled
c19aef3072
Author
Owner

Verdict: fix first. Independent review of PR #744 at c19aef3072, into dev, on local branch verify/744-independent. No product edits remain, and no commits, pushes, merges, deployments or issue closures were performed.

  1. P1: a writer can break the read lease after the final check, and cleanup then loses its completed bytes. quarantine.rs:215-218 and :247-250 check unbroken(), then call unlink_verified; the unlink itself is at :184-188. staging_lease.rs:53-61 implements the check as F_GETLEASE. A lease-break request can arrive after that query. Holding the lease while unlinking does not make this query and deletion atomic.

    Reproduction: create a recorded file and a hard-link alias; pause immediately before unlinkat, after the last unbroken() returned true. A separate Python process tries O_WRONLY|O_NONBLOCK on the alias and must receive EWOULDBLOCK, then waits in blocking open. Resume cleanup. Cleanup unlinks the application-held name and closes the lease. The writer is admitted, writes, fsyncs, closes and removes its alias. No copy of those completed outside bytes remains for recovery. The same writer at the earlier AfterCheck pause is correctly preserved. This follows the PR's own alias-removal survivor check, but exercises the later scheduling window its four pauses miss. The outside writer never accesses private metadata in these alias cases.

    Confirmed through all fifteen cleanup paths: remove_file; Folder::remove; attachment remove_staging; upload Drop; rejected persist; note recover_staging; guarded cancellation/discard; atomic replacement; guarded save displacement; replace_many; startup drain; Prepared::Drop; rejected bulk creation; import-image Drop; import-image restart cleanup. The last four tests observe the actual generated private file before creating the ordinary alias. Final independent suite: 6 passed, 2 failed, both failures for this same P1 flaw. The first test lists eleven public-file paths; the second reports:

    actual private-image cleanup lost outside bytes: [(PreparedDrop, BeforeUnlink, false), (BulkReject, BeforeUnlink, false), (ImportDrop, BeforeUnlink, false), (ImportRestart, BeforeUnlink, false)]

    The complete test-only reproduction patch and command are in this issue comment. Adding another final query alone would leave the same scheduling window.

  2. P2: private transactions under the separate state root are never drained on startup. The fresh 1,000-stop client passed its Notes/Trash check and preserved all acknowledged text. A broader scan found 487 old hidden staging entries under state: 286 directories and 201 distinct files. They include scan-state.tsv images written through fsio at notes/scan.rs:443. attachments/sweep.rs:31-32 drains only the data Home; note staging recovery enumerates Home Notes/Trash. Neither covers state/users//notes.

    Start the ordinary uninstrumented server on the completed stop-loop root, list notes and issue save barriers. Verbatim:

    before startup: 487 hidden staging entries; 286 private directories; 201 files inside them
    after ordinary startup and save barriers: 487 of 487 old hidden entries remain
    old private files still retained: 201 of 201
    AssertionError: ordinary startup did not drain state-root storage transactions

    The restart assertion exits 1. This is an accumulating hidden-state cleanup defect and fails the broader no-leftovers requirement; it is not another acknowledged-note-text-loss claim. Counts depend on scheduling. Full evidence.

  3. P2: the removal lint is bypassed in a production module. contacts-actions/src/cards.rs:18 retains a file-wide allow(clippy::disallowed_methods). A temporary direct std::fs::remove_file function there passes Clippy with warnings denied. Removing only that original allowance makes the same probe fail with the intended disallowed-method diagnostic. Restoring the original file passes. Command: cargo clippy -p calternal-contacts-actions -j3 --lib --features tokio/rt-multi-thread,tokio/fs -- -D warnings. The corresponding probe in fsio/src/lib.rs is correctly rejected. The audit found the actual production removals routed through quarantine; this finding is the missing enforcement of that boundary. Probe evidence.

Other checks passed: both runs of the original 60 fsio tests; public replacements and child deaths at four cleanup boundaries across fifteen entry points; same-file private-image replacement/rewrite at the original four boundaries; all three previous equal-length upload rewrite failures; external replacement claimed before SIGKILL and returned after restart; sixteen conflict-name collisions across repeated recovery; exchange and rollback boundaries including a second outside replacement; ordinary startup recovery with and without markers; three interrupted-rename placement/index/title tests; note staging and acknowledged split-save recovery. Exclusive normal/case-only rename, missing-destination linking, upload/directory publication and empty-directory disposal were also traced in the code and exercised by the baseline tests.

The ordinary server excludes private content from note lists, settled search, journal catch-up, notes.get and live WebSocket room joins. Both ordinary markdown and hidden staging basenames were checked. Conflict copies are filesystem-visible recovery files, not indexed notes.

The fresh stop loop completed 250 saves, 250 renames, 250 Trash operations and 250 restores. All 1,000 acknowledged Unicode markers survived exactly once after every restart; same-key retries finished; auxiliary path/title/body checks passed; final canonical bytes equaled notes.get; no staging remained in Home Notes/Trash after save barriers. Its narrower pass does not clear finding 2. No exact syscall interruption is inferred from this after-send loop; test-only pauses provide that coverage.

All local heavy commands ran sequentially through build-slot.sh, with cargo -j3. No preload, syscall interposition or fixed sleep was used in the review clients/tests. Linux process-death coverage does not establish block-device power-cut or non-Linux lease behavior. The earlier remote reviewer hosts were inaccessible; their findings and clients were read from the local sibling review worktree and Forgejo comments.

Gate output, verbatim:

fsio-original exit=0
independent-initial exit=101
binary-startup exit=0
independent-expanded exit=101
notes-staging exit=0
notes-rename-boundaries exit=0
notes-ack-storage exit=0
visibility exit=0
durability-1000 exit=0
independent-final exit=101
lint-baseline exit=0
lint-outside-rejected exit=101
lint-cards-bypass exit=101
fsio-final exit=0
state-root-startup exit=1
lint-cards-bypass-with-features exit=101
lint-cards-control-rejected exit=101
lint-cards-baseline exit=0
lint-cards-bypass-final exit=0
lint-cards-control-final exit=101
lint-cards-baseline-final exit=0
tracked-tree-clean exit=0
reproduction-patch-check exit=0
diff-check exit=0
actual-private-images-initial exit=101
actual-private-images-final exit=101
actual-private-images-verified exit=101
private-final-boundaries exit=101
final-tracked-tree-clean exit=0
final-reproduction-patch-check exit=0
final-diff-check exit=0

The independent preservation exits of 101 and state-root-startup exit 1 are real failures. lint-outside-rejected and lint-cards-control-final exit 101 are expected lint rejection controls; lint-cards-bypass-final exit 0 demonstrates the gap. Earlier contacts probe attempts encountered an isolated Tokio feature error, then a probe formatting error; earlier private-image fixture attempts counted an untouched setup file or incorrectly required success when cleanup conservatively returned DirectoryNotEmpty. Those attempts are retained in the transcript, not counted as additional product defects or safety passes. The corrected final tests produce the results described above.

Tracked files are restored byte for byte; the final reproduction patch applies cleanly. Local evidence and instructions: /home/agent/wt/verify-744/findings.md and .verification/REPRODUCE.md. Head remains c19aef3072.

Verdict: **fix first**. Independent review of PR #744 at c19aef30723397e355adf1d3e82c9e24e862466c, into dev, on local branch verify/744-independent. No product edits remain, and no commits, pushes, merges, deployments or issue closures were performed. 1. **P1: a writer can break the read lease after the final check, and cleanup then loses its completed bytes.** quarantine.rs:215-218 and :247-250 check unbroken(), then call unlink_verified; the unlink itself is at :184-188. staging_lease.rs:53-61 implements the check as F_GETLEASE. A lease-break request can arrive after that query. Holding the lease while unlinking does not make this query and deletion atomic. Reproduction: create a recorded file and a hard-link alias; pause immediately before unlinkat, after the last unbroken() returned true. A separate Python process tries O_WRONLY|O_NONBLOCK on the alias and must receive EWOULDBLOCK, then waits in blocking open. Resume cleanup. Cleanup unlinks the application-held name and closes the lease. The writer is admitted, writes, fsyncs, closes and removes its alias. No copy of those completed outside bytes remains for recovery. The same writer at the earlier AfterCheck pause is correctly preserved. This follows the PR's own alias-removal survivor check, but exercises the later scheduling window its four pauses miss. The outside writer never accesses private metadata in these alias cases. Confirmed through all fifteen cleanup paths: remove_file; Folder::remove; attachment remove_staging; upload Drop; rejected persist; note recover_staging; guarded cancellation/discard; atomic replacement; guarded save displacement; replace_many; startup drain; Prepared::Drop; rejected bulk creation; import-image Drop; import-image restart cleanup. The last four tests observe the actual generated private file before creating the ordinary alias. Final independent suite: **6 passed, 2 failed**, both failures for this same P1 flaw. The first test lists eleven public-file paths; the second reports: actual private-image cleanup lost outside bytes: [(PreparedDrop, BeforeUnlink, false), (BulkReject, BeforeUnlink, false), (ImportDrop, BeforeUnlink, false), (ImportRestart, BeforeUnlink, false)] The complete test-only reproduction patch and command are in [this issue comment](https://git.kayg.org/kayg/calternal/issues/642#issuecomment-68299). Adding another final query alone would leave the same scheduling window. 2. **P2: private transactions under the separate state root are never drained on startup.** The fresh 1,000-stop client passed its Notes/Trash check and preserved all acknowledged text. A broader scan found **487 old hidden staging entries under state: 286 directories and 201 distinct files**. They include scan-state.tsv images written through fsio at notes/scan.rs:443. attachments/sweep.rs:31-32 drains only the data Home; note staging recovery enumerates Home Notes/Trash. Neither covers state/users/<id>/notes. Start the ordinary uninstrumented server on the completed stop-loop root, list notes and issue save barriers. Verbatim: before startup: 487 hidden staging entries; 286 private directories; 201 files inside them after ordinary startup and save barriers: 487 of 487 old hidden entries remain old private files still retained: 201 of 201 AssertionError: ordinary startup did not drain state-root storage transactions The restart assertion exits 1. This is an accumulating hidden-state cleanup defect and fails the broader no-leftovers requirement; it is not another acknowledged-note-text-loss claim. Counts depend on scheduling. [Full evidence](https://git.kayg.org/kayg/calternal/issues/642#issuecomment-68295). 3. **P2: the removal lint is bypassed in a production module.** contacts-actions/src/cards.rs:18 retains a file-wide allow(clippy::disallowed_methods). A temporary direct std::fs::remove_file function there passes Clippy with warnings denied. Removing only that original allowance makes the same probe fail with the intended disallowed-method diagnostic. Restoring the original file passes. Command: cargo clippy -p calternal-contacts-actions -j3 --lib --features tokio/rt-multi-thread,tokio/fs -- -D warnings. The corresponding probe in fsio/src/lib.rs is correctly rejected. The audit found the actual production removals routed through quarantine; this finding is the missing enforcement of that boundary. [Probe evidence](https://git.kayg.org/kayg/calternal/issues/642#issuecomment-68296). Other checks passed: both runs of the original 60 fsio tests; public replacements and child deaths at four cleanup boundaries across fifteen entry points; same-file private-image replacement/rewrite at the original four boundaries; all three previous equal-length upload rewrite failures; external replacement claimed before SIGKILL and returned after restart; sixteen conflict-name collisions across repeated recovery; exchange and rollback boundaries including a second outside replacement; ordinary startup recovery with and without markers; three interrupted-rename placement/index/title tests; note staging and acknowledged split-save recovery. Exclusive normal/case-only rename, missing-destination linking, upload/directory publication and empty-directory disposal were also traced in the code and exercised by the baseline tests. The ordinary server excludes private content from note lists, settled search, journal catch-up, notes.get and live WebSocket room joins. Both ordinary markdown and hidden staging basenames were checked. Conflict copies are filesystem-visible recovery files, not indexed notes. The fresh stop loop completed 250 saves, 250 renames, 250 Trash operations and 250 restores. All 1,000 acknowledged Unicode markers survived exactly once after every restart; same-key retries finished; auxiliary path/title/body checks passed; final canonical bytes equaled notes.get; no staging remained in Home Notes/Trash after save barriers. Its narrower pass does not clear finding 2. No exact syscall interruption is inferred from this after-send loop; test-only pauses provide that coverage. All local heavy commands ran sequentially through build-slot.sh, with cargo -j3. No preload, syscall interposition or fixed sleep was used in the review clients/tests. Linux process-death coverage does not establish block-device power-cut or non-Linux lease behavior. The earlier remote reviewer hosts were inaccessible; their findings and clients were read from the local sibling review worktree and Forgejo comments. Gate output, verbatim: ```text fsio-original exit=0 independent-initial exit=101 binary-startup exit=0 independent-expanded exit=101 notes-staging exit=0 notes-rename-boundaries exit=0 notes-ack-storage exit=0 visibility exit=0 durability-1000 exit=0 independent-final exit=101 lint-baseline exit=0 lint-outside-rejected exit=101 lint-cards-bypass exit=101 fsio-final exit=0 state-root-startup exit=1 lint-cards-bypass-with-features exit=101 lint-cards-control-rejected exit=101 lint-cards-baseline exit=0 lint-cards-bypass-final exit=0 lint-cards-control-final exit=101 lint-cards-baseline-final exit=0 tracked-tree-clean exit=0 reproduction-patch-check exit=0 diff-check exit=0 actual-private-images-initial exit=101 actual-private-images-final exit=101 actual-private-images-verified exit=101 private-final-boundaries exit=101 final-tracked-tree-clean exit=0 final-reproduction-patch-check exit=0 final-diff-check exit=0 ``` The independent preservation exits of 101 and state-root-startup exit 1 are real failures. lint-outside-rejected and lint-cards-control-final exit 101 are expected lint rejection controls; lint-cards-bypass-final exit 0 demonstrates the gap. Earlier contacts probe attempts encountered an isolated Tokio feature error, then a probe formatting error; earlier private-image fixture attempts counted an untouched setup file or incorrectly required success when cleanup conservatively returned DirectoryNotEmpty. Those attempts are retained in the transcript, not counted as additional product defects or safety passes. The corrected final tests produce the results described above. Tracked files are restored byte for byte; the final reproduction patch applies cleanly. Local evidence and instructions: /home/agent/wt/verify-744/findings.md and .verification/REPRODUCE.md. Head remains c19aef30723397e355adf1d3e82c9e24e862466c.
Author
Owner

Closing: superseded by #749, then #750 (named in the PR body or comments). Branch kept.

Closing: superseded by #749, then #750 (named in the PR body or comments). Branch kept.
kayg closed this pull request 2026-11-04 13:04:35 +00:00
Some checks failed
CI / server (pull_request) Successful in 1m26s
CI / rust (pull_request) Failing after 30m1s
CI / web (pull_request) Has been cancelled

Pull request closed

Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kayg/calternal!744
No description provided.