Skip to content
Merged
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
24 changes: 24 additions & 0 deletions .agents/skills/develop-lithe/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,30 @@ the existing stack can reasonably avoid.
- Add tests in the owning crate for changes to commands, parsing, validation,
ordering, cancellation, or serialization.

#### Rust Core comments

Apply the following comment standard to first-party code under
`rust/lithe-core/`. It does not require comment coverage in the database helpers,
Windows/Tauri Rust crates, generated code, or third-party sources.

- Write comments in English and keep them accurate when behavior changes.
- Start each production module with a concise `//!` description of its
responsibility or architectural boundary.
- Use `///` for exported APIs, shared request and response types, core domain
types, and C ABI functions. Document ownership and add `# Safety` for unsafe
entry points; describe errors only when the failure contract is not obvious.
- Document enums, structs, variants, and fields whenever their names alone do
not make their semantics, allowed values, units, ownership, or protocol role
immediately clear. This requirement applies to internal types as well as
exported contracts.
- Use `//` inside implementations to explain non-obvious decisions and
constraints involving compatibility, determinism, ordering, security,
performance, or cross-platform behavior.
- In tests, comment the scenario, regression risk, or boundary being protected
when the test name and assertions do not make that intent clear.
- Do not narrate statements, restate descriptive names, or add comments to
trivial accessors and straightforward control flow solely for coverage.

### Windows React and Tauri

- Use Bun for frontend scripts and Tauri 2 for the Windows host. Keep React
Expand Down
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@

Before any work in this repository, load and follow the `develop-lithe` skill
at `.agents/skills/develop-lithe/SKILL.md`. That Skill is the single source of
truth for AI coding and verification rules.
truth for AI coding and verification rules, including the required Rust Core
comment standard.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ Before submitting a change, run:
./scripts/verify-rust-core.sh
```

See [Repository layout and shared boundaries](./docs/architecture/repository-layout.md) for directory ownership, cross-platform boundaries, and sharing rules. Include your verification steps and known limitations when submitting a change.
See [Repository layout and shared boundaries](./docs/architecture/repository-layout.md) for directory ownership, cross-platform boundaries, sharing rules, and the required Rust Core comment standard. Include your verification steps and known limitations when submitting a change.

## Project support

Expand Down
2 changes: 1 addition & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ open dist/Lithe.app
./scripts/verify-rust-core.sh
```

目录归属、跨平台边界和共享规则见[仓库目录与共享边界](./docs/architecture/repository-layout.md)。提交功能改动时,请说明验证方式和已知限制。
目录归属、跨平台边界、共享规则以及 Rust Core 必须遵守的注释规范见[仓库目录与共享边界](./docs/architecture/repository-layout.md)。提交功能改动时,请说明验证方式和已知限制。

## 项目支持

Expand Down
14 changes: 14 additions & 0 deletions docs/architecture/repository-layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,20 @@ The dependency direction is `protocol <- domain packages <- runtime/FFI`. A doma

Moving Rust files must not change JSON command strings, Serde field names, error codes, or the exported C symbols. Directory-sensitive fixtures and embedded resources must use `CARGO_MANIFEST_DIR` instead of paths derived from a module's current depth.

### Rust Core comment standard

First-party production modules under `rust/lithe-core/` start with an English
`//!` description of their responsibility or boundary. Exported APIs, shared
request and response structures, core domain types, and C ABI functions use
`///`; unsafe entry points document pointer ownership and `# Safety`
requirements. Enums, structs, variants, and fields whose names do not make
their semantics, allowed values, units, ownership, or protocol role immediately
clear are documented even when they are internal. Implementation comments explain non-obvious compatibility,
determinism, ordering, security, performance, or cross-platform constraints.
They should explain why the code has its shape instead of narrating individual
statements. Tests document scenarios or regression risks only when their names
and assertions are not already sufficient.

## Ownership rules

| Shared Rust Core | Platform-owned adapters |
Expand Down
34 changes: 31 additions & 3 deletions rust/lithe-core/src/execution/configuration.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Run-configuration schemas, layered overrides, and deterministic generation.

use super::types::{Confidence, Execution};
use crate::languages::JavaRunConfigurationsRequest;
use crate::protocol::{invalid_relative_path, CoreError, ErrorCode};
Expand All @@ -17,12 +19,14 @@ const SIDECAR_VERSION: u32 = 1;

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
/// Request to validate the layered configuration documents for a workspace.
pub struct InspectRequest {
pub root: String,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
/// Request to regenerate detected configurations from the current project tree.
pub struct GenerateRequest {
pub root: String,
#[serde(default)]
Expand All @@ -33,6 +37,7 @@ pub struct GenerateRequest {

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
/// Request to merge configuration layers and resolve host toolchains.
pub struct ResolveRequest {
pub root: String,
#[serde(default)]
Expand All @@ -41,8 +46,11 @@ pub struct ResolveRequest {

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
/// Host-discovered executable that may satisfy a configuration requirement.
pub struct ToolchainCandidate {
/// Host-stable candidate identifier referenced by resolved configurations.
pub id: String,
/// Toolchain role such as `java` or `maven`, not a display label.
#[serde(rename = "type")]
pub kind: String,
#[serde(default)]
Expand All @@ -53,6 +61,7 @@ pub struct ToolchainCandidate {

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
/// Request to turn one resolved configuration into a process launch plan.
pub struct LaunchPlanRequest {
pub root: String,
pub configuration_id: String,
Expand All @@ -66,8 +75,10 @@ pub struct LaunchPlanRequest {

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
/// Editable options applied to a team or machine-local configuration layer.
pub struct UpdateOptionsRequest {
pub root: String,
/// Persistence layer: `project` for shared configuration or `local` for this host.
pub scope: String,
pub configuration_id: String,
#[serde(default)]
Expand All @@ -92,10 +103,13 @@ pub struct UpdateOptionsRequest {

#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
/// Request to add an explicitly user-authored run configuration.
pub struct CreateUserConfigurationRequest {
pub root: String,
/// Persistence layer: `project` for shared configuration or `local` for this host.
pub scope: String,
pub name: String,
/// UI configuration kind mapped to a namespaced provider during creation.
#[serde(rename = "type")]
pub kind: String,
#[serde(default)]
Expand All @@ -106,6 +120,7 @@ pub struct CreateUserConfigurationRequest {

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
#[serde(rename_all = "camelCase")]
/// Versioned run-configuration document stored below `.lithe/run`.
pub struct RunConfigurationDocument {
pub version: u32,
#[serde(default)]
Expand Down Expand Up @@ -188,6 +203,7 @@ fn migrate_configuration_value(item: &mut Value) {

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
/// Fingerprint and inputs used to decide whether generated output is stale.
pub struct GeneratorMetadata {
pub fingerprint: String,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
Expand All @@ -196,12 +212,15 @@ pub struct GeneratorMetadata {

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
/// Debug adapter supported by a runnable configuration.
pub struct DebugCapability {
/// Stable adapter identifier, currently `jdwp` for JVM configurations.
pub adapter: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
/// Ecosystem-neutral command, identity, and launch metadata for one runnable item.
pub struct RunConfiguration {
pub id: String,
pub name: String,
Expand Down Expand Up @@ -231,6 +250,7 @@ pub struct RunConfiguration {
pub extensions: BTreeMap<String, Value>,
#[serde(default)]
pub disabled: bool,
/// Workspace-relative manifest or source file that produced the configuration.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source: Option<String>,
}
Expand Down Expand Up @@ -261,6 +281,7 @@ impl RunConfiguration {

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
/// Versioned requirements written separately from run configurations.
pub struct ToolchainRequirementsDocument {
pub version: u32,
#[serde(default)]
Expand All @@ -269,7 +290,9 @@ pub struct ToolchainRequirementsDocument {

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
/// Constraints the host uses when selecting one toolchain candidate.
pub struct ToolchainRequirement {
/// Toolchain role matched against [`ToolchainCandidate::kind`].
#[serde(rename = "type")]
pub kind: String,
#[serde(default)]
Expand All @@ -284,6 +307,7 @@ pub struct ToolchainRequirement {
pub java: Option<String>,
}

/// Validates configuration and sidecar documents without mutating the workspace.
pub fn inspect(request: InspectRequest) -> Result<Value, CoreError> {
let root = existing_root(&request.root)?;
let generated = read_document(&root, "run/generated.json")?;
Expand Down Expand Up @@ -347,6 +371,7 @@ pub fn inspect(request: InspectRequest) -> Result<Value, CoreError> {
}))
}

/// Detects runnable project entries and writes a deterministic generated layer.
pub fn generate(request: GenerateRequest) -> Result<Value, CoreError> {
let root = existing_root(&request.root)?;
let mut paths = request
Expand Down Expand Up @@ -729,6 +754,7 @@ fn is_nested_checkout_path(value: &str) -> bool {
})
}

/// Merges generated, team, and local layers and selects compatible toolchains.
pub fn resolve(request: ResolveRequest) -> Result<Value, CoreError> {
let root = existing_root(&request.root)?;
let generated = read_document_value(&root, "run/generated.json")?.ok_or_else(|| {
Expand Down Expand Up @@ -831,6 +857,7 @@ pub fn resolve(request: ResolveRequest) -> Result<Value, CoreError> {
}))
}

/// Persists editable configuration options in the requested ownership layer.
pub fn update_options(request: UpdateOptionsRequest) -> Result<Value, CoreError> {
let root = existing_root(&request.root)?;
let relative = scope_document(&request.scope)?;
Expand Down Expand Up @@ -921,6 +948,7 @@ pub fn update_options(request: UpdateOptionsRequest) -> Result<Value, CoreError>
}))
}

/// Creates a user configuration while preserving stable IDs in existing layers.
pub fn create_user_configuration(
request: CreateUserConfigurationRequest,
) -> Result<Value, CoreError> {
Expand Down Expand Up @@ -1023,6 +1051,7 @@ pub fn create_user_configuration(
}))
}

/// Resolves one configuration into the exact executable, arguments, and environment.
pub fn create_launch_plan(request: LaunchPlanRequest) -> Result<Value, CoreError> {
let resolved = resolve(ResolveRequest {
root: request.root,
Expand Down Expand Up @@ -2147,8 +2176,7 @@ fn declared_go_version(root: &Path) -> Option<String> {

fn declared_python_version(root: &Path) -> Option<String> {
let expression =
regex::Regex::new(r#"(?m)^\s*(?:requires-python|python)\s*=\s*[\"']([^\"']+)[\"']"#)
.ok()?;
regex::Regex::new(r#"(?m)^\s*(?:requires-python|python)\s*=\s*["']([^"']+)["']"#).ok()?;
highest_version(
project_manifest_paths(root, &["pyproject.toml"])
.into_iter()
Expand Down Expand Up @@ -2263,7 +2291,7 @@ fn declared_java_version(root: &Path) -> Option<(String, Option<String>)> {
}
}
if let Ok(text) = fs::read_to_string(root.join("mise.toml")) {
let expression = regex::Regex::new(r#"(?m)^\s*java\s*=\s*[\"']([^\"']+)[\"']"#).ok()?;
let expression = regex::Regex::new(r#"(?m)^\s*java\s*=\s*["']([^"']+)["']"#).ok()?;
if let Some(value) = expression
.captures(&text)
.and_then(|capture| capture.get(1))
Expand Down
2 changes: 2 additions & 0 deletions rust/lithe-core/src/execution/detectors/cargo.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Cargo binary discovery from manifests and conventional source layouts.

use super::{Detected, DirectoryContext};

/// Cargo binaries come from `[[bin]]` entries, or implicitly from `src/main.rs`.
Expand Down
3 changes: 3 additions & 0 deletions rust/lithe-core/src/execution/detectors/compose.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Docker Compose service discovery from the standard manifest names.

use super::{Detected, DirectoryContext};

const FILES: &[&str] = &[
Expand All @@ -7,6 +9,7 @@ const FILES: &[&str] = &[
"compose.yaml",
];

/// Returns one service configuration for each declared Compose service.
pub fn detect(ctx: &DirectoryContext) -> Vec<Detected> {
let Some(file) = ctx.any_of(FILES) else {
return Vec::new();
Expand Down
2 changes: 2 additions & 0 deletions rust/lithe-core/src/execution/detectors/go.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Go application discovery from module roots and conventional command layouts.

use super::super::types::Confidence;
use super::{Detected, DirectoryContext};

Expand Down
2 changes: 2 additions & 0 deletions rust/lithe-core/src/execution/detectors/gradle.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Gradle service discovery without starting Gradle or evaluating build scripts.

use super::super::types::Confidence;
use super::{Detected, DirectoryContext};

Expand Down
2 changes: 2 additions & 0 deletions rust/lithe-core/src/execution/detectors/make.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Runnable Make target discovery with conservative service classification.

use super::super::types::Confidence;
use super::{Detected, DirectoryContext};

Expand Down
2 changes: 2 additions & 0 deletions rust/lithe-core/src/execution/detectors/maven.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Maven service discovery from the declared reactor and applied plugins.

use super::super::types::Confidence;
use super::{Detected, DirectoryContext};
use crate::project::{declared_modules, DeclaredModule};
Expand Down
6 changes: 6 additions & 0 deletions rust/lithe-core/src/execution/detectors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ pub struct Detected {
}

impl Detected {
/// Creates a short-lived or interactive application detection.
pub fn application(
provider: &str,
name: &str,
Expand All @@ -80,6 +81,7 @@ impl Detected {
)
}

/// Creates a long-running service detection.
pub fn service(
provider: &str,
name: &str,
Expand All @@ -99,6 +101,7 @@ impl Detected {
)
}

/// Creates a command expected to run to completion.
pub fn task(
provider: &str,
name: &str,
Expand Down Expand Up @@ -135,6 +138,7 @@ impl Detected {
}
}

/// Replaces the default declared confidence with the detector's evidence level.
pub fn with_confidence(mut self, confidence: Confidence) -> Self {
self.confidence = confidence;
self
Expand All @@ -157,6 +161,7 @@ impl Detected {
self
}

/// Declares the debug adapter supported by this detection.
pub fn with_debug(mut self, adapter: &str) -> Self {
self.debug = Some(adapter.to_string());
self
Expand All @@ -171,6 +176,7 @@ impl Detected {
self
}

/// Attaches provider-specific metadata without expanding the shared schema.
pub fn with_extension(mut self, namespace: &str, value: serde_json::Value) -> Self {
self.extensions.insert(namespace.to_string(), value);
self
Expand Down
3 changes: 3 additions & 0 deletions rust/lithe-core/src/execution/detectors/npm.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! JavaScript package-script discovery and framework classification.

use super::super::types::Confidence;
use super::{Detected, DirectoryContext};
use serde_json::Value;
Expand Down Expand Up @@ -87,6 +89,7 @@ const MANAGERS: &[(&str, &str)] = &[
("package-lock.json", "npm"),
];

/// Classifies runnable package scripts using declared dependencies and commands.
pub fn detect(ctx: &DirectoryContext) -> Vec<Detected> {
let Some(text) = ctx.read("package.json") else {
return Vec::new();
Expand Down
2 changes: 2 additions & 0 deletions rust/lithe-core/src/execution/detectors/procfile.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
//! Procfile process discovery for commands that can be launched without a shell.

use super::super::types::Confidence;
use super::{Detected, DirectoryContext};

Expand Down
3 changes: 3 additions & 0 deletions rust/lithe-core/src/execution/detectors/python.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//! Python entry-point and framework discovery from declarations and conventions.

use super::super::types::Confidence;
use super::{Detected, DirectoryContext};

/// Combines declared Python entry points with framework-based conventions.
pub fn detect(ctx: &DirectoryContext) -> Vec<Detected> {
let mut detected = pyproject(ctx);
detected.extend(frameworks(ctx));
Expand Down
Loading
Loading