Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -328,3 +328,6 @@ required-features = ["test-live-ai"]
name = "ai_ollama_live_gate_test"
path = "tests/ai_ollama_live_gate_test.rs"
required-features = ["test-live-ai"]

[patch.crates-io]
git-internal = { path = "../git-internal" }
72 changes: 72 additions & 0 deletions docs/ai/v9fs-git-index-time.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# AI Handoff: v9fs Git Index Timestamp Fix

## Problem

Running `libra` on a v9fs/9p-backed filesystem can panic inside `git-internal`:

```text
thread 'libra-cli' panicked at .../git-internal-0.8.1/src/internal/index.rs:154:58:
called `Result::unwrap()` on an `Err` value: Error { kind: Unsupported, message: "creation time is not available for the filesystem" }
fatal: CLI thread panicked
```

The failing path is worktree indexing. The old code used `fs::Metadata::created().unwrap()` for the index `ctime` field. On Linux/v9fs, creation/birth time may be unsupported, so `created()` returns `Err(Unsupported)`.

## Fix Summary

The fix lives in the sibling `git-internal` checkout:

- `git-internal/src/internal/index.rs`
- Added `index_ctime()` and `index_mtime()` helpers.
- On Unix, `ctime` now comes from `MetadataExt::ctime()` / `ctime_nsec()`.
- On Unix, `mtime` now comes from `MetadataExt::mtime()` / `mtime_nsec()`.
- On non-Unix, timestamp helpers fall back deterministically instead of unwrapping.
- `IndexEntry::new`, `Index::refresh`, and `Index::is_modified` all use the same helpers.
- `git-internal/Cargo.toml`
- Version aligned to `0.8.1` so it satisfies `libra`'s dependency requirement.
- `libra/Cargo.toml`
- Added a local patch:

```toml
[patch.crates-io]
git-internal = { path = "../git-internal" }
```

This requires `libra` and `git-internal` to remain sibling directories during local validation.

## Why This Is Correct

Git index `ctime` means inode change time, not filesystem creation/birth time. Using `created()` was both semantically wrong for Git metadata and fragile on filesystems that do not expose birth time. Unix `MetadataExt::ctime()` matches the Git index meaning and is available on v9fs where `created()` is not.

## Validation Commands

From the `git-internal` repository:

```bash
cargo test internal::index --lib
```

From the `libra` repository:

```bash
LIBRA_SKIP_WEB_BUILD=1 cargo check --bin libra
```

`LIBRA_SKIP_WEB_BUILD=1` is needed if `pnpm` is not installed. Without it, `libra/build.rs` tries to run `pnpm install` for web assets before Rust checking completes.

## WSL/v9fs Verification Notes

Use a worktree located on the v9fs/9p-mounted path that previously triggered the panic. Then run the same `libra` command that touches the index, for example the original command or an add/status flow.

Expected result after the fix:

- No panic about `creation time is not available for the filesystem`.
- Cargo output should show `git-internal v0.8.1 (.../git-internal)` when checking/building `libra`, confirming the local patch is active.

If the panic still references a crates.io path like:

```text
.../.cargo/registry/src/.../git-internal-0.8.1/...
```

then `libra` is not using the local patched crate. Confirm the sibling layout and rerun from the patched `libra` checkout.
234 changes: 234 additions & 0 deletions docs/ai/wsl-v9fs-reproduction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,234 @@
# WSL/v9fs Reproduction Guide

This document shows how to reproduce the original `creation time is not available for the filesystem` panic and how to verify the fix.

The bug only appears when the Libra worktree is on a filesystem where Rust `std::fs::Metadata::created()` returns `Unsupported`. In WSL this is most likely on a Windows-mounted or 9p/v9fs-backed path, not on the normal Linux ext4 home directory.

## 0. Repositories and Fixed Commits

Use these repository URLs and commit ids for the commands below:

```bash
LIBRA_URL="https://github.com/Ivanbeethoven/libra.git"
GIT_INTERNAL_URL="https://github.com/Ivanbeethoven/git-internal.git"

# Pre-fix Libra commit used to reproduce the panic.
LIBRA_VULNERABLE_COMMIT="1b4665f0"

# Fixed git-internal commit.
GIT_INTERNAL_FIX_COMMIT="20d2810"

# Libra commit that adds the local git-internal override.
LIBRA_OVERRIDE_COMMIT="4d3e11d7"
```

The fixed local validation layout must keep both repositories as siblings:

```text
libra-v9fs-repro/
├── git-internal/
└── libra-fixed/
```

That sibling layout matters because `libra/Cargo.toml` contains:

```toml
[patch.crates-io]
git-internal = { path = "../git-internal" }
```

## 1. Pick a Reproduction Directory

Run these commands inside WSL:

```bash
uname -a
mount | grep -E '9p|drvfs' || true
```

Choose a path on the mounted/shared filesystem that previously triggered the bug. Examples:

```bash
cd /mnt/c/Users/$USER
mkdir -p libra-v9fs-repro
cd libra-v9fs-repro
```

Confirm the path is not the normal Linux home filesystem:

```bash
df -T "$PWD"
stat -c 'file=%n type=%F mtime=%y ctime=%z birth=%w' .
```

If `birth` is `-`, creation/birth time is unavailable there, which is the condition that exposed the old panic.

For a direct Rust-level probe:

```bash
cat >/tmp/check-created.rs <<'RS'
use std::{env, fs};

fn main() {
let path = env::args().nth(1).expect("usage: check-created <path>");
let meta = fs::symlink_metadata(&path).expect("metadata");
println!("created = {:?}", meta.created());
println!("modified = {:?}", meta.modified());
}
RS

rustc /tmp/check-created.rs -o /tmp/check-created
touch created-probe.txt
/tmp/check-created created-probe.txt
```

The vulnerable environment prints something like:

```text
created = Err(Os { code: ..., kind: Unsupported, message: "creation time is not available for the filesystem" })
modified = Ok(...)
```

If `created = Ok(...)`, this exact WSL path may not reproduce the original issue. Try the path where the panic was first observed.

## 2. Reproduce the Old Panic

Use the pre-fix `libra` commit, before the local `git-internal` override was added:

```bash
git clone "$LIBRA_URL" libra-vulnerable
cd libra-vulnerable
git checkout "$LIBRA_VULNERABLE_COMMIT"
```

Build only the Rust binary. `LIBRA_SKIP_WEB_BUILD=1` avoids requiring `pnpm` for this reproduction:

```bash
LIBRA_SKIP_WEB_BUILD=1 cargo build --bin libra
LIBRA_BIN="$PWD/target/debug/libra"
```

Create a separate test repository on the same WSL/v9fs-backed filesystem:

```bash
cd ..
rm -rf vulnerable-worktree
"$LIBRA_BIN" init --vault false vulnerable-worktree
cd vulnerable-worktree
printf 'hello from v9fs\n' > tracked.txt
```

Trigger the index path:

```bash
RUST_BACKTRACE=1 "$LIBRA_BIN" add tracked.txt
```

Expected old failure:

```text
called `Result::unwrap()` on an `Err` value: Error { kind: Unsupported, message: "creation time is not available for the filesystem" }
fatal: CLI thread panicked
```

The backtrace or panic path should reference the crates.io copy of `git-internal-0.8.1`, for example:

```text
.../.cargo/registry/src/.../git-internal-0.8.1/src/internal/index.rs
```

## 3. Verify the Fixed Version

Clone both repositories as siblings on the same WSL filesystem:

```bash
cd ..
git clone "$GIT_INTERNAL_URL" git-internal
git clone "$LIBRA_URL" libra-fixed
```

The fixed commits are:

```bash
cd git-internal
git merge-base --is-ancestor "$GIT_INTERNAL_FIX_COMMIT" HEAD && echo "git-internal fix is present"

cd ../libra-fixed
git merge-base --is-ancestor "$LIBRA_OVERRIDE_COMMIT" HEAD && echo "libra local override is present"
```

Confirm `libra` is patched to use the sibling `git-internal` checkout:

```bash
grep -n -A2 '\[patch.crates-io\]' Cargo.toml
```

Expected:

```toml
[patch.crates-io]
git-internal = { path = "../git-internal" }
```

Build/check with the local patched crate:

```bash
LIBRA_SKIP_WEB_BUILD=1 cargo check --locked --bin libra
```

During the check, Cargo should show:

```text
Checking git-internal v0.8.1 (.../git-internal)
```

Build the fixed binary:

```bash
LIBRA_SKIP_WEB_BUILD=1 cargo build --locked --bin libra
LIBRA_BIN="$PWD/target/debug/libra"
```

Run the same add flow on the v9fs-backed filesystem:

```bash
cd ..
rm -rf fixed-worktree
"$LIBRA_BIN" init --vault false fixed-worktree
cd fixed-worktree
printf 'hello from fixed v9fs\n' > tracked.txt
"$LIBRA_BIN" add tracked.txt
"$LIBRA_BIN" status --short
```

Expected fixed behavior:

- `libra add tracked.txt` exits successfully.
- There is no panic about creation time.
- The panic path no longer references `.cargo/registry/src/.../git-internal-0.8.1/...`.

## 4. Troubleshooting

If the fixed run still panics and the path includes `.cargo/registry/src`, then `libra` is not using the local patched crate. Check:

```bash
pwd
ls -d ../git-internal
grep -n -A2 '\[patch.crates-io\]' Cargo.toml
cargo tree -i git-internal
```

If `cargo check --locked --bin libra` fails before Rust checking with a `pnpm install` error, set:

```bash
export LIBRA_SKIP_WEB_BUILD=1
```

If `libra init --vault false` fails because the home directory is not writable, set `HOME` to a writable WSL directory:

```bash
export HOME="$PWD/.home"
mkdir -p "$HOME"
```

If the old version does not panic, verify that the test repository itself is on the filesystem where `created()` is unsupported. Building Libra on ext4 and testing a worktree on v9fs is fine; the important part is where `tracked.txt` lives.
Loading