Skip to content

feat: SAFE Causal Incident Graph v0.1 - #219

Merged
safal207 merged 8 commits into
mainfrom
feat/safe-causal-incident-graph-v0.1
Aug 10, 2026
Merged

feat: SAFE Causal Incident Graph v0.1#219
safal207 merged 8 commits into
mainfrom
feat/safe-causal-incident-graph-v0.1

Conversation

@safal207

@safal207 safal207 commented Aug 10, 2026

Copy link
Copy Markdown
Owner

Summary

Adds a reference implementation of SAFE Causal Incident Graph (SCIG) v0.1 to ProofPath.

SCIG formalizes AI incidents and near misses as an evidence-backed causal state-transition graph:

actor → action → pre_state → control → transition → post_state → invariant → violation → cause → containment → recovery → verification → evidence

The model also carries phase, time, provenance, trace references and evidence references.

Included

  • normative SCIG v0.1 specification;
  • JSON Schema Draft 2020-12;
  • realistic AI-agent near-miss fixture;
  • native Rust proofpath-scig verifier;
  • invariant, causal-edge, lifecycle and evidence-reference validation;
  • negative tests for broken transition references, missing evidence and impossible verification success;
  • SAFE/OpenTelemetry mapping and Why was this action possible? causal-query semantics.

Run

cargo run -p proofpath-verifier --bin proofpath-scig -- examples/safe-near-miss.json
cargo test -p proofpath-verifier --bin proofpath-scig

Expected reference verdict:

INV-CRED-001       VIOLATED
INV-NET-001        HELD
CONTAINMENT        PASSED
RECOVERY           PASSED
VERIFICATION       PASSED
RESULT             VALID

Design boundary

SCIG does not replace SAFE-style exchange or OpenTelemetry. It acts as a causal + recovery + verification semantics layer over preserved evidence.

Validation

The repository's existing Rust PR workflow will run formatting, Clippy with warnings denied, and all workspace tests.

Summary by CodeRabbit

  • New Features

    • Added support for defining and validating SAFE Causal Incident Graph (SCIG) v0.1 records.
    • Added validation for incident states, transitions, causal relationships, recovery actions, verification results, and evidence references.
    • Added a sample near-miss incident record covering credential exposure and response activities.
  • Documentation

    • Added SCIG proposal documentation, quickstart guidance, usage examples, and verifier instructions.
  • Tests

    • Added coverage for valid records and common validation failures, including broken transitions, missing recovery prerequisites, and missing evidence.

@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This change adds the SCIG v0.1 schema, specification, example incident, Rust verifier binary, validation tests, and quickstart documentation. The verifier validates incident structure, references, lifecycle rules, causal edges, recovery, verification, and evidence.

Changes

SCIG verification

Layer / File(s) Summary
SCIG contract and schema
docs/safe-causal-incident-graph/SAFE_CAUSAL_INCIDENT_GRAPH.md, schemas/safe-causal-incident-graph-v0.1.schema.json
Defines the SCIG incident model, lifecycle objects, causal edges, telemetry mappings, verification rules, and evidence constraints.
Near-miss incident example
examples/safe-near-miss.json
Adds a SAFE-SCIG incident with state transitions, invariant results, causal links, containment, recovery, verification, and provenance-bearing evidence.
Verifier validation and CLI
crates/proofpath-verifier/src/bin/proofpath-scig.rs
Adds JSON loading, SCIG validation, status reporting, exit codes, and tests for valid and invalid documents.
SCIG quickstart documentation
docs/safe-causal-incident-graph/README.md
Documents the SCIG artifacts, verifier commands, expected output, negative tests, and evidence-to-verification flow.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI as proofpath-scig CLI
  participant Document as SCIG JSON document
  participant Validator as ValidationReport
  participant Report as status report
  CLI->>Document: read and deserialize
  Document->>Validator: validate sections and references
  Validator->>Report: render status and errors
  Report-->>CLI: return exit code
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.39% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely identifies the main change: adding SAFE Causal Incident Graph v0.1.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/safe-causal-incident-graph-v0.1

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@safal207
safal207 marked this pull request as ready for review August 10, 2026 14:07
@safal207
safal207 merged commit 685d50e into main Aug 10, 2026
1 of 2 checks passed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/proofpath-verifier/src/bin/proofpath-scig.rs`:
- Around line 172-175: Replace the string-based validation in the
transition.observed_at report.require call with parsing through the available
RFC 3339/date-time parser, accepting valid UTC and offset timestamps while
rejecting malformed, invalid-calendar, and invalid-time values. Add negative
tests covering invalid calendar and time inputs, and preserve the existing
validation error message.
- Around line 391-396: Update the argument parsing around program and path in
the proofpath-scig entrypoint to check for any remaining positional arguments
after the input path. If one exists, print the usage message and exit with
status 2; preserve the existing behavior when exactly one input path is
provided.

In `@schemas/safe-causal-incident-graph-v0.1.schema.json`:
- Line 84: Align the observed_at timestamp contract between the schema and
verifier: either update the verifier’s timestamp validation to accept all RFC
3339 offsets supported by the date-time format, or constrain the observed_at
schema definition to require the UTC trailing Z form. Ensure every schema-valid
observed_at value passes verification.
- Line 121: Update the schema definitions for causalEdge.evidence_reference,
verification.evidence_reference, verification.expected, and
verification.observed to require non-empty strings by adding minLength: 1. Keep
the verifier’s existing rejection and evidence-resolution behavior unchanged so
schema and CLI validation agree.
- Around line 102-123: Define a reusable node identity model in
schemas/safe-causal-incident-graph-v0.1.schema.json and require
causalEdge.source and causalEdge.target to resolve to declared object IDs rather
than merely non-empty strings. Update examples/safe-near-miss.json lines 60-85
to declare credential-exposure and credential-revocation or replace them with
registered IDs. Update
docs/safe-causal-incident-graph/SAFE_CAUSAL_INCIDENT_GRAPH.md lines 213-239 to
state endpoint resolution as required for causal-query conformance.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 3f97fa29-0e9e-4c57-ae44-1fa20c1acf6b

📥 Commits

Reviewing files that changed from the base of the PR and between e41ddc4 and 3c1dc23.

📒 Files selected for processing (5)
  • crates/proofpath-verifier/src/bin/proofpath-scig.rs
  • docs/safe-causal-incident-graph/README.md
  • docs/safe-causal-incident-graph/SAFE_CAUSAL_INCIDENT_GRAPH.md
  • examples/safe-near-miss.json
  • schemas/safe-causal-incident-graph-v0.1.schema.json

Comment on lines +172 to +175
report.require(
doc.transition.observed_at.contains('T') && doc.transition.observed_at.ends_with('Z'),
"transition.observed_at must be an RFC3339-like UTC timestamp",
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Parse transition.observed_at as an RFC 3339 timestamp.

The current check accepts invalid values such as not-a-dateTZ. It also rejects valid offset timestamps such as 2026-08-10T11:42:31+00:00. The schema requires a date-time value. Parse the value with an RFC 3339 parser and add negative tests for invalid calendar and time values.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/proofpath-verifier/src/bin/proofpath-scig.rs` around lines 172 - 175,
Replace the string-based validation in the transition.observed_at report.require
call with parsing through the available RFC 3339/date-time parser, accepting
valid UTC and offset timestamps while rejecting malformed, invalid-calendar, and
invalid-time values. Add negative tests covering invalid calendar and time
inputs, and preserve the existing validation error message.

Comment on lines +391 to +396
let mut args = env::args();
let program = args.next().unwrap_or_else(|| "proofpath-scig".to_string());
let Some(path) = args.next() else {
eprintln!("usage: {program} <scig.json>");
process::exit(2);
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject extra positional arguments.

The CLI ignores arguments after the input path. A command such as proofpath-scig first.json second.json can report success for only first.json. Reject remaining arguments and exit with status 2.

Proposed fix
     let Some(path) = args.next() else {
         eprintln!("usage: {program} <scig.json>");
         process::exit(2);
     };
+    if args.next().is_some() {
+        eprintln!("usage: {program} <scig.json>");
+        process::exit(2);
+    }
 
     match run(&path) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let mut args = env::args();
let program = args.next().unwrap_or_else(|| "proofpath-scig".to_string());
let Some(path) = args.next() else {
eprintln!("usage: {program} <scig.json>");
process::exit(2);
};
let mut args = env::args();
let program = args.next().unwrap_or_else(|| "proofpath-scig".to_string());
let Some(path) = args.next() else {
eprintln!("usage: {program} <scig.json>");
process::exit(2);
};
if args.next().is_some() {
eprintln!("usage: {program} <scig.json>");
process::exit(2);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/proofpath-verifier/src/bin/proofpath-scig.rs` around lines 391 - 396,
Update the argument parsing around program and path in the proofpath-scig
entrypoint to check for any remaining positional arguments after the input path.
If one exists, print the usage message and exit with status 2; preserve the
existing behavior when exactly one input path is provided.

"action": { "type": "string", "minLength": 1 },
"to": { "type": "string", "minLength": 1 },
"phase": { "type": "string", "minLength": 1 },
"observed_at": { "type": "string", "format": "date-time" },

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Align the timestamp contract with the verifier.

A date-time value can use an RFC 3339 offset such as 2026-08-10T11:42:31+00:00. The verifier rejects that value because it requires a trailing Z.

Accept RFC 3339 offsets in the verifier, or restrict the schema to the UTC form. A schema-valid document must not fail this verifier rule.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@schemas/safe-causal-incident-graph-v0.1.schema.json` at line 84, Align the
observed_at timestamp contract between the schema and verifier: either update
the verifier’s timestamp validation to accept all RFC 3339 offsets supported by
the date-time format, or constrain the observed_at schema definition to require
the UTC trailing Z form. Ensure every schema-valid observed_at value passes
verification.

Comment on lines +102 to +123
"causalEdge": {
"type": "object",
"required": ["type", "source", "target"],
"properties": {
"type": {
"enum": [
"enabled_by",
"required",
"triggered",
"bypassed",
"failed_to_prevent",
"amplified",
"masked",
"recovered_by",
"verified_by"
]
},
"source": { "type": "string", "minLength": 1 },
"target": { "type": "string", "minLength": 1 },
"evidence_reference": { "type": "string" }
},
"additionalProperties": true

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Define and enforce causal endpoint identity.

The causal-edge model accepts opaque endpoint strings. The verifier only checks that those strings are non-empty. Therefore it cannot verify that an edge connects graph objects.

  • schemas/safe-causal-incident-graph-v0.1.schema.json#L102-L123: add a node identity model and require causal endpoints to resolve to declared object IDs.
  • examples/safe-near-miss.json#L60-L85: declare nodes for credential-exposure and credential-revocation, or replace them with registered IDs.
  • docs/safe-causal-incident-graph/SAFE_CAUSAL_INCIDENT_GRAPH.md#L213-L239: state endpoint resolution as a conformance requirement for causal queries.

Without this contract, the example can be reported as valid while its “Why was this action possible?” path contains undefined nodes.

📍 Affects 3 files
  • schemas/safe-causal-incident-graph-v0.1.schema.json#L102-L123 (this comment)
  • examples/safe-near-miss.json#L60-L85
  • docs/safe-causal-incident-graph/SAFE_CAUSAL_INCIDENT_GRAPH.md#L213-L239
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@schemas/safe-causal-incident-graph-v0.1.schema.json` around lines 102 - 123,
Define a reusable node identity model in
schemas/safe-causal-incident-graph-v0.1.schema.json and require
causalEdge.source and causalEdge.target to resolve to declared object IDs rather
than merely non-empty strings. Update examples/safe-near-miss.json lines 60-85
to declare credential-exposure and credential-revocation or replace them with
registered IDs. Update
docs/safe-causal-incident-graph/SAFE_CAUSAL_INCIDENT_GRAPH.md lines 213-239 to
state endpoint resolution as required for causal-query conformance.

},
"source": { "type": "string", "minLength": 1 },
"target": { "type": "string", "minLength": 1 },
"evidence_reference": { "type": "string" }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Require non-empty values that the verifier requires.

The schema accepts "" for causalEdge.evidence_reference, verification.evidence_reference, verification.expected, and verification.observed. The verifier rejects empty verification values and cannot resolve an empty evidence reference.

Set minLength: 1 for these fields, or relax the verifier consistently. Otherwise schema validation and CLI validation produce conflicting results.

Also applies to: 141-144

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@schemas/safe-causal-incident-graph-v0.1.schema.json` at line 121, Update the
schema definitions for causalEdge.evidence_reference,
verification.evidence_reference, verification.expected, and
verification.observed to require non-empty strings by adding minLength: 1. Keep
the verifier’s existing rejection and evidence-resolution behavior unchanged so
schema and CLI validation agree.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant