Two files, one repository
spacedriveapp/spacedrive ships 2 formats across 3 indexed files. The question worth asking is whether the second one says anything the first does not.
CompareAGENTS.md ↔ CLAUDE.md
| Dimension | Shared | Only in A | Only in B | Overlap |
|---|---|---|---|---|
| Sections | 1 | 53 | 9 | 2% |
| Commands | 6 | 14 | 1 | 29% |
| Section tags | 6 | 7 | 0 | 46% |
What each file covers
Sections
1 shared · 53 only in A · 9 only in B- − Spacedrive Core v2 Development Guide
- − Quick Start
- − Development Workflow
- − Common Commands
- − Common Mistakes
- − Quick tips
- − Architecture Overview
- − CQRS and DDD Pattern
- − Feature Module Structure
- − Communication Architecture
- − Daemon-Client Communication (Tauri Desktop, CLI, Web)
- − Tauri Desktop Development
- − Install dependencies
- − Run Tauri app in dev mode (auto-starts daemon)
- − Build for production
- − Generate TypeScript types
- − Native Prototypes (iOS, macOS)
- − Extension System (WASM)
- − From extension directory
- − Output: target/wasm32-unknown-unknown/release/extension_name.wasm
- − Code Standards
- − Import Organization
- − Naming Conventions
- − Error Handling
- − Async Code
- − Resumable Jobs
- − Formatting
- − Logging
- − Setup
- − Writing Style
- − Macros
- − Job Logging
- − Log Levels
- − Environment Control
- − Testing
- − Test Organization
- − Running Tests
- − Task Tracking
- − When to Create Tasks
- − Task Structure
- − Description
- − Implementation Steps
- − Acceptance Criteria
- − Managing Tasks
- − List your active tasks
- − List high priority tasks
- − Validate before committing (automatic via git hook)
- − Task Lifecycle
- − Debugging
- − Log Files
- − Daemon Restart
- − Verbose Logging
- − Documentation Locations
- + AGENTS.md - Spacedrive Core v2
- + Build/Test Commands
- + Code Style
- + Daemon Architecture
- + **The `Wire` Trait**
- + **Registration Macros**
- + **Registry System**
- + Logging Standards
- + Debug Instructions
- Documentation
Commands
6 shared · 14 only in A · 1 only in B- − bun install
- − bun run tauri:dev
- − bun run tauri:build
- − cargo run --bin generate_typescript_types
- − cargo run --bin generate_swift_types
- − cargo build --target wasm32-unknown-unknown --release
- − cargo test test_share_file
- − cargo test --lib
- − cargo test -- --nocapture
- − cargo run -p task-validator -- list --assignee "yourname" --status "In Progress"
- − cargo run -p task-validator -- list --priority "High" --sort-by id
- − cargo run -p task-validator -- validate
- − cargo run --bin sd-cli -- restart
- − cargo run --bin sd-daemon
- + cargo test library_test
- cargo build
- cargo test
- cargo test <test_name>
- cargo clippy
- cargo fmt
- cargo run --bin sd-cli -- <command>
Section tags
6 shared · 7 only in A · 0 only in B- − setup
- − architecture
- − types
- − git-pr
- − dependencies
- − deployment
- − do-not
- build
- test
- lint-format
- code-style
- agent-behaviour
- docs
Line diff
spacedriveapp/spacedrive · AGENTS.md
@@ −1 @@
1# Spacedrive Core v2 Development Guide
2
3## Quick Start
4
5### Development Workflow
6
71. Start daemon: `cargo run --bin sd-daemon`
82. Make code changes
93. Run tests: `cargo test`
104. Rebuild and restart: `cargo run --bin sd-cli -- restart`
115. Test via CLI: `cargo run --bin sd-cli -- <command>`
12
13### Common Commands
14
15```bash
16cargo build # Build the project
17cargo test # Run all tests
18cargo test <test_name> # Run specific test
19cargo clippy # Lint code
20cargo fmt # Format code
21cargo run --bin sd-cli -- <command> # Run CLI (binary is sd-cli, not spacedrive)
22```
23
24### Common Mistakes
25
26- Running `spacedrive` instead of `sd-cli` (the binary name is `sd-cli`)
27- Forgetting to restart daemon after rebuilding
28- Using `println!` instead of `tracing` macros (`info!`, `debug!`, etc)
29- Implementing `Wire` manually instead of using `register_*` macros
30- Blocking the async runtime with synchronous I/O operations
31
32### Quick tips
33
34- On frontend apps, such as the interface in React, you must ALWAYS ensure type-safety based on the auto generated TypeScript types from `ts-client`. Never cast to as any or redefine backend types. our hooks are typesafe with correct input/output types, but sometimes you might need to access types directly from the `ts-client`.
35- If you have changed types on the backend that are public to the frontend (have `Type` derive), then you must regenerate the types using `cargo run --bin generate_typescript_types`
36- Read the `.mdx` files in /docs for context on any part of the app, they are kept up to date.
37-
38
39## Architecture Overview
40
41Spacedrive uses daemon-client architecture. A single daemon process manages core functionality. Multiple clients (CLI, GraphQL server, desktop app) connect via Unix domain sockets.
42
43### CQRS and DDD Pattern
44
45- **Domain** (`src/domain/`): Core data structures and business logic (nouns)
46- **Operations** (`src/ops/`): Actions and queries (verbs)
47- **Actions**: State-changing operations (writes)
48- **Queries**: Data retrieval without state changes (reads)
49
50### Feature Module Structure
51
52Each feature lives in its own module under `src/ops/`. Example: `src/ops/files/share`
53
54```
55src/ops/files/share/
56├── action.rs # State-changing logic
57├── input.rs # Action input structures
58├── output.rs # Action output structures
59└── job.rs # Long-running job implementation (if needed)
60```
61
62Complete feature example:
63
64```rust
65// src/ops/files/share/input.rs
66#[derive(Debug, Serialize, Deserialize)]
67pub struct ShareFileInput {
68 pub file_id: i32,
69 pub recipient: String,
70}
71
72// src/ops/files/share/output.rs
73#[derive(Debug, Serialize, Deserialize)]
74pub struct ShareFileOutput {
75 pub share_id: String,
76 pub url: String,
77}
78
79// src/ops/files/share/action.rs
80use super::{ShareFileInput, ShareFileOutput};
81
82pub struct ShareFileAction;
83
84crate::register_library_action!(ShareFileAction, "files.share");
85
86impl Action for ShareFileAction {
87 type Input = ShareFileInput;
88 type Output = ShareFileOutput;
89
90 async fn run(input: Self::Input, ctx: &ActionContext) -> Result<Self::Output> {
91 // Implementation
92 }
93}
94```
95
96## Communication Architecture
97
98Spacedrive supports multiple communication patterns for different platforms and use cases.
99
100### Daemon-Client Communication (Tauri Desktop, CLI, Web)
101
102The Tauri desktop app, CLI, and web interface connect to a daemon process via Unix domain sockets (or WebSockets for web). Communication uses JSON-RPC 2.0 with Wire method strings.
103
104**Registration Macros:**
105
106Never implement `Wire` manually. Use registration macros:
107
108```rust
109// Queries
110crate::register_query!(NetworkStatusQuery, "network.status");
111// Generates: "query:network.status"
112
113// Library Actions
114crate::register_library_action!(FileCopyAction, "files.copy");
115// Generates: "action:files.copy.input"
116
117// Core Actions
118crate::register_core_action!(LibraryCreateAction, "libraries.create");
119// Generates: "action:libraries.create.input"
120```
121
122**Registry System:**
123
124The `inventory` crate collects operations at compile time. When you use `register_query!` or `register_library_action!`, the operation automatically appears in global `QUERIES` and `ACTIONS` hashmaps at startup. You never manually register operations.
125
126Location: `core/src/ops/registry.rs`
127
128### Tauri Desktop Development
129
130The Tauri app (`apps/tauri/`) is the primary desktop application for Spacedrive. It connects to the daemon via the TypeScript client.
131
132**Development Workflow:**
133
134```bash
135# Install dependencies
136bun install
137
138# Run Tauri app in dev mode (auto-starts daemon)
139cd apps/tauri
140bun run tauri:dev
141
142# Build for production
143bun run tauri:build
144```
145
146**TypeScript Client:**
147
148The TypeScript client (`packages/ts-client/`) is auto-generated from Rust types using Specta:
149
150```bash
151# Generate TypeScript types
152cargo run --bin generate_typescript_types
153```
154
155**Output:** `packages/ts-client/src/generated.ts`
156
157**Architecture:**
158
159```
160Tauri App (React)
161 ↓
162@sd/ts-client (TypeScript)
163 ↓
164Daemon (Unix Socket / IPC)
165 ↓
166RpcServer (Rust)
167 ↓
168Operation Registry
169```
170
171### Native Prototypes (iOS, macOS)
172
173**Note:** iOS and macOS apps are experimental prototypes, not production apps.
174
175Native prototypes embed the core directly as a library via FFI rather than connecting to a daemon. These are located in `apps/ios/` and `apps/macos/` but are private and not documented for public use.
176
177**Swift Client Generation:**
178
179For the prototypes, Swift types can be generated:
180
181```bash
182cargo run --bin generate_swift_types
183```
184
185Output: `packages/swift-client/Sources/SpacedriveClient/`
186
187### Extension System (WASM)
188
189Extensions run as sandboxed WASM modules that interact with Spacedrive core via host functions. Extensions are distributed as compiled `.wasm` files.
190
191**Architecture:**
192
193```
194Extension.wasm (compiled Rust)
195 ↓
196spacedrive-sdk (Rust crate)
197 ↓
198Host Functions (FFI boundary)
199 ↓
200Core (VDFS, Jobs, AI, etc.)
201```
202
203**Key Components:**
204
205**SDK Location:** `crates/sdk/`
206
207- High-level Rust API abstracting FFI details
208- Procedural macros for extension definition
209- Type-safe job, model, and action builders
210
211**Extension Development:**
212
213Extensions use procedural macros to minimize boilerplate:
214
215```rust
216use spacedrive_sdk::prelude::*;
217
218#[extension(
219 id = "test-extension",
220 name = "Test Extension",
221 version = "0.1.0",
222 jobs = [test_counter],
223)]
224struct TestExtension;
225
226#[derive(Serialize, Deserialize, Default)]
227pub struct CounterState {
228 pub current: u32,
229 pub target: u32,
230 pub processed: Vec<String>,
231}
232
233#[job(name = "counter")]
234fn test_counter(ctx: &JobContext, state: &mut CounterState) -> Result<()> {
235 ctx.log(&format!("Starting counter (current: {}, target: {})",
236 state.current, state.target));
237
238 while state.current < state.target {
239 if ctx.check_interrupt() {
240 ctx.checkpoint(state)?;
241 return Err(Error::OperationFailed("Interrupted".into()));
242 }
243
244 state.current += 1;
245 ctx.report_progress(
246 state.current as f32 / state.target as f32,
247 &format!("Counted {}/{}", state.current, state.target),
248 );
249
250 if state.current % 10 == 0 {
251 ctx.checkpoint(state)?;
252 }
253 }
254
255 Ok(())
256}
257```
258
259**Host Functions:**
260
261Extensions import minimal FFI functions:
262
263```rust
264#[link(wasm_import_module = "spacedrive")]
265extern "C" {
266 fn spacedrive_log(level: u32, msg_ptr: *const u8, msg_len: usize);
267 fn register_job(
268 job_name_ptr: *const u8,
269 job_name_len: u32,
270 export_fn_ptr: *const u8,
271 export_fn_len: u32,
272 resumable: u32,
273 ) -> i32;
274}
275```
276
277**Building Extensions:**
278
279```bash
280# From extension directory
281cargo build --target wasm32-unknown-unknown --release
282
283# Output: target/wasm32-unknown-unknown/release/extension_name.wasm
284```
285
286**Extension Capabilities:**
287
288Extensions can define:
289
290- Models: Data structures stored in `models` table (content-scoped, standalone, or entry-scoped)
291- Jobs: Long-running resumable operations
292- Actions: User-invoked operations with preview-commit workflow
293- Agents: Autonomous logic with memory and event handling
294- UI: Custom views via `ui_manifest.json`
295
296**Example Use Cases:**
297
298- Photos extension: Face detection, scene tagging, album organization
299- Finance extension: Receipt extraction, expense tracking
300- Research extension: Citation extraction, knowledge graphs
301
302**Key Benefits:**
303
304- Single `.wasm` file works on all platforms
305- True sandboxing (WASM isolation)
306- Resumable jobs with checkpointing
307- Type-safe API with procedural macros
308- No core modifications needed for new features
309
310**Documentation:**
311
312- `/docs/sdk/sdk.md` - Complete SDK specification and API reference
313- `extensions/test-extension/` - Working example extension
314- `crates/sdk/` - SDK implementation
315- `crates/sdk-macros/` - SDK procedural macros
316
317**Status:** SDK implementation in progress. Test extension compiles to WASM successfully. Core integration for loading and executing WASM modules is next phase.
318
319## Code Standards
320
321### Import Organization
322
323Group imports with blank lines between groups:
324
325```rust
326// Standard library
327use std::path::PathBuf;
328use std::sync::Arc;
329
330// External crates
331use serde::{Deserialize, Serialize};
332use tokio::sync::RwLock;
333
334// Local modules
335use crate::domain::library::Library;
336use crate::ops::Action;
337```
338
339### Naming Conventions
340
341- Functions/variables: `snake_case`
342- Types: `PascalCase`
343- Constants: `SCREAMING_SNAKE_CASE`
344
345### Error Handling
346
347Use `Result<T, E>` for all fallible operations. Use `thiserror` for custom errors, `anyhow` for application errors.
348
349```rust
350use thiserror::Error;
351
352#[derive(Error, Debug)]
353pub enum ShareError {
354 #[error("File not found: {0}")]
355 FileNotFound(i32),
356
357 #[error("Permission denied")]
358 PermissionDenied,
359
360 #[error("Database error: {0}")]
361 Database(#[from] sea_orm::DbErr),
362}
363
364pub async fn share_file(id: i32) -> Result<String, ShareError> {
365 let file = find_file(id).await.ok_or(ShareError::FileNotFound(id))?;
366 // Implementation
367 Ok(share_url)
368}
369```
370
371### Async Code
372
373- Use `async/await` syntax
374- Prefer `tokio` primitives (`tokio::sync::RwLock`, `tokio::spawn`)
375- Avoid blocking operations (use `tokio::fs` not `std::fs`)
376- Use `tokio::task::spawn_blocking` for CPU-intensive work
377
378### Resumable Jobs
379
380Store job state within the job struct. Use `#[serde(skip)]` for non-persistent fields.
381
382```rust
383#[derive(Serialize, Deserialize)]
384pub struct FileCopyJob {
385 pub source: PathBuf,
386 pub destination: PathBuf,
387 pub copied_files: Vec<PathBuf>, // Persisted for resumability
388
389 #[serde(skip)]
390 pub progress_tx: Option<tokio::sync::mpsc::Sender<Progress>>, // Not persisted
391}
392
393impl Job for FileCopyJob {
394 async fn run(&mut self, ctx: &JobContext) -> Result<()> {
395 ctx.log().info("Starting file copy job");
396
397 for file in &self.files_to_copy {
398 if self.copied_files.contains(file) {
399 continue; // Skip already copied files on resume
400 }
401
402 copy_file(file).await?;
403 self.copied_files.push(file.clone());
404 }
405
406 Ok(())
407 }
408}
409```
410
411### Documentation
412
413**Core principle:** Explain WHY, not WHAT. Keep comments as short as possible. One sentence explaining rationale beats a paragraph restating code.
414
415**Module docs (`//!`):**
416- Add a title with `#` for the module name
417- Explain what the module does in plain language (not bullet points)
418- Include design rationale naturally in prose
419- Add runnable code examples showing usage
420
421````rust
422//! # File Sharing System
423//!
424//! `core::ops::files::share` provides temporary file sharing via signed URLs.
425//! Share links expire after 7 days by default to prevent indefinite access to
426//! private files. UUID v5 deterministic IDs ensure the same file generates
427//! consistent share URLs across devices without coordination.
428//!
429//! ## Example
430//! ```rust,no_run
431//! use spacedrive_core::ops::files::share::{ShareFileAction, ShareFileInput};
432//!
433//! let input = ShareFileInput { file_id: 123, recipient: "user@example.com" };
434//! let output = ShareFileAction::run(input, &ctx).await?;
435//! ```
436````
437
438**Function docs (`///`):**
439- First line: brief one-liner
440- Second paragraph: explain design rationale and why this exists
441- Document error handling philosophy when relevant
442- Explain non-obvious behavior and platform differences
443
444```rust
445/// Creates a share link with automatic expiration.
446///
447/// Share links use signed JWTs so the daemon can validate them without
448/// database lookups on every request. Expiration is enforced server-side
449/// to prevent timezone manipulation. Recipients without library access
450/// get read-only access to the specific file only.
451///
452/// Returns `ShareError::PermissionDenied` if the file is private and
453/// the recipient isn't a library member. The share is still created
454/// but marked inactive for audit logging.
455pub async fn share_file(input: ShareFileInput) -> Result<ShareFileOutput>
456```
457
458**Inline comments:**
459- Delete comments that restate obvious code
460- Explain WHY for decisions, not WHAT the code does
461- Use one sentence when possible
462- Only expand for truly non-obvious consequences
463
464```rust
465// Good: explains WHY
466// Lowercase for case-insensitive search matching.
467let ext = path.extension().map(|e| e.to_lowercase());
468
469// Bad: restates code
470// Extract file extension and convert to lowercase
471let ext = path.extension().map(|e| e.to_lowercase());
472
473// Good: explains consequence
474// Preserve ephemeral UUIDs so tags attached during browsing survive promotion to managed location.
475let uuid = ephemeral_cache.get(path).unwrap_or_else(|| Uuid::new_v4());
476
477// Bad: verbose explanation of obvious behavior
478// UUID assignment strategy:
479// 1. First check if there's an ephemeral UUID
480// 2. If not, generate a new one
481let uuid = ephemeral_cache.get(path).unwrap_or_else(|| Uuid::new_v4());
482```
483
484**Error handling comments:**
485Explain strategy and recovery, not just "log and continue".
486
487```rust
488// Good: explains recovery
489// Best-effort: continue with remaining moves, stale paths cleaned up on next reindex.
490Err(e) => ctx.log(format!("Failed to move: {}", e)),
491
492// Bad: states the obvious
493// Log error but continue
494Err(e) => ctx.log(format!("Failed to move: {}", e)),
495```
496
497**Platform-specific comments:**
498Explain consequences, not implementation blockers.
499
500```rust
501// Good: explains why and fallback
502#[cfg(windows)]
503pub fn get_inode(_metadata: &std::fs::Metadata) -> Option<u64> {
504 // Windows file indices are unstable across reboots; fall back to path-only matching.
505 None
506}
507
508// Bad: over-explains implementation details
509#[cfg(windows)]
510pub fn get_inode(_metadata: &std::fs::Metadata) -> Option<u64> {
511 // Windows doesn't have inodes.
512 // The method `file_index()` is unstable (issue #63010).
513 // Returning None is safe as the field is Optional.
514 None
515}
516```
517
518**Never use:**
519- Placeholder comments ("for now", "TODO: extract this later")
520- Markdown formatting (`**bold**`, `_italic_`) in code comments
521- ASCII diagrams (put those in `/docs/` if needed)
522- Section divider comments (`// ========== Section ==========`)
523- Comments explaining removed code during refactors
524
525Track future work in GitHub issues, not code comments.
526
527### Formatting
528
529Run `cargo fmt` before committing. Tabs for indentation. No emojis.
530
531## Logging
532
533### Setup
534
535Use `tracing_subscriber` in main or examples:
536
537```rust
538use tracing_subscriber::EnvFilter;
539
540fn main() {
541 tracing_subscriber::fmt()
542 .with_env_filter(
543 EnvFilter::try_from_default_env()
544 .unwrap_or_else(|_| EnvFilter::new("sd_core=info"))
545 )
546 .init();
547}
548```
549
550## Writing Style
551
552This applies to all documentation, code comments, and design documents.
553
554Use clear, simple language. Write short, impactful sentences. Use active voice. Focus on practical, actionable information.
555
556Address the reader directly with "you" and "your". Support claims with data and examples when possible.
557
558Avoid these constructions:
559
560- Em dashes (use commas or periods)
561- "Not only this, but also this"
562- Metaphors and cliches
563- Generalizations
564- Setup language like "in conclusion"
565- Unnecessary adjectives and adverbs
566- Emojis, hashtags, markdown formatting in prose
567
568Avoid these words:
569comprehensive, delve, utilize, harness, realm, tapestry, unlock, revolutionary, groundbreaking, remarkable, pivotal
570
571### Macros
572
573Use `tracing` macros, never `println!`:
574
575```rust
576use tracing::{info, warn, error, debug};
577
578info!("Server started on port {}", port);
579debug!(file_id = %id, "Processing file");
580warn!(error = %e, "Retrying operation");
581error!("Failed to connect to database");
582```
583
584### Job Logging
585
586Use `ctx.log()` in job implementations for automatic `job_id` tagging:
587
588```rust
589impl Job for MyJob {
590 async fn run(&mut self, ctx: &JobContext) -> Result<()> {
591 ctx.log().info("Job started");
592 ctx.log().debug!(progress = %self.progress, "Processing");
593 Ok(())
594 }
595}
596```
597
598### Log Levels
599
600- `debug`: Detailed flow for troubleshooting
601- `info`: User-relevant events (server start, job completion)
602- `warn`: Recoverable issues (retry, fallback)
603- `error`: Failures requiring attention
604
605### Environment Control
606
607Use `RUST_LOG` environment variable:
608
609```bash
610RUST_LOG=debug cargo run --bin sd-cli
611RUST_LOG=sd_core=trace cargo run
612RUST_LOG=sd_core::ops=debug cargo run
613```
614
615## Testing
616
617### Test Organization
618
619- Unit tests: Colocated in `#[cfg(test)]` modules
620- Integration tests: `tests/` directory at crate root
621
622```rust
623// src/ops/files/share/action.rs
624
625#[cfg(test)]
626mod tests {
627 use super::*;
628
629 #[tokio::test]
630 async fn test_share_file() {
631 let input = ShareFileInput {
632 file_id: 1,
633 recipient: "test@example.com".to_string(),
634 };
635
636 let output = share_file(input).await.unwrap();
637 assert!(!output.share_id.is_empty());
638 }
639}
640```
641
642### Running Tests
643
644```bash
645cargo test # All tests
646cargo test test_share_file # Specific test
647cargo test --lib # Library tests only
648cargo test -- --nocapture # Show output
649```
650
651## Task Tracking
652
653Spacedrive uses a file-based task system in `/.tasks/` to track features, epics, and development work. All task files are version-controlled alongside the code.
654
655### When to Create Tasks
656
657Create tasks for work that:
658
659- Introduces a new feature or capability
660- Refactors a significant system or module
661- Fixes a bug requiring architectural changes
662- Implements a whitepaper specification
663
664Do not create tasks for:
665
666- Routine code formatting or style fixes
667- Trivial bug fixes (single line changes)
668- Documentation updates to existing features
669- Dependency version bumps
670
671### Task Structure
672
673Each task is a Markdown file: `CATEGORY-###-title-slug.md`
674
675```yaml
676---
677id: CORE-042
678title: "Implement file sharing API"
679status: "In Progress"
680assignee: "james"
681priority: "High"
682tags: ["core", "networking"]
683whitepaper: "Section 4.2" # And/or design_doc: DESIGN_DOC_NAME.md
684---
685
686## Description
687Brief overview of what needs to be done and why.
688
689## Implementation Steps
690- [ ] Create share action in src/ops/files/share
691- [ ] Add database schema for shares table
692- [ ] Implement expiration logic
693
694## Acceptance Criteria
695- Share links work across all platforms
696- Expired shares return 404
697- Tests cover edge cases
698```
699
700### Managing Tasks
701
702```bash
703# List your active tasks
704cargo run -p task-validator -- list --assignee "yourname" --status "In Progress"
705
706# List high priority tasks
707cargo run -p task-validator -- list --priority "High" --sort-by id
708
709# Validate before committing (automatic via git hook)
710cargo run -p task-validator -- validate
711```
712
713### Task Lifecycle
714
7151. Create task file in `/.tasks/` with `status: "To Do"`
7162. Update status to `"In Progress"` when you start work
7173. Complete implementation and tests
7184. Update status to `"Done"` and commit
719
720Full documentation: `/docs/core/task-tracking.md`
721
722## Debugging
723
724### Log Files
725
726Job logs live in the `job_logs` directory in the data folder root.
727
728### Daemon Restart
729
730After rebuilding, restart the daemon to use the latest code:
731
732```bash
733cargo build
734cargo run --bin sd-cli -- restart
735```
736
737### Verbose Logging
738
739```bash
740RUST_LOG=debug cargo run --bin sd-daemon
741RUST_LOG=sd_core::jobs=trace cargo run
742```
743
744## Documentation Locations
745
746- Core architecture: `/docs/core/`
747- Design docs and RFCs: `/docs/core/design/`
748- Application docs: `/docs/`
749- Daemon details: `/docs/core/daemon.md`
750- Task tracking: `/docs/core/task-tracking.md`
751
spacedriveapp/spacedrive · core/AGENTS.md
@@ +1 @@
1# AGENTS.md - Spacedrive Core v2
2
3## Build/Test Commands
4
5- `cargo build` - Build the project
6- `cargo test` - Run all tests
7- `cargo test <test_name>` - Run specific test (e.g., `cargo test library_test`)
8- `cargo clippy` - Lint code
9- `cargo fmt` - Format code
10- `cargo run --bin sd-cli -- <command>` - Run CLI (note: binary is `sd-cli`, not `spacedrive`)
11
12## Code Style
13
14- **Imports**: Group std, external crates, then local modules with blank lines between
15- **Formatting**: Use `cargo fmt` - tabs for indentation, snake_case for variables/functions. DO NOT use emojis at all.
16- **Types**: Explicit types preferred, use `Result<T, E>` for error handling with `thiserror`
17- **Naming**: snake_case for functions/variables, PascalCase for types, SCREAMING_SNAKE_CASE for constants
18- **Error Handling**: Use `Result` types, `thiserror` for custom errors, `anyhow` for application errors
19- **Async**: Use `async/await`, prefer `tokio` primitives, avoid blocking operations
20- **Resumable Jobs**: For long-running jobs that need to be resumable, store the job's state within the job's struct itself. Use `#[serde(skip)]` for fields that should not be persisted. For example, in a file copy job, the list of already copied files can be stored to allow the job to resume from where it left off.
21- **Documentation**: Use `//!` for module docs, `///` for public items, include examples
22- **Architecture**: Follow a Command Query Responsibility Segregation (CQRS) and Domain-Driven Design (DDD) pattern.
23 - **Domain**: Core data structures and business logic are located in `src/domain/`. These are the "nouns" of your system.
24 - **Operations**: State-changing commands (actions) and data-retrieving queries are located in `src/ops/`. These are the "verbs" of your system.
25 - **Actions**: Operations that modify the state of the application. They should be self-contained and transactional.
26 - **Queries**: Operations that retrieve data without modifying state. They should be efficient and optimized for reading.
27- **Feature Modules**: Each new feature should be implemented in its own module within the `src/ops/` directory. For example, a new "share" feature would live in `src/ops/files/share`. Each feature module should contain the following files where applicable:
28 - `action.rs`: The main logic for the state-changing operation.
29 - `input.rs`: Data structures for the action's input.
30 - `output.rs`: Data structures for the action's output.
31 - `job.rs`: If the action is long-running, the job implementation.
32- **Database**: Use SeaORM entities, async queries, proper error propagation
33- **Comments**: Minimal inline comments, focus on why not what, no TODO comments in production code
34
35## Daemon Architecture
36
37Spacedrive uses a **daemon-client architecture** where a single daemon process manages the core functionality and multiple client applications (CLI, GraphQL server, desktop app) connect to it via Unix domain sockets.
38
39> **For detailed daemon architecture documentation, see [/docs/core/daemon.md](/docs/core/daemon.md)**
40
41### **The `Wire` Trait**
42
43All actions and queries must implement the `Wire` trait to enable type-safe client-daemon communication:
44
45```rust
46pub trait Wire {
47 const METHOD: &'static str;
48}
49```
50
51### **Registration Macros**
52
53Instead of manually implementing `Wire`, use these registration macros that automatically:
54
551. Implement the `Wire` trait with the correct method string
562. Register the operation in the global registry using the `inventory` crate
57
58**For Queries:**
59
60```rust
61crate::register_query!(NetworkStatusQuery, "network.status");
62// Generates method: "query:network.status"
63```
64
65**For Library Actions:**
66
67```rust
68crate::register_library_action!(FileCopyAction, "files.copy");
69// Generates method: "action:files.copy.input"
70```
71
72**For Core Actions:**
73
74```rust
75crate::register_core_action!(LibraryCreateAction, "libraries.create");
76// Generates method: "action:libraries.create.input"
77```
78
79### **Registry System**
80
81- **Location**: `core/src/ops/registry.rs`
82- **Mechanism**: Uses the `inventory` crate for compile-time registration
83- **Global Maps**: `QUERIES` and `ACTIONS` hashmaps populated at startup
84- **Handler Functions**: Generic handlers that decode payloads, execute operations, and encode results
85
86## Logging Standards
87
88- **Setup**: Use `tracing_subscriber::fmt()` with env filter for structured logging
89- **Macros**: Use `info!`, `warn!`, `error!`, `debug!` from `tracing` crate, not `println!`
90- **Job Context**: Use `ctx.log()` in jobs for job-specific logging with automatic job_id tagging
91- **Structured**: Include relevant context fields: `debug!(job_id = %self.id, "message")`
92- **Levels**: debug for detailed flow, info for user-relevant events, warn for recoverable issues, error for failures
93- **Format**: `tracing_subscriber::fmt().with_env_filter(env_filter).init()` in main/examples
94- **Environment**: Respect `RUST_LOG` env var, fallback to module-specific filters like `sd_core=info`
95
96## Documentation
97
98- **Core level docs**: Live in `/docs/core` - comprehensive architecture and implementation guides
99- **Core design docs**: Live in `/docs/core/design` - planning documents, RFCs, and design decisions
100- **Application level docs**: Live in `/docs`
101- **Code docs**: Use `///` for public APIs, `//!` for module overviews, include examples
102
103## Debug Instructions
104
105- You can view the logs of a job in the job_logs directory in the root of the data folder
106- When testing the CLI, after compiling you must first use the `restart` command to ensure the Spacedrive daemon is using the latest build.
107
@@ −1 +1 @@
1−# Spacedrive Core v2 Development Guide
1+# AGENTS.md - Spacedrive Core v2
22
3−## Quick Start
3+## Build/Test Commands
44
5−### Development Workflow
5+- `cargo build` - Build the project
6+- `cargo test` - Run all tests
7+- `cargo test <test_name>` - Run specific test (e.g., `cargo test library_test`)
8+- `cargo clippy` - Lint code
9+- `cargo fmt` - Format code
10+- `cargo run --bin sd-cli -- <command>` - Run CLI (note: binary is `sd-cli`, not `spacedrive`)
611
7−1. Start daemon: `cargo run --bin sd-daemon`
8−2. Make code changes
9−3. Run tests: `cargo test`
10−4. Rebuild and restart: `cargo run --bin sd-cli -- restart`
11−5. Test via CLI: `cargo run --bin sd-cli -- <command>`
12+## Code Style
1213
13−### Common Commands
14+- **Imports**: Group std, external crates, then local modules with blank lines between
15+- **Formatting**: Use `cargo fmt` - tabs for indentation, snake_case for variables/functions. DO NOT use emojis at all.
16+- **Types**: Explicit types preferred, use `Result<T, E>` for error handling with `thiserror`
17+- **Naming**: snake_case for functions/variables, PascalCase for types, SCREAMING_SNAKE_CASE for constants
18+- **Error Handling**: Use `Result` types, `thiserror` for custom errors, `anyhow` for application errors
19+- **Async**: Use `async/await`, prefer `tokio` primitives, avoid blocking operations
20+- **Resumable Jobs**: For long-running jobs that need to be resumable, store the job's state within the job's struct itself. Use `#[serde(skip)]` for fields that should not be persisted. For example, in a file copy job, the list of already copied files can be stored to allow the job to resume from where it left off.
21+- **Documentation**: Use `//!` for module docs, `///` for public items, include examples
22+- **Architecture**: Follow a Command Query Responsibility Segregation (CQRS) and Domain-Driven Design (DDD) pattern.
23+ - **Domain**: Core data structures and business logic are located in `src/domain/`. These are the "nouns" of your system.
24+ - **Operations**: State-changing commands (actions) and data-retrieving queries are located in `src/ops/`. These are the "verbs" of your system.
25+ - **Actions**: Operations that modify the state of the application. They should be self-contained and transactional.
26+ - **Queries**: Operations that retrieve data without modifying state. They should be efficient and optimized for reading.
27+- **Feature Modules**: Each new feature should be implemented in its own module within the `src/ops/` directory. For example, a new "share" feature would live in `src/ops/files/share`. Each feature module should contain the following files where applicable:
28+ - `action.rs`: The main logic for the state-changing operation.
29+ - `input.rs`: Data structures for the action's input.
30+ - `output.rs`: Data structures for the action's output.
31+ - `job.rs`: If the action is long-running, the job implementation.
32+- **Database**: Use SeaORM entities, async queries, proper error propagation
33+- **Comments**: Minimal inline comments, focus on why not what, no TODO comments in production code
1434
15−```bash
16−cargo build # Build the project
17−cargo test # Run all tests
18−cargo test <test_name> # Run specific test
19−cargo clippy # Lint code
20−cargo fmt # Format code
21−cargo run --bin sd-cli -- <command> # Run CLI (binary is sd-cli, not spacedrive)
22−```
35+## Daemon Architecture
2336
24−### Common Mistakes
37+Spacedrive uses a **daemon-client architecture** where a single daemon process manages the core functionality and multiple client applications (CLI, GraphQL server, desktop app) connect to it via Unix domain sockets.
2538
26−- Running `spacedrive` instead of `sd-cli` (the binary name is `sd-cli`)
27−- Forgetting to restart daemon after rebuilding
28−- Using `println!` instead of `tracing` macros (`info!`, `debug!`, etc)
29−- Implementing `Wire` manually instead of using `register_*` macros
30−- Blocking the async runtime with synchronous I/O operations
39+> **For detailed daemon architecture documentation, see [/docs/core/daemon.md](/docs/core/daemon.md)**
3140
32−### Quick tips
41+### **The `Wire` Trait**
3342
34−- On frontend apps, such as the interface in React, you must ALWAYS ensure type-safety based on the auto generated TypeScript types from `ts-client`. Never cast to as any or redefine backend types. our hooks are typesafe with correct input/output types, but sometimes you might need to access types directly from the `ts-client`.
35−- If you have changed types on the backend that are public to the frontend (have `Type` derive), then you must regenerate the types using `cargo run --bin generate_typescript_types`
36−- Read the `.mdx` files in /docs for context on any part of the app, they are kept up to date.
37−-
43+All actions and queries must implement the `Wire` trait to enable type-safe client-daemon communication:
3844
39−## Architecture Overview
40−
41−Spacedrive uses daemon-client architecture. A single daemon process manages core functionality. Multiple clients (CLI, GraphQL server, desktop app) connect via Unix domain sockets.
42−
43−### CQRS and DDD Pattern
44−
45−- **Domain** (`src/domain/`): Core data structures and business logic (nouns)
46−- **Operations** (`src/ops/`): Actions and queries (verbs)
47−- **Actions**: State-changing operations (writes)
48−- **Queries**: Data retrieval without state changes (reads)
49−
50−### Feature Module Structure
51−
52−Each feature lives in its own module under `src/ops/`. Example: `src/ops/files/share`
53−
54−```
55−src/ops/files/share/
56−├── action.rs # State-changing logic
57−├── input.rs # Action input structures
58−├── output.rs # Action output structures
59−└── job.rs # Long-running job implementation (if needed)
60−```
61−
62−Complete feature example:
63−
6445 ```rust
65−// src/ops/files/share/input.rs
66−#[derive(Debug, Serialize, Deserialize)]
67−pub struct ShareFileInput {
68− pub file_id: i32,
69− pub recipient: String,
46+pub trait Wire {
47+ const METHOD: &'static str;
7048 }
71−
72−// src/ops/files/share/output.rs
73−#[derive(Debug, Serialize, Deserialize)]
74−pub struct ShareFileOutput {
75− pub share_id: String,
76− pub url: String,
77−}
78−
79−// src/ops/files/share/action.rs
80−use super::{ShareFileInput, ShareFileOutput};
81−
82−pub struct ShareFileAction;
83−
84−crate::register_library_action!(ShareFileAction, "files.share");
85−
86−impl Action for ShareFileAction {
87− type Input = ShareFileInput;
88− type Output = ShareFileOutput;
89−
90− async fn run(input: Self::Input, ctx: &ActionContext) -> Result<Self::Output> {
91− // Implementation
92− }
93−}
9449 ```
9550
96−## Communication Architecture
51+### **Registration Macros**
9752
98−Spacedrive supports multiple communication patterns for different platforms and use cases.
53+Instead of manually implementing `Wire`, use these registration macros that automatically:
9954
100−### Daemon-Client Communication (Tauri Desktop, CLI, Web)
55+1. Implement the `Wire` trait with the correct method string
56+2. Register the operation in the global registry using the `inventory` crate
10157
102−The Tauri desktop app, CLI, and web interface connect to a daemon process via Unix domain sockets (or WebSockets for web). Communication uses JSON-RPC 2.0 with Wire method strings.
58+**For Queries:**
10359
104−**Registration Macros:**
105−
106−Never implement `Wire` manually. Use registration macros:
107−
10860 ```rust
109−// Queries
11061 crate::register_query!(NetworkStatusQuery, "network.status");
111−// Generates: "query:network.status"
112−
113−// Library Actions
114−crate::register_library_action!(FileCopyAction, "files.copy");
115−// Generates: "action:files.copy.input"
116−
117−// Core Actions
118−crate::register_core_action!(LibraryCreateAction, "libraries.create");
119−// Generates: "action:libraries.create.input"
62+// Generates method: "query:network.status"
12063 ```
12164
122−**Registry System:**
65+**For Library Actions:**
12366
124−The `inventory` crate collects operations at compile time. When you use `register_query!` or `register_library_action!`, the operation automatically appears in global `QUERIES` and `ACTIONS` hashmaps at startup. You never manually register operations.
125−
126−Location: `core/src/ops/registry.rs`
127−
128−### Tauri Desktop Development
129−
130−The Tauri app (`apps/tauri/`) is the primary desktop application for Spacedrive. It connects to the daemon via the TypeScript client.
131−
132−**Development Workflow:**
133−
134−```bash
135−# Install dependencies
136−bun install
137−
138−# Run Tauri app in dev mode (auto-starts daemon)
139−cd apps/tauri
140−bun run tauri:dev
141−
142−# Build for production
143−bun run tauri:build
144−```
145−
146−**TypeScript Client:**
147−
148−The TypeScript client (`packages/ts-client/`) is auto-generated from Rust types using Specta:
149−
150−```bash
151−# Generate TypeScript types
152−cargo run --bin generate_typescript_types
153−```
154−
155−**Output:** `packages/ts-client/src/generated.ts`
156−
157−**Architecture:**
158−
159−```
160−Tauri App (React)
161− ↓
162−@sd/ts-client (TypeScript)
163− ↓
164−Daemon (Unix Socket / IPC)
165− ↓
166−RpcServer (Rust)
167− ↓
168−Operation Registry
169−```
170−
171−### Native Prototypes (iOS, macOS)
172−
173−**Note:** iOS and macOS apps are experimental prototypes, not production apps.
174−
175−Native prototypes embed the core directly as a library via FFI rather than connecting to a daemon. These are located in `apps/ios/` and `apps/macos/` but are private and not documented for public use.
176−
177−**Swift Client Generation:**
178−
179−For the prototypes, Swift types can be generated:
180−
181−```bash
182−cargo run --bin generate_swift_types
183−```
184−
185−Output: `packages/swift-client/Sources/SpacedriveClient/`
186−
187−### Extension System (WASM)
188−
189−Extensions run as sandboxed WASM modules that interact with Spacedrive core via host functions. Extensions are distributed as compiled `.wasm` files.
190−
191−**Architecture:**
192−
193−```
194−Extension.wasm (compiled Rust)
195− ↓
196−spacedrive-sdk (Rust crate)
197− ↓
198−Host Functions (FFI boundary)
199− ↓
200−Core (VDFS, Jobs, AI, etc.)
201−```
202−
203−**Key Components:**
204−
205−**SDK Location:** `crates/sdk/`
206−
207−- High-level Rust API abstracting FFI details
208−- Procedural macros for extension definition
209−- Type-safe job, model, and action builders
210−
211−**Extension Development:**
212−
213−Extensions use procedural macros to minimize boilerplate:
214−
21567 ```rust
216−use spacedrive_sdk::prelude::*;
217−
218−#[extension(
219− id = "test-extension",
220− name = "Test Extension",
221− version = "0.1.0",
222− jobs = [test_counter],
223−)]
224−struct TestExtension;
225−
226−#[derive(Serialize, Deserialize, Default)]
227−pub struct CounterState {
228− pub current: u32,
229− pub target: u32,
230− pub processed: Vec<String>,
231−}
232−
233−#[job(name = "counter")]
234−fn test_counter(ctx: &JobContext, state: &mut CounterState) -> Result<()> {
235− ctx.log(&format!("Starting counter (current: {}, target: {})",
236− state.current, state.target));
237−
238− while state.current < state.target {
239− if ctx.check_interrupt() {
240− ctx.checkpoint(state)?;
241− return Err(Error::OperationFailed("Interrupted".into()));
242− }
243−
244− state.current += 1;
245− ctx.report_progress(
246− state.current as f32 / state.target as f32,
247− &format!("Counted {}/{}", state.current, state.target),
248− );
249−
250− if state.current % 10 == 0 {
251− ctx.checkpoint(state)?;
252− }
253− }
254−
255− Ok(())
256−}
68+crate::register_library_action!(FileCopyAction, "files.copy");
69+// Generates method: "action:files.copy.input"
25770 ```
25871
259−**Host Functions:**
72+**For Core Actions:**
26073
261−Extensions import minimal FFI functions:
262−
26374 ```rust
264−#[link(wasm_import_module = "spacedrive")]
265−extern "C" {
266− fn spacedrive_log(level: u32, msg_ptr: *const u8, msg_len: usize);
267− fn register_job(
268− job_name_ptr: *const u8,
269− job_name_len: u32,
270− export_fn_ptr: *const u8,
271− export_fn_len: u32,
272− resumable: u32,
273− ) -> i32;
274−}
75+crate::register_core_action!(LibraryCreateAction, "libraries.create");
76+// Generates method: "action:libraries.create.input"
27577 ```
27678
277−**Building Extensions:**
79+### **Registry System**
27880
279−```bash
280−# From extension directory
281−cargo build --target wasm32-unknown-unknown --release
81+- **Location**: `core/src/ops/registry.rs`
82+- **Mechanism**: Uses the `inventory` crate for compile-time registration
83+- **Global Maps**: `QUERIES` and `ACTIONS` hashmaps populated at startup
84+- **Handler Functions**: Generic handlers that decode payloads, execute operations, and encode results
28285
283−# Output: target/wasm32-unknown-unknown/release/extension_name.wasm
284−```
86+## Logging Standards
28587
286−**Extension Capabilities:**
88+- **Setup**: Use `tracing_subscriber::fmt()` with env filter for structured logging
89+- **Macros**: Use `info!`, `warn!`, `error!`, `debug!` from `tracing` crate, not `println!`
90+- **Job Context**: Use `ctx.log()` in jobs for job-specific logging with automatic job_id tagging
91+- **Structured**: Include relevant context fields: `debug!(job_id = %self.id, "message")`
92+- **Levels**: debug for detailed flow, info for user-relevant events, warn for recoverable issues, error for failures
93+- **Format**: `tracing_subscriber::fmt().with_env_filter(env_filter).init()` in main/examples
94+- **Environment**: Respect `RUST_LOG` env var, fallback to module-specific filters like `sd_core=info`
28795
288−Extensions can define:
96+## Documentation
28997
290−- Models: Data structures stored in `models` table (content-scoped, standalone, or entry-scoped)
291−- Jobs: Long-running resumable operations
292−- Actions: User-invoked operations with preview-commit workflow
293−- Agents: Autonomous logic with memory and event handling
294−- UI: Custom views via `ui_manifest.json`
98+- **Core level docs**: Live in `/docs/core` - comprehensive architecture and implementation guides
99+- **Core design docs**: Live in `/docs/core/design` - planning documents, RFCs, and design decisions
100+- **Application level docs**: Live in `/docs`
101+- **Code docs**: Use `///` for public APIs, `//!` for module overviews, include examples
295102
296−**Example Use Cases:**
103+## Debug Instructions
297104
298−- Photos extension: Face detection, scene tagging, album organization
299−- Finance extension: Receipt extraction, expense tracking
300−- Research extension: Citation extraction, knowledge graphs
301−
302−**Key Benefits:**
303−
304−- Single `.wasm` file works on all platforms
305−- True sandboxing (WASM isolation)
306−- Resumable jobs with checkpointing
307−- Type-safe API with procedural macros
308−- No core modifications needed for new features
309−
310−**Documentation:**
311−
312−- `/docs/sdk/sdk.md` - Complete SDK specification and API reference
313−- `extensions/test-extension/` - Working example extension
314−- `crates/sdk/` - SDK implementation
315−- `crates/sdk-macros/` - SDK procedural macros
316−
317−**Status:** SDK implementation in progress. Test extension compiles to WASM successfully. Core integration for loading and executing WASM modules is next phase.
318−
319−## Code Standards
320−
321−### Import Organization
322−
323−Group imports with blank lines between groups:
324−
325−```rust
326−// Standard library
327−use std::path::PathBuf;
328−use std::sync::Arc;
329−
330−// External crates
331−use serde::{Deserialize, Serialize};
332−use tokio::sync::RwLock;
333−
334−// Local modules
335−use crate::domain::library::Library;
336−use crate::ops::Action;
337−```
338−
339−### Naming Conventions
340−
341−- Functions/variables: `snake_case`
342−- Types: `PascalCase`
343−- Constants: `SCREAMING_SNAKE_CASE`
344−
345−### Error Handling
346−
347−Use `Result<T, E>` for all fallible operations. Use `thiserror` for custom errors, `anyhow` for application errors.
348−
349−```rust
350−use thiserror::Error;
351−
352−#[derive(Error, Debug)]
353−pub enum ShareError {
354− #[error("File not found: {0}")]
355− FileNotFound(i32),
356−
357− #[error("Permission denied")]
358− PermissionDenied,
359−
360− #[error("Database error: {0}")]
361− Database(#[from] sea_orm::DbErr),
362−}
363−
364−pub async fn share_file(id: i32) -> Result<String, ShareError> {
365− let file = find_file(id).await.ok_or(ShareError::FileNotFound(id))?;
366− // Implementation
367− Ok(share_url)
368−}
369−```
370−
371−### Async Code
372−
373−- Use `async/await` syntax
374−- Prefer `tokio` primitives (`tokio::sync::RwLock`, `tokio::spawn`)
375−- Avoid blocking operations (use `tokio::fs` not `std::fs`)
376−- Use `tokio::task::spawn_blocking` for CPU-intensive work
377−
378−### Resumable Jobs
379−
380−Store job state within the job struct. Use `#[serde(skip)]` for non-persistent fields.
381−
382−```rust
383−#[derive(Serialize, Deserialize)]
384−pub struct FileCopyJob {
385− pub source: PathBuf,
386− pub destination: PathBuf,
387− pub copied_files: Vec<PathBuf>, // Persisted for resumability
388−
389− #[serde(skip)]
390− pub progress_tx: Option<tokio::sync::mpsc::Sender<Progress>>, // Not persisted
391−}
392−
393−impl Job for FileCopyJob {
394− async fn run(&mut self, ctx: &JobContext) -> Result<()> {
395− ctx.log().info("Starting file copy job");
396−
397− for file in &self.files_to_copy {
398− if self.copied_files.contains(file) {
399− continue; // Skip already copied files on resume
400− }
401−
402− copy_file(file).await?;
403− self.copied_files.push(file.clone());
404− }
405−
406− Ok(())
407− }
408−}
409−```
410−
411−### Documentation
412−
413−**Core principle:** Explain WHY, not WHAT. Keep comments as short as possible. One sentence explaining rationale beats a paragraph restating code.
414−
415−**Module docs (`//!`):**
416−- Add a title with `#` for the module name
417−- Explain what the module does in plain language (not bullet points)
418−- Include design rationale naturally in prose
419−- Add runnable code examples showing usage
420−
421−````rust
422−//! # File Sharing System
423−//!
424−//! `core::ops::files::share` provides temporary file sharing via signed URLs.
425−//! Share links expire after 7 days by default to prevent indefinite access to
426−//! private files. UUID v5 deterministic IDs ensure the same file generates
427−//! consistent share URLs across devices without coordination.
428−//!
429−//! ## Example
430−//! ```rust,no_run
431−//! use spacedrive_core::ops::files::share::{ShareFileAction, ShareFileInput};
432−//!
433−//! let input = ShareFileInput { file_id: 123, recipient: "user@example.com" };
434−//! let output = ShareFileAction::run(input, &ctx).await?;
435−//! ```
436−````
437−
438−**Function docs (`///`):**
439−- First line: brief one-liner
440−- Second paragraph: explain design rationale and why this exists
441−- Document error handling philosophy when relevant
442−- Explain non-obvious behavior and platform differences
443−
444−```rust
445−/// Creates a share link with automatic expiration.
446−///
447−/// Share links use signed JWTs so the daemon can validate them without
448−/// database lookups on every request. Expiration is enforced server-side
449−/// to prevent timezone manipulation. Recipients without library access
450−/// get read-only access to the specific file only.
451−///
452−/// Returns `ShareError::PermissionDenied` if the file is private and
453−/// the recipient isn't a library member. The share is still created
454−/// but marked inactive for audit logging.
455−pub async fn share_file(input: ShareFileInput) -> Result<ShareFileOutput>
456−```
457−
458−**Inline comments:**
459−- Delete comments that restate obvious code
460−- Explain WHY for decisions, not WHAT the code does
461−- Use one sentence when possible
462−- Only expand for truly non-obvious consequences
463−
464−```rust
465−// Good: explains WHY
466−// Lowercase for case-insensitive search matching.
467−let ext = path.extension().map(|e| e.to_lowercase());
468−
469−// Bad: restates code
470−// Extract file extension and convert to lowercase
471−let ext = path.extension().map(|e| e.to_lowercase());
472−
473−// Good: explains consequence
474−// Preserve ephemeral UUIDs so tags attached during browsing survive promotion to managed location.
475−let uuid = ephemeral_cache.get(path).unwrap_or_else(|| Uuid::new_v4());
476−
477−// Bad: verbose explanation of obvious behavior
478−// UUID assignment strategy:
479−// 1. First check if there's an ephemeral UUID
480−// 2. If not, generate a new one
481−let uuid = ephemeral_cache.get(path).unwrap_or_else(|| Uuid::new_v4());
482−```
483−
484−**Error handling comments:**
485−Explain strategy and recovery, not just "log and continue".
486−
487−```rust
488−// Good: explains recovery
489−// Best-effort: continue with remaining moves, stale paths cleaned up on next reindex.
490−Err(e) => ctx.log(format!("Failed to move: {}", e)),
491−
492−// Bad: states the obvious
493−// Log error but continue
494−Err(e) => ctx.log(format!("Failed to move: {}", e)),
495−```
496−
497−**Platform-specific comments:**
498−Explain consequences, not implementation blockers.
499−
500−```rust
501−// Good: explains why and fallback
502−#[cfg(windows)]
503−pub fn get_inode(_metadata: &std::fs::Metadata) -> Option<u64> {
504− // Windows file indices are unstable across reboots; fall back to path-only matching.
505− None
506−}
507−
508−// Bad: over-explains implementation details
509−#[cfg(windows)]
510−pub fn get_inode(_metadata: &std::fs::Metadata) -> Option<u64> {
511− // Windows doesn't have inodes.
512− // The method `file_index()` is unstable (issue #63010).
513− // Returning None is safe as the field is Optional.
514− None
515−}
516−```
517−
518−**Never use:**
519−- Placeholder comments ("for now", "TODO: extract this later")
520−- Markdown formatting (`**bold**`, `_italic_`) in code comments
521−- ASCII diagrams (put those in `/docs/` if needed)
522−- Section divider comments (`// ========== Section ==========`)
523−- Comments explaining removed code during refactors
524−
525−Track future work in GitHub issues, not code comments.
526−
527−### Formatting
528−
529−Run `cargo fmt` before committing. Tabs for indentation. No emojis.
530−
531−## Logging
532−
533−### Setup
534−
535−Use `tracing_subscriber` in main or examples:
536−
537−```rust
538−use tracing_subscriber::EnvFilter;
539−
540−fn main() {
541− tracing_subscriber::fmt()
542− .with_env_filter(
543− EnvFilter::try_from_default_env()
544− .unwrap_or_else(|_| EnvFilter::new("sd_core=info"))
545− )
546− .init();
547−}
548−```
549−
550−## Writing Style
551−
552−This applies to all documentation, code comments, and design documents.
553−
554−Use clear, simple language. Write short, impactful sentences. Use active voice. Focus on practical, actionable information.
555−
556−Address the reader directly with "you" and "your". Support claims with data and examples when possible.
557−
558−Avoid these constructions:
559−
560−- Em dashes (use commas or periods)
561−- "Not only this, but also this"
562−- Metaphors and cliches
563−- Generalizations
564−- Setup language like "in conclusion"
565−- Unnecessary adjectives and adverbs
566−- Emojis, hashtags, markdown formatting in prose
567−
568−Avoid these words:
569−comprehensive, delve, utilize, harness, realm, tapestry, unlock, revolutionary, groundbreaking, remarkable, pivotal
570−
571−### Macros
572−
573−Use `tracing` macros, never `println!`:
574−
575−```rust
576−use tracing::{info, warn, error, debug};
577−
578−info!("Server started on port {}", port);
579−debug!(file_id = %id, "Processing file");
580−warn!(error = %e, "Retrying operation");
581−error!("Failed to connect to database");
582−```
583−
584−### Job Logging
585−
586−Use `ctx.log()` in job implementations for automatic `job_id` tagging:
587−
588−```rust
589−impl Job for MyJob {
590− async fn run(&mut self, ctx: &JobContext) -> Result<()> {
591− ctx.log().info("Job started");
592− ctx.log().debug!(progress = %self.progress, "Processing");
593− Ok(())
594− }
595−}
596−```
597−
598−### Log Levels
599−
600−- `debug`: Detailed flow for troubleshooting
601−- `info`: User-relevant events (server start, job completion)
602−- `warn`: Recoverable issues (retry, fallback)
603−- `error`: Failures requiring attention
604−
605−### Environment Control
606−
607−Use `RUST_LOG` environment variable:
608−
609−```bash
610−RUST_LOG=debug cargo run --bin sd-cli
611−RUST_LOG=sd_core=trace cargo run
612−RUST_LOG=sd_core::ops=debug cargo run
613−```
614−
615−## Testing
616−
617−### Test Organization
618−
619−- Unit tests: Colocated in `#[cfg(test)]` modules
620−- Integration tests: `tests/` directory at crate root
621−
622−```rust
623−// src/ops/files/share/action.rs
624−
625−#[cfg(test)]
626−mod tests {
627− use super::*;
628−
629− #[tokio::test]
630− async fn test_share_file() {
631− let input = ShareFileInput {
632− file_id: 1,
633− recipient: "test@example.com".to_string(),
634− };
635−
636− let output = share_file(input).await.unwrap();
637− assert!(!output.share_id.is_empty());
638− }
639−}
640−```
641−
642−### Running Tests
643−
644−```bash
645−cargo test # All tests
646−cargo test test_share_file # Specific test
647−cargo test --lib # Library tests only
648−cargo test -- --nocapture # Show output
649−```
650−
651−## Task Tracking
652−
653−Spacedrive uses a file-based task system in `/.tasks/` to track features, epics, and development work. All task files are version-controlled alongside the code.
654−
655−### When to Create Tasks
656−
657−Create tasks for work that:
658−
659−- Introduces a new feature or capability
660−- Refactors a significant system or module
661−- Fixes a bug requiring architectural changes
662−- Implements a whitepaper specification
663−
664−Do not create tasks for:
665−
666−- Routine code formatting or style fixes
667−- Trivial bug fixes (single line changes)
668−- Documentation updates to existing features
669−- Dependency version bumps
670−
671−### Task Structure
672−
673−Each task is a Markdown file: `CATEGORY-###-title-slug.md`
674−
675−```yaml
676−---
677−id: CORE-042
678−title: "Implement file sharing API"
679−status: "In Progress"
680−assignee: "james"
681−priority: "High"
682−tags: ["core", "networking"]
683−whitepaper: "Section 4.2" # And/or design_doc: DESIGN_DOC_NAME.md
684−---
685−
686−## Description
687−Brief overview of what needs to be done and why.
688−
689−## Implementation Steps
690−- [ ] Create share action in src/ops/files/share
691−- [ ] Add database schema for shares table
692−- [ ] Implement expiration logic
693−
694−## Acceptance Criteria
695−- Share links work across all platforms
696−- Expired shares return 404
697−- Tests cover edge cases
698−```
699−
700−### Managing Tasks
701−
702−```bash
703−# List your active tasks
704−cargo run -p task-validator -- list --assignee "yourname" --status "In Progress"
705−
706−# List high priority tasks
707−cargo run -p task-validator -- list --priority "High" --sort-by id
708−
709−# Validate before committing (automatic via git hook)
710−cargo run -p task-validator -- validate
711−```
712−
713−### Task Lifecycle
714−
715−1. Create task file in `/.tasks/` with `status: "To Do"`
716−2. Update status to `"In Progress"` when you start work
717−3. Complete implementation and tests
718−4. Update status to `"Done"` and commit
719−
720−Full documentation: `/docs/core/task-tracking.md`
721−
722−## Debugging
723−
724−### Log Files
725−
726−Job logs live in the `job_logs` directory in the data folder root.
727−
728−### Daemon Restart
729−
730−After rebuilding, restart the daemon to use the latest code:
731−
732−```bash
733−cargo build
734−cargo run --bin sd-cli -- restart
735−```
736−
737−### Verbose Logging
738−
739−```bash
740−RUST_LOG=debug cargo run --bin sd-daemon
741−RUST_LOG=sd_core::jobs=trace cargo run
742−```
743−
744−## Documentation Locations
745−
746−- Core architecture: `/docs/core/`
747−- Design docs and RFCs: `/docs/core/design/`
748−- Application docs: `/docs/`
749−- Daemon details: `/docs/core/daemon.md`
750−- Task tracking: `/docs/core/task-tracking.md`
105+- You can view the logs of a job in the job_logs directory in the root of the data folder
106+- When testing the CLI, after compiling you must first use the `restart` command to ensure the Spacedrive daemon is using the latest build.
751107
