feat: SAFE Causal Incident Graph v0.1 - #219
Conversation
📝 WalkthroughWalkthroughThis 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. ChangesSCIG verification
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
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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
📒 Files selected for processing (5)
crates/proofpath-verifier/src/bin/proofpath-scig.rsdocs/safe-causal-incident-graph/README.mddocs/safe-causal-incident-graph/SAFE_CAUSAL_INCIDENT_GRAPH.mdexamples/safe-near-miss.jsonschemas/safe-causal-incident-graph-v0.1.schema.json
| 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", | ||
| ); |
There was a problem hiding this comment.
🗄️ 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.
| 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); | ||
| }; |
There was a problem hiding this comment.
🎯 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.
| 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" }, |
There was a problem hiding this comment.
🗄️ 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.
| "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 |
There was a problem hiding this comment.
🗄️ 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 forcredential-exposureandcredential-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-L85docs/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" } |
There was a problem hiding this comment.
🗄️ 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.
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 → evidenceThe model also carries phase, time, provenance, trace references and evidence references.
Included
proofpath-scigverifier;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-scigExpected reference verdict:
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
Documentation
Tests