Files
vnidrop/AGENTS.md

10 KiB
Raw Blame History

AGENTS.md

Operational instructions for coding agents working in this repository. Humans: see README.md for product overview and run configs. Agents: read this file (and the nearest nested AGENTS.md) before editing.

Nested guides take precedence when editing under those trees:


Project overview

VniDrop is a cross-platform local P2P file transfer app.

Layer Path Responsibility
Rust core crates/vnidrop/ Iroh endpoint, blobs, SQLite, tickets, approval, streaming
Shared KMP shared/ Compose UI and platform bridges for Android, Windows, and Linux
Compose hosts androidApp/, desktopApp/ Thin Android and Windows/Linux app shells
Apple app apple/ Native SwiftUI UI using generated Rust/UniFFI Swift bindings

Invariant: UI/platform opens files and handles pickers; Rust streams bytes. Do not design features that move transfer payloads through Kotlin heap by default.

Domain docs (reference, do not paste into PRs):


Absolute rules

  1. Prefer PRs into master. Do not merge to master locally unless the user asks.
  2. Do not git push, force-push, or open a PR unless the user asks.
  3. If commit.gpgsign is enabled, create signed commits only. If signing fails (empty ssh-add -l), stop and tell the user to unlock the key. Never switch to unsigned commits to “unblock” yourself.
  4. Change only files required for the task. No drive-by refactors, dependency bumps, or repo-wide formatting.
  5. Do not force architecture migrations (MVI, Hilt, Nav3, etc.) unless requested.
  6. Never commit secrets, key material, or passphrases.
  7. Destructive git (reset --hard, push --force, dropping DBs) only with explicit user approval.
  8. Every bug fix includes a regression test at the lowest layer that catches it.
  9. After code changes, run the relevant checks in Build and test and fix failures before finishing.

Build and test

Install prerequisites when missing: GNU Make + Bash, Rust stable + rustfmt + clippy, JDK 17, Android NDK/SDK only if building Android, Xcode only for the native Apple app.

Rust core (crates/vnidrop or workspace root)

Run from the repo root (Cargo workspace):

make check-rust

Focused:

make test-rust
make test-rust-output-sink
make test-rust-transfer
make test-rust-approval
make test-rust-lifecycle

After finishing Rust edits, format:

make format

make check-rust includes documentation with warnings denied, matching .github/workflows/rust-core.yml.

Shared KMP / Compose (shared/)

make check-shared

Other targets (slower / machine-dependent):

make test-android-host
make check-android
make run-desktop

Note: jvmTest CI runs on Linux. Gobley host cargo is enabled for the current host and architecture, so local desktop builds embed their matching Rust library.

What to run before finishing

You changed… Minimum verification
crates/vnidrop/** only make check-rust
Cancel / export / sinks Above + make test-rust-output-sink
shared/** only make test-shared
Both make test-rust test-shared
Docs only No suite required; verify links/paths

Do not kill long cargo / Gradle runs mid-flight unless they hang past several minutes with no output; first builds are slow.


Repository map (edit here)

Rust runtime (keep split; do not re-merge into one file)

crates/vnidrop/src/runtime/
  mod.rs        # CoreInner, startup recovery, emit helpers
  facade.rs     # UniFFI VnidropCore + block_on
  share.rs      # import / share
  receive.rs    # receive, download, export, output sinks
  lifecycle.rs  # cancel, delete, status, access mode, shutdown
  provider.rs   # provider events, per-connection send progress

Other core modules: filesystem.rs, repository.rs, approval.rs, handshake.rs, ticket.rs, access_policy.rs, event_hub.rs, api.rs.

Shared app

shared/src/commonMain/kotlin/com/vnidrop/app/
  core/           # CoreGateway, models, pickers interfaces
  feature/send|receive|approvals|settings|app/
  ui/theme|components|navigation|feedback|state/
androidMain|jvmMain/   # expect/actual implementations

Platform file rules (do not violate)

  • Windows/Linux desktop: paths; directory walk in Rust when is_directory.
  • Android share: ParcelFileDescriptor file FDs only — never a directory FD. Folder share expands SAF trees in Kotlin to per-file FDs + relative names.
  • Android receive default: MediaStore Downloads sink; custom trees via SAF write.
  • Receive publish: no-overwrite temp + hard link / exclusive rename (see CORE_FLOW.md).

Code style

General

  • Match surrounding code (naming, imports, error handling).
  • Prefer small, reviewable diffs. Avoid files growing past ~800 LoC without splitting when adding substantial logic.
  • Do not add one-off helpers used only once if an inline block is clearer.
  • Prefer exhaustive when / match; avoid wildcards that hide new cases.

Comments (strict)

Comment why, invariants, and platform/concurrency traps only.

  • Do comment: cancel-before-await ordering, SAF/FD limits, security-scoped leases, “exactly one finish/abort after start_file”, durability rules.
  • Do not comment: restating the next line, tutorial narration, section banners that repeat the function name, pasted docs from this file.

Rust

  • Follow Clippy with -D warnings (CI fails otherwise).
  • Do not hold std::sync::MutexGuard or other guards across .await.
  • Prefer Handle::block_on via existing VnidropCore::block_on for concurrent API entry; cancel signals active transfers synchronously before async work.
  • Prefer private modules; export only what UniFFI / other crates need.
  • New public traits/types: short docs when the role is non-obvious.

Kotlin / Compose

For UI and presentation work, load and follow the in-repo skill:

.codex/skills/compose-skill/SKILL.md
  • Open at most one references/*.md file when the skills Quick Routing requires it.
  • Do not invent a second Compose style guide.
  • VniDrop uses MVVM-style ViewModels (*State + StateFlow + named methods), not a forced MVI onEvent base — adapt, do not rewrite.
  • Theme via LocalVniDropColors / VniDropThemeTokens only. Brand primary (light): HSL 271, 91%, 65%#A855F7.
  • Strings: CMP Res.string.* / composeResources — not Android R in commonMain.
  • Verify multiplatform target support before adding AndroidX/Jetpack deps to commonMain.

Details: shared/AGENTS.md.


Testing instructions

  • Prefer deterministic tests (gates, fixed sizes, public API fixtures).
  • Avoid long sleeps; if polling is required: short interval + hard timeout + clear assertion message.
  • Rust integration tests use public UniFFI API + tests/support/ only.
  • Failure paths: assert durable status and/or events when applicable, not only the error string.
  • Do not add tests for pure static constants.
  • Do not add negative tests for code you deleted.
  • Prefer comparing whole objects when equality is meaningful.

Layout:

Layer Location
Rust unit / private crates/vnidrop/src/tests/
Rust integration crates/vnidrop/tests/
Shared logic shared/src/commonTest/
Shared Compose/JVM shared/src/jvmTest/

Git and PR instructions

Branches

Name the change, not a roadmap step:

  • Good: feat/folder-share, fix/cancel-export-hang, docs/agents-md
  • Bad: feat/step3-remaining, wip, temp

After a PR merges: delete the feature branch locally and on origin, then branch from updated master.

Commits

  • Style in history: feat(scope):, fix(scope):, refactor(scope):, docs:, ci:.
  • Subject = outcome; body only when needed.
  • Signed when repo requires it.

Pull requests

  • Title matches the main change.
  • Summary: short bullets of what/why.
  • Test plan must be executable for this PR:
    • exact commands, and/or
    • 12 concrete scenarios that would catch a regression.
  • No filler plans (“everything works”, “CI green”) without commands or scenarios.

Security considerations

  • Treat tickets and endpoint IDs as sensitive enough not to log full blobs in production paths.
  • Do not weaken approval/access checks for convenience.
  • Do not store secrets in the repo; app data dirs and key files stay out of git.
  • Be careful with file publish races (no-clobber rename/link policy exists for a reason).

Common tasks → start files

Task Start here
Share / multi-file / folders runtime/share.rs, filesystem.rs, platform FileSystemService.*
Receive / export / sinks runtime/receive.rs
Cancel / delete / stop share runtime/lifecycle.rs, facade.rs
Per-receiver send progress runtime/provider.rs, ui/state/AppUiModels.kt
Approvals feature/approvals/, approval.rs
QR / NFC invitations TransferShareActions.*, ReceiveInvitationActions.*
Theme / brand ui/theme/VniDropTheme.kt
Compose skill .codex/skills/compose-skill/SKILL.md

Anti-patterns (never)

  • Streaming multi-MB transfer data through Kotlin as the primary design
  • Passing Android directory FDs into Rust
  • Nested exclusive Runtime::block_on that deadlocks cancel during receive
  • Holding locks across .await
  • Rebuilding a monolithic runtime.rs
  • Flaky multi-minute sleeps in tests
  • Unsigned commits when signing is required
  • Force-push or secret commits without explicit user direction

Implementation checklist

  1. Read this file + nearest nested AGENTS.md.
  2. For Compose/UI: load compose-skill.
  3. Smallest correct change; tests for bugs/behavior changes.
  4. Run relevant build/test commands; fix failures.
  5. Sparse comments only where non-obvious.
  6. Commit (signed) / push / PR only as the user requests.
  7. Summarize what changed and what you ran.