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
157 changes: 157 additions & 0 deletions artifacts/v031-phase2-features.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
# Features shipped in v0.3.1 second batch (2026-04-02)
artifacts:
# ── Rowan parser phases ──────────────────────────────────────────────

- id: FEAT-093
type: feature
title: Rowan YAML CST parser (Phase 1)
status: approved
description: >
Lossless, span-preserving YAML parser using rowan. 28 SyntaxKind
variants, hand-written lexer and recursive-descent parser with
indent tracking. Round-trip guarantee for all project YAML files.
links:
- type: satisfies
target: REQ-002

- id: FEAT-094
type: feature
title: HIR extraction from rowan CST (Phase 2)
status: approved
description: >
Walks rowan CST to extract Vec<SpannedArtifact> with byte-accurate
spans for every field. Cross-validated against serde-based parser.
links:
- type: satisfies
target: REQ-002

- id: FEAT-095
type: feature
title: Schema-driven YAML extraction (Phase 3)
status: approved
description: >
Uses yaml-section and shorthand-links schema metadata to parse
section-based YAML formats. One function replaces the 861-line
hardcoded STPA adapter. Handles generic and section formats.
links:
- type: satisfies
target: REQ-002
- type: satisfies
target: REQ-010

# ── Domain schemas ─────────────────────────────────────────────────

- id: FEAT-096
type: feature
title: DO-178C airborne software schema
status: approved
description: >
14 artifact types covering PSAC through SAS with DAL-based
traceability rules for aviation software certification.
links:
- type: satisfies
target: REQ-010

- id: FEAT-097
type: feature
title: EN 50128 railway software safety schema
status: approved
description: >
14 artifact types with SIL-dependent rules, tool qualification,
independent assessment for railway software.
links:
- type: satisfies
target: REQ-010

- id: FEAT-098
type: feature
title: IEC 61508 functional safety schema
status: approved
description: >
15 artifact types covering the full IEC 61508 V-model with
SIL-based traceability and independent assessment rules.
links:
- type: satisfies
target: REQ-010

- id: FEAT-099
type: feature
title: IEC 62304 medical device software schema
status: approved
description: >
13 artifact types with class-conditional verification rules
for medical device software (Class A/B/C).
links:
- type: satisfies
target: REQ-010

- id: FEAT-100
type: feature
title: ISO/PAS 8800 AI safety lifecycle schema
status: approved
description: >
12 artifact types for AI element safety in road vehicles.
Bridges to STPA-AI for ML controller traceability.
links:
- type: satisfies
target: REQ-010

- id: FEAT-101
type: feature
title: ISO 21448 SOTIF schema
status: approved
description: >
8 artifact types covering functional insufficiency hazards,
triggering conditions, and known/unknown unsafe scenarios.
links:
- type: satisfies
target: REQ-010

# ── Other features ─────────────────────────────────────────────────

- id: FEAT-102
type: feature
title: MCP server expanded to 9 tools
status: approved
description: >
Added rivet_get, rivet_coverage, rivet_schema, rivet_embed,
rivet_snapshot_capture, rivet_add to the MCP server with
proper JSON Schema inputSchema definitions.
links:
- type: satisfies
target: REQ-001

- id: FEAT-103
type: feature
title: rivet schema validate command
status: approved
description: >
Validates loaded schemas are well-formed: checks link types
exist, target types exist, traceability rule references valid.
Found 3 real errors in AADL cross-schema references.
links:
- type: satisfies
target: REQ-010

- id: FEAT-104
type: feature
title: Documentation refresh with 7 new topics
status: approved
description: >
New docs: embed-syntax, schemas-overview, schema/eu-ai-act,
schema/safety-case, schema/stpa-ai, schema/stpa-sec,
schema/research. Updated CLI and YAML references.
links:
- type: satisfies
target: REQ-001

- id: FEAT-105
type: feature
title: Pre-commit hook for formatting and clippy
status: approved
description: >
scripts/pre-commit runs cargo fmt and clippy before every commit.
Prevents CI format failures.
links:
- type: satisfies
target: REQ-001
14 changes: 7 additions & 7 deletions rivet-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6540,7 +6540,7 @@ fn cmd_lsp(cli: &Cli) -> Result<bool> {
};

// Build supplementary state for rendering
let store = db.store(source_set);
let store = db.store(source_set, schema_set);
let render_schema = db.schema(schema_set);
let mut render_graph = rivet_core::links::LinkGraph::build(&store, &render_schema);

Expand Down Expand Up @@ -6617,7 +6617,7 @@ fn cmd_lsp(cli: &Cli) -> Result<bool> {
match method {
"textDocument/hover" => {
let params: HoverParams = serde_json::from_value(req.params.clone())?;
let store = db.store(source_set);
let store = db.store(source_set, schema_set);
let result = lsp_hover(&params, &store);
connection.sender.send(Message::Response(Response {
id: req.id,
Expand All @@ -6628,7 +6628,7 @@ fn cmd_lsp(cli: &Cli) -> Result<bool> {
"textDocument/definition" => {
let params: GotoDefinitionParams =
serde_json::from_value(req.params.clone())?;
let store = db.store(source_set);
let store = db.store(source_set, schema_set);
let result = lsp_goto_definition(&params, &store);
connection.sender.send(Message::Response(Response {
id: req.id,
Expand All @@ -6638,7 +6638,7 @@ fn cmd_lsp(cli: &Cli) -> Result<bool> {
}
"textDocument/completion" => {
let params: CompletionParams = serde_json::from_value(req.params.clone())?;
let store = db.store(source_set);
let store = db.store(source_set, schema_set);
let schema = db.schema(schema_set);
let result = lsp_completion(&params, &store, &schema);
connection.sender.send(Message::Response(Response {
Expand Down Expand Up @@ -6923,7 +6923,7 @@ fn cmd_lsp(cli: &Cli) -> Result<bool> {
// and append document [[ID]] reference validation so
// broken wiki-links in markdown files are reported.
let mut new_diagnostics = db.diagnostics(source_set, schema_set);
let new_store = db.store(source_set);
let new_store = db.store(source_set, schema_set);
new_diagnostics
.extend(validate::validate_documents(&doc_store, &new_store));
lsp_publish_salsa_diagnostics(
Expand All @@ -6939,7 +6939,7 @@ fn cmd_lsp(cli: &Cli) -> Result<bool> {
);

// Rebuild render state
render_store = db.store(source_set);
render_store = db.store(source_set, schema_set);
let render_schema = db.schema(schema_set);
render_graph = rivet_core::links::LinkGraph::build(
&render_store,
Expand Down Expand Up @@ -6986,7 +6986,7 @@ fn cmd_lsp(cli: &Cli) -> Result<bool> {
// including document [[ID]] reference validation.
let mut diagnostics =
db.diagnostics(source_set, schema_set);
let store = db.store(source_set);
let store = db.store(source_set, schema_set);
diagnostics.extend(validate::validate_documents(
&doc_store, &store,
));
Expand Down
3 changes: 2 additions & 1 deletion rivet-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ rust-version.workspace = true


[features]
default = ["aadl"]
default = ["aadl", "rowan-yaml"]
rowan-yaml = []
oslc = ["dep:reqwest", "dep:urlencoding"]
wasm = ["dep:wasmtime", "dep:wasmtime-wasi"]
aadl = ["dep:spar-hir", "dep:spar-analysis"]
Expand Down
84 changes: 73 additions & 11 deletions rivet-core/src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ use crate::model::Artifact;
use crate::schema::{Schema, SchemaFile};
use crate::store::Store;
use crate::validate::Diagnostic;
#[cfg(feature = "rowan-yaml")]
use crate::yaml_hir;

// ── Salsa inputs ────────────────────────────────────────────────────────

Expand Down Expand Up @@ -109,6 +111,30 @@ pub fn parse_artifacts(db: &dyn salsa::Database, source: SourceFile) -> Vec<Arti
}
}

/// Parse artifacts from a single source file using the schema-driven rowan parser.
///
/// Uses `yaml_hir::extract_schema_driven` which reads `yaml-section` metadata
/// from the schema to discover sections and auto-convert shorthand links.
///
/// This is a salsa tracked function — results are memoized and only
/// recomputed when the `SourceFile` content or `SchemaInputSet` changes.
#[cfg(feature = "rowan-yaml")]
#[salsa::tracked]
pub fn parse_artifacts_v2(
db: &dyn salsa::Database,
source: SourceFile,
schema_set: SchemaInputSet,
) -> Vec<Artifact> {
let content = source.content(db);
let path = source.path(db);
let source_path = std::path::Path::new(&path);

let schema = build_schema(db, schema_set);
let parsed = yaml_hir::extract_schema_driven(&content, &schema, Some(source_path));

parsed.artifacts.into_iter().map(|sa| sa.artifact).collect()
}

/// Collect parse errors from all source files as diagnostics.
///
/// Each file that fails to parse produces a `yaml-parse-error` diagnostic
Expand Down Expand Up @@ -234,9 +260,9 @@ pub fn validate_all(
///
/// Both `validate_all` and this function call `build_pipeline`, which is a
/// plain (non-tracked) helper. The tracked functions that `build_pipeline`
/// delegates to (`parse_artifacts`) are individually cached by salsa, so
/// the repeated calls do NOT re-parse source files — only the lightweight
/// store/schema assembly runs twice.
/// delegates to (`parse_artifacts` / `parse_artifacts_v2`) are individually
/// cached by salsa, so the repeated calls do NOT re-parse source files —
/// only the lightweight store/schema assembly runs twice.
#[salsa::tracked]
pub fn evaluate_conditional_rules(
db: &dyn salsa::Database,
Expand Down Expand Up @@ -280,18 +306,53 @@ fn build_pipeline(
source_set: SourceFileSet,
schema_set: SchemaInputSet,
) -> (Store, Schema, LinkGraph) {
let store = build_store(db, source_set);
let store = build_store(db, source_set, schema_set);
let schema = build_schema(db, schema_set);
let graph = LinkGraph::build(&store, &schema);
(store, schema, graph)
}

/// Build an artifact `Store` from all source file inputs.
fn build_store(db: &dyn salsa::Database, source_set: SourceFileSet) -> Store {
///
/// When the `rowan-yaml` feature is enabled, uses the schema-driven rowan
/// parser (`parse_artifacts_v2`) which reads `yaml-section` metadata from
/// the schema. In debug builds, both parsers run and their output is
/// compared as a cross-check.
fn build_store(
db: &dyn salsa::Database,
source_set: SourceFileSet,
schema_set: SchemaInputSet,
) -> Store {
#[cfg(not(feature = "rowan-yaml"))]
let _ = schema_set;

let sources = source_set.files(db);
let mut store = Store::new();
for source in sources {
for artifact in parse_artifacts(db, source) {
#[cfg(feature = "rowan-yaml")]
let artifacts = {
let new_arts = parse_artifacts_v2(db, source, schema_set);

#[cfg(debug_assertions)]
{
let old_arts = parse_artifacts(db, source);
let new_ids: Vec<&str> = new_arts.iter().map(|a| a.id.as_str()).collect();
let old_ids: Vec<&str> = old_arts.iter().map(|a| a.id.as_str()).collect();
if old_ids != new_ids {
log::warn!(
"parser mismatch for {}: old={old_ids:?} new={new_ids:?}",
source.path(db),
);
}
}

new_arts
};

#[cfg(not(feature = "rowan-yaml"))]
let artifacts = parse_artifacts(db, source);

for artifact in artifacts {
// Use upsert to avoid panics on duplicate IDs across files.
store.upsert(artifact);
}
Expand Down Expand Up @@ -378,9 +439,9 @@ impl RivetDatabase {
false
}

/// Get the current store (computed from source inputs).
pub fn store(&self, source_set: SourceFileSet) -> Store {
build_store(self, source_set)
/// Get the current store (computed from source and schema inputs).
pub fn store(&self, source_set: SourceFileSet, schema_set: SchemaInputSet) -> Store {
build_store(self, source_set, schema_set)
}

/// Get the current merged schema (computed from schema inputs).
Expand Down Expand Up @@ -618,9 +679,10 @@ artifacts:
fn adding_artifact_appears_in_store() {
let mut db = RivetDatabase::new();
let sources = db.load_sources(&[("reqs.yaml", SOURCE_REQ)]);
let schemas = db.load_schemas(&[("test", TEST_SCHEMA)]);

// Initially: 1 artifact (REQ-001).
let store = db.store(sources);
let store = db.store(sources, schemas);
assert_eq!(store.len(), 1);
assert!(store.contains("REQ-001"));

Expand All @@ -636,7 +698,7 @@ artifacts:
"#;
db.update_source(sources, "reqs.yaml", combined.to_string());

let store = db.store(sources);
let store = db.store(sources, schemas);
assert_eq!(store.len(), 2);
assert!(store.contains("REQ-001"));
assert!(store.contains("REQ-002"));
Expand Down
Loading