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
9 changes: 8 additions & 1 deletion .claude/settings.local.json
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,14 @@
"Bash(cargo tree *)",
"Bash(git restore *)",
"Bash(awk -F'\\\\t' '{print $4}')",
"Bash(awk -F'\\\\t' '{print $1, $2}')"
"Bash(awk -F'\\\\t' '{print $1, $2}')",
"Bash(gh workflow *)",
"Bash(perl -i -pe 's/\\(ci_yaml: \\(None|Some\\\\\\(ci\\\\\\)\\),\\)/$1\\\\n external_namespaces: &[],\\\\n ignore_patterns: &[],/g' rivet-core/src/doc_check.rs)",
"Bash(xargs -I{} gh api {} --jq '.jobs[] | \"\\\\\\(.conclusion // \"running\"\\): \\\\\\(.name\\)\"')",
"Bash(perl -0777 -i -pe 's/\\(TraceabilityRule \\\\{[^}]*severity: [^,}]+,\\)\\(\\\\s*\\\\}\\)/$1\\\\n alternate_backlinks: vec![],$2/g' rivet-core/src/coverage.rs rivet-core/src/export.rs rivet-core/src/proofs.rs rivet-core/src/lifecycle.rs rivet-core/src/validate.rs)",
"Bash(perl -0777 -i -pe 's/\\(TraceabilityRule \\\\{[^}]*severity: [^,}]+,\\)\\(\\\\s*\\\\}\\)/$1\\\\n alternate_backlinks: vec![],$2/g' rivet-core/tests/proptest_operations.rs)",
"Bash(node scripts/diff-to-markdown.mjs --diff /tmp/malformed.json --pr 1 --run 1 --repo x/y)",
"Bash(node -e \"require\\('typescript'\\).transpileModule\\(require\\('fs'\\).readFileSync\\('rivet-delta.spec.ts','utf8'\\), { compilerOptions: { target: 'es2022', module: 'nodenext' } }\\)\")"
]
}
}
137 changes: 137 additions & 0 deletions .github/workflows/rivet-delta.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
name: Rivet Delta

# Runs on every PR that touches artifacts, schemas, or `rivet.yaml`.
# Produces a graphical diff of the Rivet artifact graph between the PR's
# merge base and the PR head, posts a markdown summary as a PR comment,
# and uploads the full HTML dashboard + diff JSON as workflow artifacts
# for deep inspection. The comment is updated in place on subsequent
# pushes via a hidden marker (<!-- rivet-delta-bot -->).
#
# This is informational — the workflow never blocks a merge. If the diff
# can't be computed (parse errors, missing base, etc.) it posts a
# warning comment instead of failing.

on:
pull_request:
paths:
- "artifacts/**"
- "schemas/**"
- "rivet.yaml"
- "rivet-core/**"
- "rivet-cli/**"
- ".github/workflows/rivet-delta.yml"
- "scripts/diff-to-markdown.mjs"

permissions:
contents: read
pull-requests: write

concurrency:
group: rivet-delta-${{ github.event.pull_request.number }}
cancel-in-progress: true

jobs:
delta:
name: Artifact delta
runs-on: ubuntu-latest
# All user-derived fields are captured into env vars at job scope so
# no `run:` step interpolates untrusted input into a shell context.
# base.sha is a 40-char SHA (safe) but enving everything is the
# GitHub-recommended pattern for workflows that accept PR input.
env:
PR_NUMBER: ${{ github.event.pull_request.number }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
RUN_ID: ${{ github.run_id }}
REPO: ${{ github.repository }}
steps:
- name: Checkout head
uses: actions/checkout@v6
with:
path: head
fetch-depth: 0

- name: Checkout base
uses: actions/checkout@v6
with:
path: base
ref: ${{ github.event.pull_request.base.sha }}
fetch-depth: 1

- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
workspaces: head

- name: Build rivet (release)
working-directory: head
run: cargo build --release -p rivet-cli

- name: Run diff (base vs head)
working-directory: head
continue-on-error: true
run: |
set -euo pipefail
mkdir -p delta-out
./target/release/rivet diff \
--base "$GITHUB_WORKSPACE/base" \
--head "$GITHUB_WORKSPACE/head" \
--format json > delta-out/diff.json 2> delta-out/diff.stderr

- name: Run impact (since base)
working-directory: head
continue-on-error: true
run: |
set -euo pipefail
./target/release/rivet impact \
--since "$BASE_SHA" \
--depth 5 \
--format json > delta-out/impact.json 2> delta-out/impact.stderr || \
echo '{"impacts": [], "error": "impact analysis failed"}' > delta-out/impact.json

- name: Export HTML dashboard for head
working-directory: head
continue-on-error: true
run: |
set -euo pipefail
./target/release/rivet export \
--format html \
--output delta-out/html \
--version-label "pr-$PR_NUMBER" \
--offline || echo "export failed" > delta-out/export.err

- name: Generate markdown summary
id: summary
working-directory: head
run: |
set -euo pipefail
node scripts/diff-to-markdown.mjs \
--diff delta-out/diff.json \
--impact delta-out/impact.json \
--pr "$PR_NUMBER" \
--run "$RUN_ID" \
--repo "$REPO" \
> delta-out/comment.md
echo "comment_file=head/delta-out/comment.md" >> "$GITHUB_OUTPUT"

- name: Upload delta artifacts
uses: actions/upload-artifact@v4
with:
name: rivet-delta-pr-${{ github.event.pull_request.number }}
path: head/delta-out/
retention-days: 14

- name: Find previous delta comment
id: find_comment
uses: peter-evans/find-comment@v3
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: github-actions[bot]
body-includes: "<!-- rivet-delta-bot -->"

- name: Post or update delta comment
uses: peter-evans/create-or-update-comment@v4
with:
issue-number: ${{ github.event.pull_request.number }}
comment-id: ${{ steps.find_comment.outputs.comment-id }}
edit-mode: replace
body-path: ${{ steps.summary.outputs.comment_file }}
118 changes: 118 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,124 @@
<!-- AUDIT-FILE: verified 2026-04-22 — all numeric counts in this file
are historical snapshots taken at release time, not current state. -->

## [0.4.2] — 2026-04-23

<!-- rivet-docs-check: ignore SEC-AS-001 -->

This release closes 18 silent-accept findings discovered through dogfooding
plus a customer bug-hunt pass. Theme: every place where invalid input used
to silently succeed now surfaces a typed error or warning. Most are tiny
behavioural changes; the cumulative effect is a much louder pipeline.

### Correctness fixes (silent-accept antipattern)

- **Required-link cardinality silently passed on flow-style YAML** —
`links: [{type: X, target: Y}]` parsed without error but the cardinality
counter saw zero, so a "required" link could be entirely absent and
`rivet validate` still returned PASS. Same hole for the named-field form
`targets: [SEC-AS-001]` derived from a schema's `link-fields[].name`. Both
shapes now produce identical `Vec<Link>` and the cardinality counter sees
them. (issue #3)
- **Schema link-fields referencing undeclared link types** were emitted as
`Warning` from `rivet validate` (overall result still PASS) and silently
tolerated at schema load. Now `Error` with one diagnostic per
`(artifact, link-type)` pair, plus a new `Schema::validate_consistency()`
for fail-fast load-time checks. (issue #1)
- **`{{group:TYPE:FIELD}}` two-arg form** discarded the second arg, treating
the type name as the field — every artifact bucketed into `"unset"`.
- **`{{query (...) :limit 10}}`** colon-prefixed options were silently
dropped because the parser only recognised `key=value`. Now rejected with
a hint pointing to the correct syntax. New `fields=id,title,asil` option
customises columns.
- **`{{coverage:typo-rule}} / {{matrix:UnknownType:Y}} / {{diagnostics:warnings}}`**
all rendered blank or all-results when given typo'd arguments. Each now
errors with a list of valid values.
- **Standalone `{{artifact|links|table:…}}` on its own line** wrapped in
`<p>` producing invalid HTML nesting. Block-level embeds now emit
directly.
- **`#[serde(deny_unknown_fields)]`** added to every schema-author struct
(`SchemaFile`, `SchemaMetadata`, `ArtifactTypeDef`, `FieldDef`,
`LinkFieldDef`, `LinkTypeDef`, `TraceabilityRule`, `ConditionalRule`,
`MistakeGuide`, `AlternateBacklink`) plus the artifact-level `Link` and
`Provenance` structs. Typo'd YAML keys now error at load time instead of
being silently dropped. New `LinkFieldDef.description` and
`TraceabilityRule.alternate_backlinks` to surface fields the bundled
schemas were already using.
- **YAML CST parser** now handles inline `# comments` on mapping lines —
the LSP previously emitted `expected mapping key, found Some(Comment)`
on every CI workflow file. (issue #6b)
- **`rivet docs check`** now honors `rivet.yaml` `docs:` paths instead of
only scanning the top-level `docs/` directory; projects with
`crates/*/docs` or `rivet/docs` layouts no longer get silently skipped.

### LSP

- **LSP resolves workspace schemas** — was reading from the launching
process's CWD. User-extended schema files referenced via
`rivet.yaml: schemas:` now load correctly. (issue #6a)

### Dashboard / UI

- **Artifact detail page** lists the documents that `[[ID]]`-reference it
(reverse index — closes the loop on the existing forward `/doc-linkage`
view).
- **Mermaid + AADL diagrams** on artifact detail and `schema/show` pages
now wrap in `.svg-viewer` so they get the same zoom / fullscreen / popout
toolbar as graph and doc-linkage views. Parity test in
`diagram-viewer.spec.ts` pins the invariant.
- **Document headings** carry stable `id="…"` slugs so in-page TOC links
and `#anchor` URLs navigate. (B1)
- **Variants in the dashboard** are now documented in `getting-started.md`
and `what-is-rivet.md`. The auto-discovery convention, sidebar entry,
header dropdown and `/variants` overview are spelled out.

### Documentation invariants

- **External-namespace exemption** for `ArtifactIdValidity`. Three layers
to escape the `[A-Z]+-NNN`-pattern check when the prose legitimately
references external IDs (Jira, Polarion, hazard catalogs):
- `rivet.yaml: docs-check.external-namespaces: [GNV, GNR, HZO, UC]`
- `rivet.yaml: docs-check.ignore-patterns: [<regex>]`
- HTML-comment directives: `<!-- rivet-docs-check: ignore GNV-396 -->`
or `<!-- rivet-docs-check: ignore-line -->`.
- **AGENTS.md template** now ships an `ignore SC-1 REQ-001 FEAT-042`
directive so a fresh `rivet init && rivet docs check` doesn't fail on
its own example IDs. (issue #2)
- **`AUDIT:` marker syntax** documented for the `ArtifactCounts`
invariant.
- **`conditional-rules:` worked example** in `getting-started.md`.
- **`<!-- BEGIN/END rivet-managed -->` contract** documented for
`rivet init --agents`. Content outside the markers is preserved across
regeneration.

### CLI

- **`rivet stamp` batch flags**: `--type PATTERN` (glob or exact type),
`--changed-since REF` (git-aware), `--missing-provenance`. No more
`xargs` loops to stamp a batch of artifacts. (issue #4)
- **`rivet init --agents --force-regen`** now requires `--yes` to confirm
the destructive overwrite. The flag was previously one accidental
trigger away from destroying a hand-written AGENTS.md.
- **`rivet embed artifact:X / links:X / table:T:F`** error message now
explains why the embed only renders inside markdown documents instead
of the cryptic "handled inline" string.

### Looking ahead — Safety-Critical Rust roadmap

The next planned release will start a workspace-wide clippy lint
escalation aligned with the Safety-Critical Rust Consortium guidelines:
`unwrap_used`, `expect_used`, `indexing_slicing`,
`wildcard_enum_match_arm`, `as_conversions`, `arithmetic_side_effects`,
and `print_stdout` / `print_stderr` outside the CLI binary. Each lint
will be enabled at `warn` first with per-site `allow` annotations
carrying a `// SAFETY-REVIEW:` rationale, then escalated to `deny` once
the backlog is drained. A later release will raise the `rivet-core`
coverage gate from 40% → 70% and flip mutation testing to a hard gate.

The eight commits in this release already implement the SCRC pattern
"no silent acceptance of malformed input" empirically — the lint
escalation makes the same discipline mechanical.

## [0.4.1] — 2026-04-22

### Correctness fixes (HIGH)
Expand Down
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ members = [
]

[workspace.package]
version = "0.4.1"
version = "0.4.2"
authors = ["PulseEngine <https://github.com/pulseengine>"]
edition = "2024"
license = "Apache-2.0"
Expand Down
Loading
Loading