diff --git a/CHANGELOG.md b/CHANGELOG.md index 333290d..70e9e06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,72 @@ ## [Unreleased] +## [0.11.0] — 2026-05-21 + +Theme: **the cross-git investigation**. A 2026-05-19 investigation +(six adversarial personas, ten scenarios, three test-bed repos in +different orgs) found eleven ways `rivet validate` could report a +green PASS while the project was, in fact, broken — the Cederqvist +"the tool reports textual success over a semantically-failed +operation" cliff. The findings were filed as typed Rivet artifacts +(FEAT-135, REQ-062..REQ-077) and closed across three waves. Minor +version: new flags and validation behaviour, no breaking schema or +CLI removal — but `rivet validate` is now stricter (it fails on +inputs it previously skipped silently), so a project that "passed" +on 0.10.x may legitimately FAIL on 0.11.0. That is the fix working. + +### Added + +- `rivet validate --with-externals-validate` — runs validate inside + each linked external project and surfaces its diagnostics under a + `cross_repo_diagnostics` array (REQ-065). +- `rivet validate --strict-orphans` — promote `orphan-artifact` + warnings to errors for CI (REQ-076). +- `rivet supplier pull --accept-drift` — the explicit auditor + override for sha256 drift (REQ-068). +- `rivet docs cross-repo-ci` — a new topic: the multi-repo CI + sequence, a worked GitHub Actions example, the AoU register + (REQ-071). +- `docs/rivet-is-not.md` — an ISO 26262-10 SEooC Safety-Manual-draft + "Rivet is not..." doc with the Cross-org Assumptions-of-Use + register AoU-X1..X7 (REQ-072), linked from the README. +- `external-anchor` schema now declares the `cited-source` field + (REQ-066); `docs/historical/` archive + `docs/README.md`. + +### Fixed + +- **`rivet validate` reported `PASS` over artifact files that failed + to parse** — the skip was a stderr log line, uncounted. Skipped + files are now Error diagnostics (`artifact-parse-error`) and the + run exits non-zero (REQ-062). The headline finding. +- **Duplicate artifact IDs** silently collapsed via `Store::upsert` + last-write-wins — now detected at load time (`duplicate-artifact-id`, + REQ-075). The new check immediately surfaced a real `REQ-060` + collision in Rivet's own `artifacts/` (resolved → `REQ-077`). +- **Orphan artifacts** (no inbound/outbound links) were invisible to + `rivet validate` — now reported (`orphan-artifact`, Warning; + REQ-076). +- `rivet init --preset {do-178c, en-50128, iec-61508, iec-62304}` + produced unvalidatable projects — the four safety-critical schemas + are now embedded in the binary (REQ-063). +- A `derives-from-external` link did not satisfy a required + `derives-from` link-field — the cross-org variant now counts + (REQ-064). +- `rivet supplier pull` silently overwrote the cache on sha256 + drift — it now refuses, naming both hashes (REQ-068). +- Stale documentation: MSRV (1.85 → 1.89), the schema/oracle/module + counts in `schemas.md` / `architecture.md` / `oracles.md` (REQ-074). + +### Changed + +- `rivet validate --format json` diagnostics now include the `rule` + field. +- 17 stale planning docs archived to `docs/historical/`; + `docs/historical/` is doc-check-exempt like `plans/` / `design/`. +- The `rivet docs cross-repo` topic now documents both cross-repo + mechanisms (`externals:` vs `external-anchor` + `cited-source`) + and when to use each (REQ-067). + ## [0.10.1] — 2026-05-19 Theme: **adversarial-review action items + user-reported regressions**. diff --git a/Cargo.lock b/Cargo.lock index 53187a2..056c3fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -973,7 +973,7 @@ dependencies = [ [[package]] name = "etch" -version = "0.10.1" +version = "0.11.0" dependencies = [ "petgraph 0.7.1", ] @@ -2709,7 +2709,7 @@ dependencies = [ [[package]] name = "rivet-cli" -version = "0.10.1" +version = "0.11.0" dependencies = [ "anyhow", "axum", @@ -2737,7 +2737,7 @@ dependencies = [ [[package]] name = "rivet-core" -version = "0.10.1" +version = "0.11.0" dependencies = [ "anyhow", "criterion", diff --git a/Cargo.toml b/Cargo.toml index 820ad06..13668e6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ members = [ ] [workspace.package] -version = "0.10.1" +version = "0.11.0" authors = ["PulseEngine "] edition = "2024" license = "Apache-2.0" diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..818b72e --- /dev/null +++ b/docs/README.md @@ -0,0 +1,80 @@ +--- +id: DOC-DOCS-README +title: docs/ folder map and lifecycle +type: reference +status: current +tags: [docs, structure, lifecycle, governance] +--- + +# `docs/` — folder map and lifecycle + +This directory holds Rivet's prose documentation. The top level (`docs/*.md`) +carries the stable, user-facing reference and guide material; four +subdirectories carry material with a more specific lifecycle. Every `.md` file +directly under `docs/` carries YAML frontmatter (`id`, `title`, `type`, +`status`, `tags`) so it is a first-class document artifact — `rivet validate` +skips files without frontmatter and reports them. Per the doc scanner's +`read_dir` contract, only the top level of each `docs:` path is scanned for +frontmatter; subdirectory files are not gated, but carrying frontmatter is +still recommended. + +The `status` field is the lifecycle marker. The intended values: + +- `current` — describes shipped behaviour and is believed accurate against the + current release. The default state for reference and guide material. +- `snapshot` — a point-in-time record (an audit, a comparison, a status page). + Accurate as of its date; not maintained to track `main`. +- `draft` — work in progress; not yet a settled description of anything. +- `historical` — superseded planning or analysis kept for the record. Lives + under `docs/historical/`. + +## Top level — `docs/*.md` + +Stable, user-facing reference and guide material: `getting-started.md`, +`architecture.md`, `schemas.md`, `oracles.md`, `feature-model-schema.md`, +`feature-model-bindings.md`, `pre-commit.md`, `mutation-testing.md`, +`verification.md`, `srs.md`, `stpa-sec.md`, plus the positioning documents +(`what-is-rivet.md`, `rivet-is-not.md`, `intro-talk-*.md`). These are the docs +a reader is expected to land on. They are maintained to track `main`; when they +drift, they get a `status: snapshot` demotion or an `UPDATE` verdict in the +next docs audit. **Lifecycle:** long-lived; updated in place; never deleted +while the feature they describe ships. + +## `design/` + +Forward-looking design notes — RFC-style decision documents for a single +feature track (status-gate rules, variant-aware properties, the +tool-confidence-level dossier, cross-org supplier traceability, …). A design +doc is written before or during implementation and is rarely revisited once the +feature ships; it stays as a decision record. Design docs may carry the +`` marker, which exempts +them from the documentation invariants that gate shipped reference docs. +**Lifecycle:** authored once per feature track; kept as a decision record; +archived to `historical/` only when wholly superseded. + +## `plans/` + +In-flight implementation plans — current work only. A plan describes a body of +work that has not yet landed. Once the work ships, the plan has served its +purpose and moves to `historical/`. This directory should be near-empty most of +the time; a long-lived plan is a signal that the work stalled or that the +document is really a design note in the wrong folder. **Lifecycle:** short; +created for in-flight work, moved to `historical/` on completion. + +## `research/` + +Scoped research reports with explicit verdicts — competitive analysis, +feasibility spikes, prototype reports, the cross-git investigation, the docs +audits. A research report is a `snapshot` by nature: it is accurate as of its +date and is not maintained afterwards. Negative results belong here too and are +treated as first-class (e.g. the MIRAI prototype report). **Lifecycle:** each +report is written once, dated, and frozen as a `snapshot`; old reports are kept +for the record rather than updated. + +## `historical/` + +The dated archive of completed plans and superseded analysis. Nothing here is +maintained; documents land here when their `design/` or `plans/` originals are +overtaken by shipped work or by a newer document. Keeping them (rather than +deleting) preserves the project's decision history for auditors. **Lifecycle:** +write-once on arrival; `status: historical`; never updated, never deleted. diff --git a/docs/adoption-status.md b/docs/adoption-status.md index 4b91813..0e890fa 100644 --- a/docs/adoption-status.md +++ b/docs/adoption-status.md @@ -1,3 +1,11 @@ +--- +id: DOC-ADOPTION-STATUS +title: rivet-validate adoption status +type: status +status: snapshot +tags: [adoption, status, audit, dogfood] +--- + # `rivet-validate` adoption status This file records the most recent run of diff --git a/docs/architecture.md b/docs/architecture.md index 8360c4c..4dfdaad 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -67,32 +67,98 @@ root: RivetSystem::RivetCli.Impl ### 2.1 rivet-core Modules +The library crate has grown well beyond the original core set. The table below groups the +modules by concern; consult `ls rivet-core/src/` for the authoritative file list. + +**Artifact model and storage** + | Module | Purpose | |--------------|------------------------------------------------------------------| | `model` | Core data types: `Artifact`, `Link`, `ProjectConfig`, `SourceConfig` | | `store` | In-memory artifact store with by-ID and by-type indexing | | `schema` | Schema loading, merging, artifact type and link type definitions | | `links` | `LinkGraph` construction via petgraph, backlinks, orphan detection | +| `document` | Markdown documents with YAML frontmatter and wiki-link references | +| `results` | Test run results model, YAML loading, and `ResultStore` | + +**Validation, coverage, and analysis** + +| Module | Purpose | +|--------------|------------------------------------------------------------------| | `validate` | Validation engine: types, fields, cardinality, traceability rules | | `coverage` | Traceability coverage computation per rule | +| `coverage_evidence` | Coverage-to-evidence cross-checking | | `matrix` | Traceability matrix computation (forward and backward) | | `query` | Query engine: filter artifacts by type, status, tag, link presence | | `diff` | Artifact diff and diagnostic diff between two store snapshots | -| `document` | Markdown documents with YAML frontmatter and wiki-link references | -| `results` | Test run results model, YAML loading, and `ResultStore` | -| `adapter` | Adapter trait and configuration for import/export | -| `reqif` | ReqIF 1.2 XML import/export adapter | -| `oslc` | OSLC client for discovery, query, CRUD, and sync (feature-gated) | +| `impact` | Change-impact analysis between store snapshots | +| `convergence`| Convergence / gap-closure tracking | +| `doc_check` | Documentation invariants (`rivet docs check`) over `.md` bodies | +| `lifecycle` | Artifact status lifecycle transitions and gating | +| `compliance` | Compliance-report computation across domain schemas | + +**Incremental engine (rowan + salsa)** + +| Module | Purpose | +|---------------|-----------------------------------------------------------------| +| `db` | salsa database: tracked queries and incremental revalidation | +| `yaml_cst` | Lossless YAML concrete syntax tree (rowan) | +| `yaml_hir` | Higher-level YAML IR: artifact / link extraction from the CST | +| `yaml_edit` | Structure-preserving YAML edits for `rivet add` / `modify` | +| `sexpr` | S-expression parser for `validation-rules:` / status gates | +| `sexpr_eval` | S-expression evaluator against the artifact store | + +**Cross-repo, variants, and provenance** + +| Module | Purpose | +|-----------------|---------------------------------------------------------------| +| `externals` | Cross-repo `externals:` resolution, `git clone`/`fetch`, lock | +| `baseline` | Named baseline tags and baseline-scoped validation | +| `snapshot` | Point-in-time store snapshots for diff / impact | +| `feature_model` | Feature-model parsing and SAT-style constraint checking | +| `variant_emit` | Variant-resolved artifact emission | +| `commits` | Git commit-trailer parsing and commit-to-artifact coverage | +| `cited_source` | `cited-source` hash tracking and drift detection | +| `ownership` | Artifact ownership / `CODEOWNERS`-style attribution | + +**Mutation, formats, and integration** + +| Module | Purpose | +|----------------|----------------------------------------------------------------| +| `mutate` | Schema-validated artifact mutation (`add`, `modify`, `link`, …) | +| `adapter` | Adapter trait and configuration for import/export | +| `reqif` | ReqIF 1.2 XML import/export adapter | +| `oslc` | OSLC client library (not exposed as a CLI subcommand — see §7) | | `wasm_runtime` | WASM component adapter runtime (feature-gated) | -| `error` | Unified error type for the library | -| `formats/` | Format-specific adapters: `generic` (YAML), `stpa` (STPA YAML) | +| `bazel` | MODULE.bazel parsing for build-system-aware cross-repo discovery| +| `mcp` | Model Context Protocol server primitives | +| `export` | HTML / Zola static-site export | +| `embed` | `{{stats:}}` / `{{coverage:}}` embed expansion in doc bodies | +| `verus_specs` | Verus specification scaffolding for formal verification | +| `proofs` | Proof-artifact tracking (Kani / Verus / Rocq) | +| `formats/` | Format-specific adapters: `generic` (YAML), `aadl`, `needs_json`| +| `error` | Unified error type for the library | + +This is a representative grouping, not an exhaustive file list; smaller support modules +(`agent_pipelines`, `test_scanner`, `runs`, `templates`, `migrate`, `bundle`, …) round +out the crate. ### 2.2 rivet-cli Modules -| Module | Purpose | -|---------|----------------------------------------------------------------------| -| `main` | CLI entry point, clap argument parsing, subcommand dispatch | -| `serve` | axum HTTP server with HTMX-rendered dashboard pages | +| Module | Purpose | +|-----------------|---------------------------------------------------------------| +| `main` | CLI entry point, clap argument parsing, subcommand dispatch | +| `check` | Oracle subcommands (`rivet check ...`) — see `docs/oracles.md`| +| `close_gaps` | `rivet close-gaps` gap-closure driver | +| `docs` | `rivet docs ` / `rivet docs check` documentation tools | +| `mcp` | `rivet mcp` Model Context Protocol server entry point | +| `serve` | axum HTTP server with HTMX-rendered dashboard pages | +| `render` | Server-side HTML rendering helpers for `serve` | +| `schema_cmd` | `rivet schema` inspection subcommands | +| `pipelines_cmd` | `rivet pipelines` agent-pipeline runner | +| `runs_cmd` | `rivet runs` evidence-run management | +| `migrate_cmd` | `rivet schema migrate` schema-migration subcommand | +| `templates_cmd` | `rivet templates` scaffold subcommand | ## 3. Data Flow @@ -147,17 +213,24 @@ This design is specified by [[REQ-010]] and [[DD-003]]. ### 3.2 Adapter Pipeline Adapters implement the `Adapter` trait, which defines `import()` and -`export()` methods. Three native adapters exist: +`export()` methods. The native adapters under `rivet-core/src/formats/` are: -1. **GenericYamlAdapter** -- Canonical YAML format with explicit type, links +1. **GenericYamlAdapter** (`generic`) -- Canonical YAML format with explicit type, links array, and fields map. Used for Rivet's own artifacts. -2. **StpaYamlAdapter** -- Imports STPA analysis artifacts from the meld - project's YAML format (losses, hazards, UCAs, etc.). -3. **ReqIfAdapter** -- Import/export for OMG ReqIF 1.2 XML, enabling - interchange with DOORS, Polarion, and codebeamer ([[REQ-005]]). +2. **AadlAdapter** (`aadl`) -- Imports AADL architecture models (components, flows, + analysis results) for the architecture-level traceability bridge. +3. **NeedsJsonAdapter** (`needs_json`) -- Imports sphinx-needs `needs.json` exports with + configurable type mapping ([[REQ-025]]). -The WASM adapter runtime ([[DD-004]]) and OSLC sync adapter ([[DD-001]]) -extend this pipeline for plugin formats and remote tool synchronization. +Two further adapters live alongside the format adapters: the **ReqIfAdapter** +(`rivet-core/src/reqif.rs`) for OMG ReqIF 1.2 XML import/export ([[REQ-005]], enabling +interchange with DOORS, Polarion, and codebeamer), and the WASM adapter runtime +([[DD-004]]) for plugin formats. + +The `oslc` module ([[DD-001]]) is a client *library* for OSLC discovery, query, and CRUD; +it is **not** wired to a `rivet` CLI subcommand. OSLC has no command-line +surface today — neither an `oslc` subcommand nor an `--oslc` flag on +`sync` — see §7. ```aadl root: RivetAdapters::WasmRuntime.Impl @@ -266,13 +339,21 @@ traceability-rules: ### 5.2 Available Schemas -| Schema | Types | Link Types | Rules | Domain | -|-----------------|-------|------------|-------|--------------------------------| -| `common` | 0 | 9 | 0 | Base fields and link types | -| `dev` | 3 | 1 | 2 | Development tracking | -| `stpa` | 10 | 5 | 7 | STPA safety analysis | -| `aspice` | 14 | 2 | 10 | ASPICE v4.0 V-model | -| `cybersecurity` | 10 | 2 | 10 | SEC.1-4, ISO/SAE 21434 | +The `schemas/` directory ships a catalogue of domain schemas — STPA, ASPICE 4.0, +ISO 26262, ISO/PAS 8800, SOTIF, DO-178C, EN 50128, IEC 61508, IEC 62304, the EU AI Act, +GSN safety cases, AADL, Eclipse SCORE, supply-chain — plus a set of *bridge* schemas that +declare cross-domain link types. The structurally distinct examples are summarised below; +the canonical inventory is `docs/schemas.md` and, ultimately, `schemas/*.yaml` itself. + +| Schema | Domain | +|-----------------|--------------------------------| +| `common` | Base fields and link types | +| `dev` | Development tracking | +| `stpa` | STPA safety analysis | +| `aspice` | ASPICE v4.0 V-model | +| `cybersecurity` | SEC.1-4, ISO/SAE 21434 | + +See `docs/schemas.md` for the full domain and bridge-schema catalogue. ### 5.3 Merge Semantics @@ -298,7 +379,10 @@ dashboard alongside artifacts they verify. This architecture reflects the following key decisions: -- [[DD-001]] -- OSLC over per-tool REST adapters for external tool sync +- [[DD-001]] -- OSLC over per-tool REST adapters for external tool sync. The decision + stands and the `oslc` client library exists, but OSLC is **not** exposed as a CLI + subcommand in the current release; `rivet --help` has no `oslc` entry. See + `docs/plans/2026-03-16-oslc-analysis.md` for the deliberate non-shipping rationale. - [[DD-002]] -- petgraph for link graph operations - [[DD-003]] -- Mergeable YAML schemas for domain composition - [[DD-004]] -- WIT-based WASM adapter interface for plugins diff --git a/docs/feature-model-bindings.md b/docs/feature-model-bindings.md index 3e691bd..a7b197c 100644 --- a/docs/feature-model-bindings.md +++ b/docs/feature-model-bindings.md @@ -1,3 +1,11 @@ +--- +id: DOC-FEATURE-MODEL-BINDINGS +title: Feature Binding File Format +type: reference +status: current +tags: [reference, variant, feature-model, bindings] +--- + # Feature Binding File Format A binding file maps features from the feature model to the artifacts and diff --git a/docs/feature-model-schema.md b/docs/feature-model-schema.md index 43f9bde..1610cfe 100644 --- a/docs/feature-model-schema.md +++ b/docs/feature-model-schema.md @@ -1,3 +1,11 @@ +--- +id: DOC-FEATURE-MODEL-SCHEMA +title: Feature Model Schema +type: reference +status: current +tags: [reference, variant, feature-model, schema] +--- + # Feature Model Schema This document is the reference for `rivet variant` YAML files: diff --git a/docs/getting-started.md b/docs/getting-started.md index b610e04..40ed1e8 100644 --- a/docs/getting-started.md +++ b/docs/getting-started.md @@ -1,3 +1,11 @@ +--- +id: DOC-GETTING-STARTED +title: Getting Started +type: guide +status: current +tags: [guide, onboarding, cli, getting-started] +--- + # Getting Started Rivet is a schema-driven SDLC artifact manager for safety-critical systems. It keeps @@ -1117,7 +1125,8 @@ rivet export --format zola --output ./site --prefix rivet \ Generated structure: - `content//artifacts/*.md` — one page per artifact with TOML frontmatter -- `content//docs/*.md` — documents with resolved `[[ID]]` wiki-links +- `content//docs/*.md` — documents with their wiki-style + artifact cross-links resolved to page links - `data//artifacts.json` — full data for `load_data()` - `data//validation.json` — PASS/FAIL status for export freshness diff --git a/docs/intro-talk-onepager.md b/docs/intro-talk-onepager.md index 7cb9ad4..4ea35cd 100644 --- a/docs/intro-talk-onepager.md +++ b/docs/intro-talk-onepager.md @@ -1,3 +1,11 @@ +--- +id: DOC-INTRO-ONEPAGER +title: rivet — one-page introduction +type: positioning +status: current +tags: [positioning, pitch, marketing, onepager] +--- + # rivet — one-page introduction *Hand this to someone on the way to lunch. Read in 90 seconds. Try the demo in 5 minutes.* diff --git a/docs/intro-talk-template.md b/docs/intro-talk-template.md index c9e740a..16ed7d2 100644 --- a/docs/intro-talk-template.md +++ b/docs/intro-talk-template.md @@ -1,3 +1,11 @@ +--- +id: DOC-INTRO-TEMPLATE +title: Introducing rivet — presenter template +type: positioning +status: current +tags: [positioning, pitch, marketing, talk-template] +--- + # Introducing rivet — presenter template A reusable structure for introducing rivet in 5, 15, or 30 minutes. Pick the audience variant in §1, the demo depth in §3, and the close in §5. Hook and thesis (§2) are the same for all audiences — that's the canonical message. diff --git a/docs/mutation-testing.md b/docs/mutation-testing.md index 6a6983a..534a823 100644 --- a/docs/mutation-testing.md +++ b/docs/mutation-testing.md @@ -1,3 +1,11 @@ +--- +id: DOC-MUTATION-TESTING +title: Mutation testing with cargo-mutants +type: guide +status: current +tags: [guide, testing, mutation, cargo-mutants] +--- + # Mutation testing with cargo-mutants This document captures rivet's mutation-testing pattern so other diff --git a/docs/oracles.md b/docs/oracles.md index 1dc08e9..a9f445d 100644 --- a/docs/oracles.md +++ b/docs/oracles.md @@ -1,3 +1,11 @@ +--- +id: DOC-ORACLES +title: "Oracles — rivet check" +type: reference +status: current +tags: [oracles, check, ci, agent-pipelines, reference] +--- + # Oracles — `rivet check ...` Oracles are reusable, mechanical checks that either pass (exit 0) or fire @@ -5,10 +13,21 @@ Oracles are reusable, mechanical checks that either pass (exit 0) or fire pipeline declared in a schema's `agent-pipelines:` block can gate a step on a single oracle's outcome. -This document lists the oracle catalog shipped in v0.4.3 and their JSON +This document lists the oracle catalog shipped in v0.10.1 and their JSON output schemas. The JSON shape is the contract pipelines consume — downstream tools must not re-parse text output. +The catalog currently has five oracles, all variants of the `CheckAction` +enum in `rivet-cli/src/main.rs`: + +| Oracle | Purpose | +|--------------------|---------------------------------------------------------------| +| `bidirectional` | Every forward link with a declared inverse has its inverse | +| `review-signoff` | A `released` artifact has a reviewer distinct from the author | +| `gaps-json` | Canonical JSON gap summary grouped by artifact | +| `sources` | `cited-source` hash status (match / drift / missing / stale) | +| `ai-defects-open` | Block release on open `ai-found-defect` against shipped work | + ## General contract All oracles accept a `--format {text|json}` flag. JSON is emitted on @@ -23,7 +42,7 @@ Exit codes: - `1` — oracle fires (one or more violations). - `2` — invocation error (unknown artifact, invalid format, etc.). -All three oracles in this catalog live under the `rivet check ...` +All oracles in this catalog live under the `rivet check ...` subcommand namespace. ## 1. `rivet check bidirectional` @@ -151,6 +170,53 @@ rivet check gaps-json [--baseline NAME] [--format json|text] - Exit code reflects `by_severity.error`: oracle fires iff `error > 0`. Warnings and infos are reported in the JSON but do not fail the gate. +## 4. `rivet check sources` + +Walks every artifact carrying a `cited-source` and classifies its hash +status against the referenced file. The cited-source mechanism is how +Rivet detects drift between an artifact and an upstream document it cites. + +``` +rivet check sources [--update [--apply]] [--strict] [--format text|json] +``` + +Three modes: + +- **default** — read-only listing of every cited-source and its status. +- **`--update`** — refresh `sha256` + `last-checked` stamps; prompts + per-artifact unless paired with `--apply` for a non-interactive batch. +- **`--strict`** — read-only audit gate. Walks every cited-source and + exits non-zero if anything has drifted, is missing a hash, is stale + (`last-checked` older than 30 days or absent), or could not be read. + Mutually exclusive with `--update`. + +Status values: `match`, `drift`, `missing-hash`, `read-error`, +`skipped-remote`, `stale`. Phase 1 handles `kind: file` cited sources; +remote sources are skipped (see `rivet docs schema-cited-sources`). + +In CI, `rivet check sources --strict` is the gate; `--update --apply` is +the (separate) fix invocation. + +## 5. `rivet check ai-defects-open` + +Blocks a release when an unresolved AI-found defect still attaches to +shipped work. The oracle fires if any `ai-found-defect` with +`triage-status: open` links to an artifact whose `status` is `released` +or `approved`, **or** if a defect's `triaged-by` equals the originating +session's `invoker` (a DPO segregation-of-duties violation — the same +person cannot both author and clear the defect). + +``` +rivet check ai-defects-open [--format text|json] +``` + +This oracle is the operational primitive for the Tool Confidence Level +(TCL) workstream. The tool-qualification dossier +(`docs/design/tool-qualification-dossier.md`, also `rivet docs +tool-qualification`) cites this loop as the TD1 detection layer that +compensates for eroded human review when the upstream author is an AI +assistant. Without this gate the TD1 claim has no mechanical backing. + ## Pipeline wiring An agent pipeline step in a schema declares which oracles must pass before @@ -166,6 +232,10 @@ agent-pipelines: oracles: [review-signoff] - id: collect-gaps oracles: [gaps-json] + - id: verify-cited-sources + oracles: [sources] + - id: block-open-ai-defects + oracles: [ai-defects-open] ``` The runner exec's `rivet check ` with `--format json` and captures diff --git a/docs/pre-commit.md b/docs/pre-commit.md index c34c6b5..87d653f 100644 --- a/docs/pre-commit.md +++ b/docs/pre-commit.md @@ -1,3 +1,11 @@ +--- +id: DOC-PRE-COMMIT +title: Canonical pre-commit configuration +type: reference +status: current +tags: [reference, pre-commit, ci, hooks] +--- + # Canonical pre-commit configuration Rivet ships a 21-hook `.pre-commit-config.yaml` that is the reference for diff --git a/docs/pure-variants-comparison.md b/docs/pure-variants-comparison.md index 01892cc..f27cdf2 100644 --- a/docs/pure-variants-comparison.md +++ b/docs/pure-variants-comparison.md @@ -1,3 +1,11 @@ +--- +id: DOC-PURE-VARIANTS-COMPARISON +title: Rivet vs pure::variants — Feature Framework Comparison +type: research +status: snapshot +tags: [research, comparison, variant, feature-model, pure-variants] +--- + # Rivet vs pure::variants — Feature Framework Comparison Status: research report, v0.4.3 baseline. diff --git a/docs/rivet-is-not.md b/docs/rivet-is-not.md index 9918b4c..7d21d1c 100644 --- a/docs/rivet-is-not.md +++ b/docs/rivet-is-not.md @@ -63,6 +63,76 @@ The recording-without-performing failure mode is common in practice for a struct *Cliff:* an agent runs `rivet sync` against a supplier repo that ships ReqIF rather than rivet YAML. The sync succeeds because the externals graph is satisfied at the directory level; the supplier's artifacts are unreadable; coverage silently reports the boundary as `UNCOVERED` rather than `EXTERNAL_BOUNDARY`. The matrix looks like the supplier never delivered. +## 7a. Cross-org Assumptions of Use + +Section 7 names the boundary. This section makes the boundary operational. The cross-git +investigation (`docs/research/cross-git-repo-investigation.md` §4.2) enumerated eleven +findings — referred to below as **F1** through **F11** — about Rivet's behaviour when an +integrator links to an external, separately-owned repository. Every finding that the +integrator must compensate for becomes an explicit **Assumption of Use (AoU)**: a thing +Rivet does *not* do, that the integrator is therefore obliged to do instead. + +This is the SEooC discipline applied across an organisational boundary. A "green" cross-org +PASS is only meaningful if these assumptions hold. If they do not, the green PASS is the +Cederqvist function-signature example transposed across an org boundary: each repository +is internally consistent, the cross-repo link is well-formed, and the defect lives in the +gap between the two repositories that no single `rivet validate` run can see. A worked +CI register implementing this whole list inline is hosted in the `rivet docs +cross-repo-ci` topic (see [[REQ-071]]). + +The register below is **additive** to §7; it does not replace the prose above it. Each +assumption cites the source finding (an F-number) it compensates for. + +**AoU-X1 — Validate every linked external. (Source finding: F6.)** +The integrator runs `rivet validate --strict-cited-sources --fail-on warning` (and, once +it lands, the parse-error-as-error fix) on *every* linked external repository as part of +CI. Rivet does not propagate a supplier's validation state to the consumer. The absence of +a `cross_repo_diagnostics` block in the consumer's JSON output is silence, not +confirmation that the supplier validated cleanly. F6 is the finding that `rivet validate` +on the consumer does not surface the supplier's diagnostics. + +**AoU-X2 — Treat supplier-pull as fetch, never as authorisation. (Source finding: F8.)** +The integrator treats `rivet supplier pull` as a fetch operation and never as an +authorisation or sign-off. Pull silently overwrites the local cache when the supplier's +bytes change and exits 0 with no drift header. CI must follow every `pull` with +`rivet validate --strict-cited-sources` and gate on the exit code. F8 is the finding that +`rivet supplier pull` overwrites on drift with exit 0 and no header. + +**AoU-X3 — Re-derive cross-org diagnostics from the supplier's own JSON. (Source finding: F6.)** +The integrator re-derives every cross-org diagnostic count from the supplier's own +`rivet validate --format json` output, never from the consumer's `cross_repo_*` counters. +Those counters are wired but currently report zero against silence; they will report +real supplier diagnostics only once F6 is closed, and not before. + +**AoU-X4 — Bisect cross-org regressions in the supplier's repository. (Source finding: F9.)** +The integrator bisects cross-org regressions inside the supplier's repository, using +`git bisect run` or the supplier's own tooling. Rivet does not bisect across repository +boundaries. `rivet impact --baseline ` may identify the artifact set +that changed, but cross-repo attribution is human work. F9 is the finding that there is no +bisect support and no documented cross-repo bisect workflow. + +**AoU-X5 — Do not use the broken safety-critical presets. (Source finding: F1.)** +The integrator does not use `rivet init --preset {do-178c, en-50128, iec-61508, +iec-62304}` for new compliance work until that defect is closed. The four safety-critical +presets currently emit a `rivet.yaml` referencing a schema the binary cannot resolve; the +error surfaces only on the next `rivet validate`. Use `--preset dev` and write the schema +by hand, or wait. F1 is the finding that `rivet init --preset {…}` silently creates a +broken project; this AoU becomes obsolete once [[REQ-063]] closes it. + +**AoU-X6 — Keep using git submodule / repo for repository lifecycle. (Source findings: F7, F9.)** +The integrator continues to use `git submodule`, `git subrepo`, or Google's `repo` tool +for repository lifecycle — clones, fetches, branch state, merge conflicts in pinned SHAs. +Rivet's diagnostics layer (`external-anchor`, `cited-source`) is layered *on top of* the +repository lifecycle, not in place of it. F7 and F9 together establish that Rivet ships no +repository lifecycle management and no cross-boundary bisect or blame. + +**AoU-X7 — Pick exactly one cross-repo mechanism per link. (Source finding: F7.)** +The integrator picks exactly one of the two cross-repo mechanisms — `externals:` or +`external-anchor` — for each cross-link, and documents the choice. Both ship in v0.10.1 +with no documented relationship between them; F7 is the finding that names these two +parallel systems. Until that consolidation is committed under [[DD-067]], the integrator +must own the choice locally and consistently. + ## 8. Rivet is not an AI provenance verifier. When an agent stamps an artifact with `provenance.created-by: ai-assisted` and `model: claude-opus-4-7`, Rivet records the self-report. It does not independently confirm that an Anthropic model was actually invoked, that the cited model version was the one used, or that the `ai-session` block honestly reflects the prompt and the response. The `rivet stamp` command writes what it is told to write. The pre-commit hook that auto-stamps on edits runs in the same trust boundary as the agent doing the editing. Provenance is a recording mechanism, not a forensic one — useful exactly to the degree that the surrounding process (signed commits, CI-side re-validation, the human reviewing the PR) is trusted. diff --git a/docs/schemas.md b/docs/schemas.md index e718506..1871511 100644 --- a/docs/schemas.md +++ b/docs/schemas.md @@ -1,3 +1,11 @@ +--- +id: DOC-SCHEMA-REFERENCE +title: Schema Reference +type: reference +status: current +tags: [schema, reference, stpa, aspice, cybersecurity, safety] +--- + # Schema Reference Rivet schemas are YAML files that define artifact types, link types, field constraints, @@ -8,15 +16,66 @@ loads `common` plus one or more domain schemas. ## Available Schemas -| Schema | Version | Types | Rules | Domain | -|-----------------|---------|-------|-------|---------------------------------------| -| `common` | 0.1.0 | -- | -- | Base fields and link types | -| `dev` | 0.1.0 | 3 | 2 | Software development tracking | -| `stpa` | 0.1.0 | 10 | 7 | STPA safety analysis | -| `aspice` | 0.2.0 | 14 | 10 | Automotive SPICE V-model | -| `cybersecurity` | 0.1.0 | 10 | 10 | Cybersecurity (SEC.1-4 / ISO 21434) | - -Schemas are located in `schemas/` relative to the project directory. +The `schemas/` directory ships a catalogue of domain schemas covering systems-safety +analysis, automotive process models, functional-safety standards, AI-safety regulation, +and supply-chain compliance, plus a set of *bridge* schemas that wire those domains +together. Every domain schema implicitly extends `common`. + +This catalogue grows; the table below is a snapshot, not a hard contract. The +authoritative inventory is whatever `schemas/*.yaml` actually contains in the repository +you are using -- run `ls schemas/` to see it. + +### Domain schemas + + +As of this writing the directory ships 21 domain schemas and 8 bridge schemas. + +| Schema | Domain | +|------------------|-------------------------------------------------------------------| +| `common` | Base fields and link types shared by every schema | +| `dev` | Lightweight software-development tracking (Rivet dogfoods this) | +| `stpa` | STPA systems-theoretic process analysis (the full four-step model)| +| `stpa-sec` | STPA-Sec security-extended analysis | +| `stpa-ai` | STPA-for-AI: ML-lifecycle hazards, data provenance, retraining | +| `aspice` | Automotive SPICE PAM v4.0 V-model | +| `cybersecurity` | Automotive cybersecurity (SEC.1-4 / ISO/SAE 21434) | +| `iso-26262` | ISO 26262 road-vehicle functional safety | +| `iso-pas-8800` | ISO/PAS 8800 AI safety lifecycle for road vehicles | +| `sotif` | ISO 21448 Safety Of The Intended Functionality | +| `do-178c` | DO-178C airborne software certification | +| `en-50128` | EN 50128 railway software safety | +| `iec-61508` | IEC 61508 functional safety of E/E/PE systems | +| `iec-62304` | IEC 62304 medical-device software lifecycle | +| `eu-ai-act` | EU AI Act technical documentation for high-risk AI systems | +| `safety-case` | GSN (Goal Structuring Notation) structured safety arguments | +| `aadl` | AADL architecture components, flows, and analysis results | +| `score` | Eclipse SCORE metamodel (ASIL-rated open-source software) | +| `supply-chain` | Software supply chain: SBOM, build provenance, CVE tracking | +| `vv-coverage` | Repo-level V&V technique coverage matrix | +| `research` | Market research, competitive analysis, patent landscape | + +### Bridge schemas + +Bridge schemas declare cross-domain link types so artifacts modelled under one schema can +trace into another (e.g. an STPA hazard linked to an ISO 26262 safety goal). Load a bridge +schema alongside both of the domain schemas it connects. + +| Bridge schema | Connects | +|-----------------------------------|--------------------------------------------| +| `stpa-dev.bridge` | STPA analysis <-> `dev` requirements | +| `iso-8800-stpa.bridge` | ISO/PAS 8800 <-> STPA | +| `sotif-stpa.bridge` | SOTIF <-> STPA | +| `eu-ai-act-stpa.bridge` | EU AI Act <-> STPA | +| `eu-ai-act-aspice.bridge` | EU AI Act <-> ASPICE | +| `safety-case-stpa.bridge` | GSN safety case <-> STPA | +| `safety-case-eu-ai-act.bridge` | GSN safety case <-> EU AI Act | +| `supply-chain-dev.bridge` | Supply chain <-> `dev` requirements | + +Schemas are located in `schemas/` relative to the project directory. The remaining +sections of this document describe the structurally distinct schemas (`common`, `dev`, +`stpa`, `aspice`, `cybersecurity`) field-by-field; the standards-aligned schemas above +follow the same shape and are best read directly from their YAML, which carries an +extensive header comment per file. --- diff --git a/rivet-cli/src/docs.rs b/rivet-cli/src/docs.rs index 0206a31..c543828 100644 --- a/rivet-cli/src/docs.rs +++ b/rivet-cli/src/docs.rs @@ -115,6 +115,12 @@ const TOPICS: &[DocTopic] = &[ category: "Reference", content: CROSS_REPO_DOC, }, + DocTopic { + slug: "cross-repo-ci", + title: "Cross-repo CI — sync, pull, validate in a pipeline", + category: "Reference", + content: CROSS_REPO_CI_DOC, + }, DocTopic { slug: "mutation", title: "CLI mutation commands (add, modify, remove, link, unlink)", @@ -1103,6 +1109,28 @@ Rivet supports linking artifacts across multiple git repositories using a mesh topology. Any rivet project can declare dependencies on other rivet projects and reference their artifacts using prefixed IDs. +## Two cross-repo mechanisms — pick one per link + +Rivet ships **two** distinct cross-repo mechanisms. They are not +interchangeable; choose per cross-link and document the choice +(AoU-X7 in docs/rivet-is-not.md). + +| | `externals:` (this topic) | `external-anchor` + `cited-source` | +|---|---|---| +| Source | another **rivet** git repo | any file — incl. ReqIF, PDF, OSLC export | +| Pin | git commit SHA (`rivet lock`) | sha256 of the file's bytes | +| Reference | prefixed ID — `prefix:ID` | structured `derives-from-external` target / an `external-anchor` artifact | +| Fetch | `rivet sync` clones into `.rivet/repos/` | `rivet supplier pull` into `.rivet/supplier-cache/` | +| Use when | the supplier is a rivet project and you want to navigate its whole artifact graph | the supplier is non-rivet, or only a single delivered document needs anchoring | + +`externals:` is git-aware federated traceability. `external-anchor` is +git-agnostic, content-hash-pinned traceability — see +`rivet docs schema-cited-sources` and `rivet docs cross-repo-ci`. The +question of whether one mechanism should eventually subsume the other +is the open architectural decision **DD-067** +(`artifacts/cross-git-investigation.yaml`); until it is decided, both +ship and the integrator owns the per-link choice. + ## Configuration Declare external dependencies in `rivet.yaml`: @@ -1202,6 +1230,71 @@ Repos participate in baselines by tagging: `git tag baseline/v1.0` - **DD-017**: Transitive dependency resolution — declare direct deps only "#; +const CROSS_REPO_CI_DOC: &str = r#"# Cross-repo CI + +How to gate a multi-repo Rivet workspace in CI. Single-repo CI is +covered by `rivet docs commit-traceability`; this topic is the +cross-repo sequence and the obligations it does NOT discharge for you. + +## The recommended sequence + +For a consumer project that links one or more externals, run, in order, +failing the job on any non-zero exit: + +```bash +rivet sync # 1. fetch externals into .rivet/repos/ +rivet supplier pull [ ...] # 2. fetch each external-anchor's cited-source +rivet validate --strict-cited-sources --fail-on warning + # 3. gate: drift is an error, warnings fail +rivet validate --with-externals-validate # 4. surface the SUPPLIER's own diagnostics +``` + +Step 3 catches `cited-source` drift and tightens the local gate. Step 4 +is the cross-repo diagnostic propagation (REQ-065): without it the +consumer's PASS says nothing about whether the supplier's own artifacts +are sound. + +## Worked GitHub Actions example + +```yaml +name: rivet-cross-repo +on: [push, pull_request] +jobs: + validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install rivet + run: cargo install --path rivet-cli + - name: Sync externals + run: rivet sync + - name: Pull supplier anchors + run: | + for a in $(rivet supplier list --format json | jq -r '.anchors[].id'); do + rivet supplier pull "$a" + done + - name: Validate (gate) + run: rivet validate --strict-cited-sources --fail-on warning + - name: Surface supplier diagnostics (non-gating) + run: rivet validate --with-externals-validate || true +``` + +## What this does NOT do — the integrator's Assumptions of Use + +CI running the sequence above does not discharge the cross-org +obligations enumerated in `docs/rivet-is-not.md` §7a. In brief: + +- **AoU-X1** — you must run validate in every linked external; absence + of `cross_repo_diagnostics` is silence, not confirmation. +- **AoU-X2** — `rivet supplier pull` is a fetch, not an authorisation; + always follow it with `validate --strict-cited-sources`. +- **AoU-X4** — Rivet does not bisect across repo boundaries; attribute + cross-repo regressions in the supplier's repo yourself. + +Read `docs/rivet-is-not.md` §7a for the full register before relying on +a green cross-repo CI run. +"#; + const STPA_DOC: &str = concat!( include_str!("../../schemas/stpa.yaml"), r#" diff --git a/rivet-cli/src/main.rs b/rivet-cli/src/main.rs index 2d8689b..5100d1b 100644 --- a/rivet-cli/src/main.rs +++ b/rivet-cli/src/main.rs @@ -362,6 +362,17 @@ enum Command { /// `--strict-cited-sources`. #[arg(long = "strict-orphans")] strict_orphans: bool, + + /// Also run `rivet validate` inside every linked external + /// project and surface its diagnostics under a + /// `cross_repo_diagnostics` array (JSON) / a "Cross-repo + /// diagnostics" section (text). Off by default: the consumer's + /// PASS does not otherwise reflect the supplier's validation + /// state (REQ-065 / AoU-X1). Independent of + /// `--skip-external-validation`, which governs cross-*ref* + /// checking. + #[arg(long = "with-externals-validate")] + with_externals_validate: bool, }, /// Show a single artifact by ID @@ -1873,6 +1884,7 @@ fn run(cli: Cli) -> Result { strict_cited_source_stale, check_remote_sources, strict_orphans, + with_externals_validate, } => cmd_validate( &cli, format, @@ -1889,6 +1901,7 @@ fn run(cli: Cli) -> Result { *strict_cited_source_stale, *check_remote_sources, *strict_orphans, + *with_externals_validate, ), Command::List { r#type, @@ -4553,6 +4566,7 @@ fn cmd_validate( strict_cited_source_stale: bool, check_remote_sources: bool, strict_orphans: bool, + with_externals_validate: bool, ) -> Result { validate_format(format, &["text", "json"])?; let fail_on_threshold = parse_fail_on(fail_on)?; @@ -5002,6 +5016,40 @@ fn cmd_validate( } } + // REQ-065 / AoU-X1: opt-in cross-repo diagnostic propagation. By + // default the consumer's validate says nothing about the supplier's + // own validation state — `--with-externals-validate` runs validate + // inside each linked external and surfaces its diagnostics here. + // Independent of `--skip-external-validation` (which governs + // cross-*ref* checking, not the supplier's internal state). + let mut cross_repo_diagnostics: Vec<(String, validate::Diagnostic)> = Vec::new(); + if with_externals_validate { + if let Some(ref externals) = config.externals { + if !externals.is_empty() { + match rivet_core::externals::load_all_externals(externals, &cli.project) { + Ok(resolved) => { + for ext in &resolved { + let Some(ref ext_schema) = ext.schema else { + continue; + }; + let mut ext_store = Store::new(); + for a in &ext.artifacts { + ext_store.upsert(a.clone()); + } + let ext_graph = LinkGraph::build(&ext_store, ext_schema); + for d in validate::validate(&ext_store, ext_schema, &ext_graph) { + cross_repo_diagnostics.push((ext.prefix.clone(), d)); + } + } + } + Err(e) => eprintln!( + " warning: --with-externals-validate: could not load externals: {e}" + ), + } + } + } + } + // Lifecycle completeness check let all_artifacts: Vec<_> = store.iter().cloned().collect(); let lifecycle_gaps = @@ -5085,6 +5133,18 @@ fn cmd_validate( }) }) .collect(); + let cross_repo_diagnostics_json: Vec = cross_repo_diagnostics + .iter() + .map(|(prefix, d)| { + serde_json::json!({ + "source_project": prefix, + "source_artifact_id": d.artifact_id, + "severity": format!("{:?}", d.severity).to_lowercase(), + "rule": d.rule, + "message": d.message, + }) + }) + .collect(); let total_errors = errors + cross_errors; let result_str = if total_errors > 0 { "FAIL" } else { "PASS" }; let mut output = serde_json::json!({ @@ -5104,6 +5164,7 @@ fn cmd_validate( "circular_dependencies": cycles_json, "version_conflict_details": conflicts_json, "lifecycle_coverage": lifecycle_json, + "cross_repo_diagnostics": cross_repo_diagnostics_json, }); if let Some((ref vname, bound_count)) = variant_scope_name { output["variant"] = serde_json::json!({ @@ -5182,6 +5243,24 @@ fn cmd_validate( } } + if with_externals_validate && !cross_repo_diagnostics.is_empty() { + println!(); + println!( + "Cross-repo diagnostics ({}) — from --with-externals-validate; \ + these are the SUPPLIER's, not gating this run:", + cross_repo_diagnostics.len() + ); + for (prefix, d) in &cross_repo_diagnostics { + eprintln!( + " [{}] {}: [{}] {}", + prefix, + format!("{:?}", d.severity).to_uppercase(), + d.artifact_id.as_deref().unwrap_or("-"), + d.message, + ); + } + } + println!(); let total_errors = errors + cross_errors; if total_errors > 0 { @@ -8215,6 +8294,7 @@ fn cmd_diff( strict_cited_source_stale: false, check_remote_sources: false, strict_orphans: false, + with_externals_validate: false, }, }; let head_cli = Cli { @@ -8237,6 +8317,7 @@ fn cmd_diff( strict_cited_source_stale: false, check_remote_sources: false, strict_orphans: false, + with_externals_validate: false, }, }; let bc = ProjectContext::load(&base_cli)?; diff --git a/rivet-cli/tests/cli_commands.rs b/rivet-cli/tests/cli_commands.rs index 5fce876..eaf0790 100644 --- a/rivet-cli/tests/cli_commands.rs +++ b/rivet-cli/tests/cli_commands.rs @@ -880,6 +880,144 @@ fn validate_reports_orphans_as_warnings() { ); } +/// REQ-065 / AoU-X1: by default the consumer's `rivet validate` says +/// nothing about a linked external's own validation state — the +/// `cross_repo_diagnostics` array is empty. `--with-externals-validate` +/// runs validate inside each external and surfaces its diagnostics. +/// +/// rivet: verifies REQ-065 +#[test] +fn validate_with_externals_validate_surfaces_supplier_diagnostics() { + let root = tempfile::tempdir().expect("temp dir"); + let supplier = root.path().join("supplier"); + let consumer = root.path().join("consumer"); + std::fs::create_dir_all(&supplier).unwrap(); + std::fs::create_dir_all(&consumer).unwrap(); + + // Supplier: a dev project with two deliberately invalid-priority reqs. + let init_s = Command::new(rivet_bin()) + .args([ + "init", + "--preset", + "dev", + "--dir", + supplier.to_str().unwrap(), + ]) + .output() + .expect("init supplier"); + assert!( + init_s.status.success(), + "init supplier: {}", + String::from_utf8_lossy(&init_s.stderr) + ); + std::fs::write( + supplier.join("artifacts").join("reqs.yaml"), + "artifacts:\n\ + \x20 - id: REQ-S1\n type: requirement\n title: Bad priority one\n\ + \x20 status: approved\n fields: {priority: NotValid, category: functional}\n\ + \x20 - id: REQ-S2\n type: requirement\n title: Bad priority two\n\ + \x20 status: approved\n fields: {priority: AlsoBad, category: functional}\n", + ) + .expect("write supplier reqs"); + + // Consumer: links the supplier as a path external. + let init_c = Command::new(rivet_bin()) + .args([ + "init", + "--preset", + "dev", + "--dir", + consumer.to_str().unwrap(), + ]) + .output() + .expect("init consumer"); + assert!( + init_c.status.success(), + "init consumer: {}", + String::from_utf8_lossy(&init_c.stderr) + ); + std::fs::write( + consumer.join("rivet.yaml"), + format!( + "project:\n name: consumer\n version: \"0.1.0\"\n schemas: [common, dev]\n\ + externals:\n sup:\n path: {}\n prefix: sup\n\ + sources:\n - path: artifacts\n format: generic-yaml\n", + supplier.display() + ), + ) + .expect("write consumer rivet.yaml"); + let sync = Command::new(rivet_bin()) + .args(["--project", consumer.to_str().unwrap(), "sync"]) + .output() + .expect("rivet sync"); + assert!( + sync.status.success(), + "sync: {}", + String::from_utf8_lossy(&sync.stderr) + ); + + // Default validate — cross_repo_diagnostics MUST be empty. + let plain = Command::new(rivet_bin()) + .args([ + "--project", + consumer.to_str().unwrap(), + "validate", + "--format", + "json", + ]) + .output() + .expect("validate plain"); + let plain_json: serde_json::Value = + serde_json::from_slice(&plain.stdout).expect("plain validate JSON"); + assert_eq!( + plain_json + .get("cross_repo_diagnostics") + .and_then(|v| v.as_array()) + .map(|a| a.len()), + Some(0), + "default validate must not surface external diagnostics; got: {plain_json}" + ); + + // --with-externals-validate — MUST surface the supplier's warnings. + let withv = Command::new(rivet_bin()) + .args([ + "--project", + consumer.to_str().unwrap(), + "validate", + "--with-externals-validate", + "--format", + "json", + ]) + .output() + .expect("validate --with-externals-validate"); + let withv_json: serde_json::Value = + serde_json::from_slice(&withv.stdout).expect("validate JSON"); + let crd = withv_json + .get("cross_repo_diagnostics") + .and_then(|v| v.as_array()) + .expect("cross_repo_diagnostics array"); + assert!( + crd.len() >= 3, + "expected >= 3 supplier diagnostics; got {}: {withv_json}", + crd.len() + ); + let first = &crd[0]; + for key in [ + "source_project", + "source_artifact_id", + "severity", + "rule", + "message", + ] { + assert!(first.get(key).is_some(), "entry missing '{key}': {first}"); + } + assert_eq!( + first.get("source_project").and_then(|v| v.as_str()), + Some("sup"), + "source_project must be the external prefix; got: {first}" + ); +} + // ── rivet stats ───────────────────────────────────────────────────────── /// `rivet stats --format json` produces valid JSON with total count. diff --git a/rivet-core/src/proofs.rs b/rivet-core/src/proofs.rs index 5e2b364..a48624f 100644 --- a/rivet-core/src/proofs.rs +++ b/rivet-core/src/proofs.rs @@ -213,6 +213,8 @@ mod proofs { covered, total, uncovered_ids: vec![], + external_boundary: 0, + external_boundary_ids: vec![], }; let pct = entry.percentage(); diff --git a/tests/playwright/rendering-invariants.spec.ts b/tests/playwright/rendering-invariants.spec.ts index ff7da70..c513865 100644 --- a/tests/playwright/rendering-invariants.spec.ts +++ b/tests/playwright/rendering-invariants.spec.ts @@ -173,7 +173,12 @@ test.describe("Rendering invariants — render-shape contracts", () => { await expect(desc).toBeVisible(); // Inside that description, mermaid block was emitted as . - const mermaidPre = desc.locator("pre.mermaid"); + // ARCH-CORE-001's description carries TWO fenced mermaid blocks (a + // `flowchart LR` and a `stateDiagram-v2`); scope to `.first()` — the + // flowchart — so the strict-mode locator resolves to one element. The + // invariant under test (description-mermaid renders as ) + // holds per-block; the `.svg-viewer` count check below covers both. + const mermaidPre = desc.locator("pre.mermaid").first(); await expect(mermaidPre).toBeVisible(); // Body should contain the diagram source so mermaid.js can render it. await expect(mermaidPre).toContainText("flowchart"); diff --git a/vscode-rivet/package.json b/vscode-rivet/package.json index 6a397eb..ae37b33 100644 --- a/vscode-rivet/package.json +++ b/vscode-rivet/package.json @@ -3,7 +3,7 @@ "displayName": "Rivet SDLC", "description": "SDLC artifact traceability with live validation, hover info, and embedded dashboard", "publisher": "pulseengine", - "version": "0.10.1", + "version": "0.11.0", "license": "MIT", "repository": { "type": "git",