Skip to content
Back to skills

Create Evidence Case

ASecurity

Build structured evidence cases using Claims-Arguments-Evidence (CAE) trees. AGENT-DRIVEN composable orchestrator: the agent decomposes the question, calls existing skills for data collection and verification, then DECIDES the verdict. Python runner.py is a thin data collector and persistence layer.

  • 6 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added September 11, 2026
ai-agentspythongobashreactnodeexpressdockerdebuggingsecuritydocumentation

Works with

  • cli

Security analysis

A100/100

Pro scans all 18 files and shows the line behind each finding

Scanned September 11, 2026

npx -y skills add grahama1970/agent-skills --skill create-evidence-case --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Create Evidence Case?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Create Evidence Case
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/grahama1970-create-evidence-case/badge)](https://www.skillsdirectory.com/skills/grahama1970-create-evidence-case)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: create-evidence-case
description: >
  Build structured evidence cases using Claims-Arguments-Evidence (CAE) trees.
  AGENT-DRIVEN composable orchestrator: the agent decomposes the question,
  calls existing skills for data collection and verification, then DECIDES
  the verdict. Python runner.py is a thin data collector and persistence layer.
allowed-tools:
  - Bash
  - Read
  - Grep
  - Glob
  - Write
  - Edit
triggers:
  - create-evidence-case
  - evidence case
  - build evidence
  - verify claim
  - evidence tree
metadata:
  short-description: "Composable CAE evidence orchestrator"
  version: "4.3.0"
provides:
  - evidence-case-creation
  - uct-strategy-selection
  - evidence-tree-persistence
  - claim-verdict-grading
  - assurance-case-output
  - oscal-export
  - qra-candidate-quarantine
  - human-review-interview
composes:
  - memory
  - extract-entities
  - assistant
  - taxonomy
  - lean4-prove
  - dogpile
  - edge-verifier
  - cmmc-assessor
  - create-gsn-diagram
  - export-oscal
  - create-figure
  - task-monitor
  - interview
  - prompt-lab
  - analytics
  - agentic-evals
taxonomy:
  - verification
  - evidence
  - quality-assurance
disciplines:
  - compliance-security
  - evaluation-quality
---

> STOP. READ THIS ENTIRE SKILL.MD BEFORE CALLING ANY ENDPOINT.

**User-facing documentation:** See [COMPLETE_EXAMPLE.md](COMPLETE_EXAMPLE.md) for compliance officer workflow, CAE tree examples, and the tool's role as a research assistant (not compliance oracle).

# /create-evidence-case v4.3 — Composable Orchestrator

Build structured **Claims-Arguments-Evidence (CAE)** trees. This is an **agent-driven** skill — you (the agent) orchestrate existing skills, reason about results, and make all judgment calls. The Python code is a thin data collector.

## PROVIDER BOUNDARY

Do not call `$scillm`, `/scillm`, `http://localhost:4001`,
`/v1/chat/completions`, or `/v1/scillm/*` directly from a
`/create-evidence-case` project-agent workflow. Provider/model execution is
owned by Tau. If model rendering, batch review, shadow collection, or provider
delegation is required, express it as a Tau DAG contract, Tau skill node, or
the owning skill's Tau-mediated runtime and consume the resulting receipts.

Direct SciLLM use is allowed only for Tau/SciLLM maintenance or when the human
explicitly asks to operate SciLLM itself. A CAE verdict must remain grounded in
Memory recall, `/extract-entities` proof packets, same-technique checks, and
deterministic gates; raw model output is never evidence.

## INPUT/OUTPUT MODEL (v4.2)

**Input**: Any control from `sparta_controls` (SPARTA, NIST, CWE, CAPEC) OR a raw question.

**Output**: Always a QRA document in `sparta_qra`, regardless of input source.

| Input Source | Example | Output |
|--------------|---------|--------|
| SPARTA control | SA-1 | QRA with `source_framework: "SPARTA"` |
| NIST control | IA-5 | QRA with `source_framework: "NIST-800-53"` |
| CWE | CWE-287 | QRA with `source_framework: "CWE"` |
| Raw question | "What is CWE-287?" | QRA with extracted `source_control_id` |

### QRA Output Tiers

Not every QRA has evidence. The `evidence_case` field being `null` is a **deterministic signal** (no crosswalk edges exist), not a failure.

| Tier | evidence_case | Use Case |
|------|---------------|----------|
| **Informational** | null | Explanation, lookup (no crosswalk edges) |
| **Grounded** | `{chains: [...], formal_proof: null}` | Compliance, audit |
| **Verified** | `{chains: [...], formal_proof: {success: true}}` | Safety-critical, formal methods |

All fields (chains, formal_proof, sacm_ref) live **inside** evidence_case. One null check.

### Entity Extraction → Evidence Case Flow

```
"What is CWE-287?"
       │
       ▼
/extract-entities → "CWE-287" ✓
       │
       ▼
/create-evidence-case
├── Query crosswalk edges in sparta_relationships
├── Has edges → evidence_case: {chains: [...]}
└── No edges  → evidence_case: null (valid output)
       │
       ▼
QRA created in sparta_qra
├── question: "What is CWE-287?"
├── requirement: CWE-287 description
├── answer: Explanation text
├── source_framework: "CWE"
├── source_control_id: "CWE-287"
└── evidence_case: {...} or null  # SELF-CONTAINED: includes chains, proof, sacm_ref
```

### Self-Contained QRA Schema (v4.3)

Every QRA embeds all metadata for downstream consumption. **evidence_case is fully
self-contained** — formal proofs and SACM references live INSIDE it, not at top level.
This ensures traceability in ArangoDB and portability for OSCAL/SACM export.

```python
{
    # Core QRA fields
    "question": str,
    "requirement": str,
    "answer": str,
    "source_framework": str,        # SPARTA, NIST-800-53, CWE, CAPEC
    "source_control_id": str,       # SA-1, IA-5, CWE-287
    "relationship_id": str,         # Links persona variations
    "expertise": str,               # expert, layperson, pm, compliance
    "difficulty": str,              # single_hop, multi_hop, synthesis
    
    # Taxonomy (Mind tags for SPARTA scope)
    "mind": list[str],              # ["Detect", "Harden"]
    
    # Evidence case — SELF-CONTAINED (null if no crosswalk edges)
    # All verification evidence lives here: chains, proof, SACM ref
    "evidence_case": {
        "chains": [...],            # Crosswalk paths (source → hops → target)
        "confidence": float,        # 0.0-1.0 combined confidence
        "methods": list[str],       # ["direct", "mitre_chain", "nist_nvd"]
        
        # Formal proof (null if not verified) — INSIDE evidence_case
        "formal_proof": {
            "success": bool,
            "code": str,            # Lean4 source code
            "attempts": int,
            "errors": list | None,
            "proved_at": int | None,
        } | None,
        
        # SACM reference (null if not exported) — INSIDE evidence_case
        "sacm_ref": {
            "gid": str,             # SACM Goal node GID (lookup key)
            "xml_snippet": str,     # First 500 chars (preview)
            "generated_at": int,
        } | None,
    } | None,
}
```

**Why self-contained?** ISO 26262 and DO-178C refinement checking patterns bundle
verification evidence with the claim. Separate collections would require joins,
lose portability, and break single-document export to OSCAL/SACM XML.

### Cached Evidence Case in QRAs (v4.3.1)

Every QRA has a pre-computed `evidence_case` field containing extracted entities
with **character spans** for UI inline highlighting. This is populated by the
`evidence_case_backfill` pipeline (not query-time extraction).

```python
{
    "evidence_case": {
        # Core extracted data (from /extract-entities)
        "question_text": str,           # First 500 chars of question
        "control_ids": list[str],       # ["IA-0006", "CWE-287"]
        "glossary": [                   # Snapshot copied from /extract-entities.glossary
            {
                "id": "IA-0006",
                "name": "Authentication Mechanisms",
                "framework": "SPARTA",
                "type": "countermeasure",
                "description": "..."
            }
        ],
        "crosswalk_chains": [...],      # Framework-to-framework links
        "resolved_entities": [...],     # Terms that resolved to controls
        
        # --- SPANS FOR UI HIGHLIGHTING (SPARTA Explorer Chat) ---
        "spans": [
            {
                "text": "IA-0006",       # The matched text
                "span": [9, 16],         # [start, end] character positions
                "kind": "control_id",    # Type: control_id, aerospace_term, phrase
                "framework": "SPARTA",   # Only for control_id kind
                "name": "Authentication Mechanisms",
                "grounded_to_framework": True
            },
            {
                "text": "CWE-287",
                "span": [33, 40],
                "kind": "control_id",
                "framework": "CWE",
                "name": "Improper Authentication",
                "grounded_to_framework": True
            }
        ],
        
        "review_status": "auto",        # auto, approved, rejected
        "extracted_at": str             # ISO timestamp
    }
}
```

### SPARTA Explorer Chat: Inline Evidence Highlighting

The Chat agent uses `evidence_case.spans` to render inline highlights on question text:

```tsx
// React component example
function QuestionWithHighlights({ question, spans }) {
    // Sort spans by start position
    const sorted = [...spans].sort((a, b) => a.span[0] - b.span[0]);
    
    let result = [];
    let lastEnd = 0;
    
    for (const s of sorted) {
        const [start, end] = s.span;
        
        // Text before this span
        if (start > lastEnd) {
            result.push(<span key={`t-${lastEnd}`}>{question.slice(lastEnd, start)}</span>);
        }
        
        // The highlighted entity
        const color = s.kind === 'control_id' 
            ? FRAMEWORK_COLORS[s.framework] 
            : 'yellow';
        
        result.push(
            <Tooltip key={`e-${start}`} content={`${s.name} (${s.framework})`}>
                <span className={`bg-${color}-200`}>{s.text}</span>
            </Tooltip>
        );
        
        lastEnd = end;
    }
    
    // Remaining text
    if (lastEnd < question.length) {
        result.push(<span key="tail">{question.slice(lastEnd)}</span>);
    }
    
    return <p>{result}</p>;
}
```

**Span kinds and colors:**

| Kind | Color | Example |
|------|-------|---------|
| `control_id` | Framework-specific (SPARTA=blue, CWE=orange, NIST=green) | IA-0006, CWE-287 |
| `aerospace_term` | Purple | FPGA, TT&C, crosslink |
| `phrase` | Yellow | supply chain attack |

**Framework colors (NVIS standard):**

| Framework | Color | Hex |
|-----------|-------|-----|
| SPARTA | Blue | `#3B82F6` |
| CWE | Orange | `#F97316` |
| NIST | Green | `#22C55E` |
| CAPEC | Red | `#EF4444` |
| ATT&CK | Purple | `#A855F7` |

### Cached vs Query-Time Evidence

| Field | When Populated | Use |
|-------|----------------|-----|
| `evidence_case.spans` | Backfill (pre-computed) | UI highlighting |
| `evidence_case.glossary` | Backfill | Cached `/extract-entities.glossary` metadata |
| `cae_tree` (in endpoint response) | Query-time | Full CAE tree with traceability |
| `entity_context` (in endpoint response) | Query-time | Compact `/extract-entities` agent view with authoritative `proof_packet.assertions`, `nodes`, and candidate-only boundaries |

The cached `evidence_case` enables fast rendering without query-time entity extraction.
Glossary semantics are owned by `/extract-entities`; `/create-evidence-case`
copies that glossary into the evidence case and must not independently infer
control names, definitions, or unsupported parenthetical terms.
Runtime extraction proof is preserved in `entity_context`; agents should use
`entity_context.proof_packet.assertions` for grounding decisions and keep
legacy glossary fields for compatibility, prompts, and UI display.
The `cae_tree` (requested via `include_cae_tree=True`) provides full CAE analysis
with traceability strength and validation gates.

## TRACEABILITY vs VERIFICATION

Matching requirements to controls (via `/match/requirement`) provides **traceability** —
linking related concepts. But traceability alone is NOT SUFFICIENT for certification.

| Level | What it proves | Provides | Sufficient for |
|-------|----------------|----------|----------------|
| **Matching** | "Related concepts" | Traceability | Gap detection, triage |
| **Confidence scoring** | "Probably covers" | Prioritization | Audit prep |
| **Formal proof** | "Functionally entails" | Verification | ISO 26262, DO-178C certification |

**Why matching is insufficient:**
```
Requirement: "shall implement multi-factor authentication for all users"
Control: SV-AC-2 "implement access control mechanisms"

Matching confidence: 0.82
  - Crosswalk chain exists (CWE → NIST → SPARTA)
  - QRA evidence mentions both
  - Semantic similarity is high

Problem: Does SV-AC-2 ACTUALLY cover MFA?
  - Words overlap, but FUNCTIONS might not
  - Control might be narrower/broader than requirement
  - Edge cases might not be addressed
```

**Why formal proofs are needed:**
```
Formal proof (Lean4):
  theorem control_satisfies_requirement :
    ∀ u : User, implements(SV-AC-2, u) → authenticated(u) ∧ factors(u) ≥ 2

  Proof result: success=true
  Meaning: The control ENTAILS the requirement — not just similar words,
           but the control is a valid implementation of the requirement.
```

The `evidence_case.formal_proof` field stores this verification evidence.
Matching builds the edges; formal proofs prove they're functionally valid.

## EXECUTION MODES

| Mode | Engine | When | Latency |
|------|--------|------|---------|
| **Live** | Project agent (you) | Interactive use | ~10-12s (dominated by `/memory recall`) |
| **Batch** | `EvidenceCaseRunner` | Nightly eval, `run_question_bank.py` | ~17s (subprocess classifiers) |

**Live mode**: YOU are the engine. Call `/memory recall` for data, do decomposition + entity analysis + same-technique check + verdict in your reasoning. No `/assistant classify` calls needed — you ARE the classifier. `/lean4-prove` Docker compilation is near-instant.

**Batch mode**: `EvidenceCaseRunner` in `runner.py` proxies for you using
owned data collectors and deterministic gates. For nightly training-data
collection or model/provider shadowing, route the work through Tau and preserve
Tau receipts; do not call `/scillm` directly from this skill workflow. Run via
`run_question_bank.py`.

## CORE PRINCIPLE

Entities from different sentence components must resolve to the **same SPARTA technique**. If they don't, the question spans unrelated domains and the verdict is INCONCLUSIVE.

## HOW IT WORKS — 7-Step Agent Flow

### Step 1: Sentence Decomposition (Agent)

Parse the question into Given/Then components. This is native LM capability — no tool needed.

```
Question: "Given the F-36's use of third-party FPGA vendors, which SPARTA
           supply chain attack vectors should we prioritize in our CMMC
           Level 3 compliance audits?"

Given (context/constraints):
  - F-36                        → project context
  - third-party FPGA vendors    → SPARTA controls term

Then (search targets/threats):
  - SPARTA supply chain attack vectors  → SPARTA controls term
  - CMMC Level 3 compliance audits      → framework mapping term
```

Each component becomes a separate query. The sentence structure tells you how they relate: "Given X, which Y should we Z?" means X constrains the search for Y in the context of Z.

### Step 2: Per-Component QRA Retrieval (Parallel)

For EACH component, query `/memory recall`:

```bash
# Per-component recall — use --collections sparta_qra
.claude/skills/memory/run.sh recall --q "<component>" --collections sparta_qra --k 10
```

Run these in PARALLEL — one subprocess per component. QRAs have `control_id`, `tactical_tags`, `question`, `answer` fields.

### Step 3: Entity Extraction + Grounding Verification (CRITICAL)

Extract entities and check grounding from the FULL question. In batch mode, `collect_entities()` calls `extract_entities()` directly (no subprocess). In live mode, you can call:

```bash
.claude/skills/extract-entities/run.sh extract --json "<full question>"
```

The default JSON result is the compact project-agent view. Use `--verbose` only
when debugging legacy fields such as `resolved_entities`, `glossary`, and
`entity_nodes`. `/create-evidence-case` internally requests the legacy view for
old compatibility paths, but preserves the compact proof packet as
`entity_context`.

The result includes these key fields:

```json
{
  "view": "agent",
  "grounding_ok": false,
  "agent_decision": {
    "safe_to_answer": false,
    "needs_clarification": true,
    "primary_entity_id": "CM0029",
    "grounding_source": "proof_packet.assertions"
  },
  "proof_packet": {
    "authoritative": true,
    "llm_used": false,
    "assertions": [
      {
        "rule_id": "SPARTA_CONTROL_ID_EXACT_MATCH",
        "subject": "CM0029",
        "predicate": "resolves_to",
        "object": "sparta_controls/ctrl__CM0029",
        "status": "passed"
      },
      {
        "rule_id": "PARENTHETICAL_DESCRIPTOR_MATCHES_CONTROL_CATEGORY_OR_ALIAS",
        "subject": "unsupported:wrong_link",
        "predicate": "does_not_describe_control",
        "object": "CM0029",
        "status": "failed"
      }
    ]
  },
  "nodes": {
    "anchors": [],
    "validated_context": [],
    "suppressed": [],
    "unsupported": []
  }
}
```

**Read `grounding_ok` first.** If `false`, check failed
`proof_packet.assertions` and `nodes.unsupported`:

| Warning category | Meaning | Your action |
|-----------------|---------|-------------|
| `fabricated_id` | ID-like term with 0 matches in 8,979+ controls | Strong signal — question's premise is likely fabricated |
| `not_in_corpus` | Term doesn't appear anywhere in domain corpus | Check if it's a real concept outside SPARTA scope |
| `misspelling` | Close to a known term (edit distance) | Proceed with correction noted |

**Per-control `confidence` scores:**

| Score | Meaning |
|-------|---------|
| 1.0 | Exact control_id match with QRAs |
| 0.9 | Exact match, no QRAs yet |
| 0.7-0.85 | Fuzzy ID match (probable typo) |
| 0.4-0.8 | Phrase grounded via hybrid search word overlap |
| 0.0 | Unresolved — fabricated, misspelled, or not in corpus |

This is what prevents false positives on adversarial questions. Semantic search will ALWAYS find *something* for security-adjacent keywords. Grounding evidence shows whether the *specific entities claimed by the question* actually exist.

### Step 4: Same-Technique Check (Agent — CRITICAL)

Read the per-component recall results and extracted entities. Ask:

**Do entities from different components resolve to the SAME technique?**

- Check `tactical_tags` across components — do they cluster into 1-2 techniques?
- Check `related_pairs` from /extract-entities — do cross-component edges exist?
- If components share technique relationships → coherent question
- If entities exist but have no shared technique → spans unrelated domains

This is THE critical judgment. Components must bridge through a shared technique for the evidence to be coherent.

### Step 5: Clarification (Skill)

Ask `/memory clarify` to explain which entities are related and which aren't:

```bash
# Clarify entity relationships
.claude/skills/memory/run.sh clarify --q "<question>"
```

This returns structured disambiguation: related entities, unrelated entities, suggested decompositions. Read this to confirm or revise your same-technique assessment.

### Step 6: Formal Verification (Skill)

Formalize the evidence chain as a theorem and attempt proof:

```bash
# Formalize and verify the evidence chain
.claude/skills/lean4-prove/run.sh --requirement "<formal claim derived from evidence>"
```

The formal claim should express: "Given [components from recall], [technique bridge] implies [verdict]."
- Proof succeeds → strong formal backing for SATISFIED
- Proof fails → confirms gap, supports INCONCLUSIVE
- Explicitly not formalizable (or low-confidence formalizability) → skip gate with no penalty
- Provability classifier unavailable → Gate 5 is blocked, so do not return SATISFIED

### Step 7: Final Verdict + Report (Agent)

Synthesize all results into a verdict:

- **SATISFIED**: All components resolve, same technique bridge confirmed, QRAs address the question. Proof success strengthens this.
- **INCONCLUSIVE**: Some components covered, others need research. OR entities exist but don't share a technique. Proof failure confirms gap.
- **NOT_SATISFIED**: Components don't resolve, off-topic, or no entities found.

Persist the case:

```python
from runner import EvidenceCaseStore2

store = EvidenceCaseStore2()
store.persist_case(
    question="...",
    category="compliance",
    verdict_state="satisfied",
    grade="A+",
    score=1.0,
    gates=[...],
    evidence_items=qra_items,
    answer="...",
    technique_groups={"Harden": 5, "Detect": 3},
    sub_claims=["[supply chain] ...", "[CMMC L3] ..."],
    control_ids=["SR-3(2)", "SA-8", "SI-3"],
    decomposition=decomposition_node.to_dict(),
)
```

## CONDITIONAL BRANCHES

### If INCONCLUSIVE → Tier 3 Research

When recall is sparse or entities don't bridge, use `/dogpile` for Tier 3 research:

```bash
# Research gap areas
.claude/skills/dogpile/run.sh search "<gap query>" --auto-preset
```

Re-evaluate with new evidence. If still inconclusive, report the gaps.

### If Question Involves CMMC → Framework Mapping

When the question references CMMC, add compliance mapping:

```bash
# CMMC assessment
.claude/skills/cmmc-assessor/run.sh assess --level 3 --family <family>
```

### After Verdict → Assurance Case + OSCAL Export

For SATISFIED or INCONCLUSIVE verdicts with sufficient evidence:

```bash
# GSN diagram (ISO 15026)
.claude/skills/create-gsn-diagram/run.sh render --control <primary_control_id>

# OSCAL JSON export for auditors
.claude/skills/export-oscal/run.sh export --framework NIST-800-171
```

### Cross-Component Edge Verification

When entity relationships need validation:

```bash
# Verify edges between entities
.claude/skills/edge-verifier/run.sh verify --source_id <ID> --text "<content>"
```

### Topic Classification

For initial on-topic/off-topic filtering:

```bash
# Classify topic
.claude/skills/assistant/run.sh classify --task topic-classifier --text "<question>"
```

### Visualization

For metric charts and report figures:

```bash
# Generate charts
.claude/skills/create-figure/run.sh metrics --input <data.json> --output <path>
```

## DATA COLLECTION FUNCTIONS

The runner provides thin subprocess wrappers for each skill:

```python
from runner import (
    # Data collection
    collect_recall,        # /memory recall → QRA items
    collect_entities,      # /extract-entities → control_ids, related_pairs
    collect_topic,         # /assistant classify → on_topic, category
    collect_clarify,       # /memory clarify → disambiguation
    collect_lean4_proof,   # /lean4-prove → proof result
    collect_dogpile,       # /dogpile search → Tier 3 research
    collect_cmmc,          # /cmmc-assessor → CMMC mapping
    collect_edge_verify,   # /edge-verifier → edge validation
    group_by_technique,    # Group QRAs by tactical_tags
    # QRA quarantine + human review
    quarantine_as_candidate_qra,    # SATISFIED → staging collection
    generate_gap_review_questions,  # INCONCLUSIVE → gap review form
    promote_candidate_qra,          # Approved → sparta_qra
    reject_candidate_qra,           # Rejected → rejected_qras (GRPO)
    process_interview_result,       # Process /interview responses
)
```

## WHAT YOU MUST NOT DO

- **Do NOT regex-parse control IDs** — read structured fields from recall results
- **Do NOT use stopword lists** — you understand language, use that
- **runner.py uses direct graph_memory imports** — subprocess is only fallback when imports fail
- **Do NOT auto-pass verdicts** — if you can't tell whether evidence is coherent, say INCONCLUSIVE
- **Do NOT write deterministic if/else for coverage** — language is too variable, YOU decide
- **Do NOT assume one QRA matches the full question** — decompose and query per component
- **Do NOT skip the same-technique check** — this is the core criterion
- **Do NOT call `/scillm` directly** — provider/model work belongs behind Tau
  receipts unless the human explicitly asks for SciLLM maintenance

See [EXAMPLES.md](references/EXAMPLES.md) for worked examples of SATISFIED and INCONCLUSIVE verdicts.

---

See [POST_VERDICT.md](references/POST_VERDICT.md) for QRA quarantine, human review, promotion/rejection, and the QRA lifecycle flow.

---

# Returns: {"candidate_id": "EC-9670e932", "interview_file": "...", "status": "pending_review"}
```

This writes to `sparta_qra_candidates` collection and generates an `/interview` review form.

**Human review via /interview:**

```bash
# Launch review form in browser
.claude/skills/interview/run.sh --file <interview_file> --mode html
```

The review form asks:
1. Is the synthesized answer accurate?
2. Edit the answer if needed
3. Confirm control IDs
4. Confirm tactical tags
5. Final decision: approve / approve_with_edits / reject / defer

**Process the review result:**

```python
from runner import process_interview_result

result = process_interview_result("EC-9670e932", interview_result)
# approve → promotes to sparta_qra
# reject → moves to rejected_qras (GRPO training data)
# defer → stays as pending_review
```

### INCONCLUSIVE → Gap Review

When INCONCLUSIVE, generate a gap review form:

```python
from runner import generate_gap_review_questions

interview_file = generate_gap_review_questions(
    question="Given the F-36's use of FPGA vendors...",
    case_result=case_result,
    clarify_result=clarify_result,
)
```

The gap review asks:
1. Real gap or false gap?
2. Action: /dogpile research, adjust thresholds, manual QRA, or ignore

### Promotion/Rejection Functions

```python
from runner import promote_candidate_qra, reject_candidate_qra

# After human approves
promote_candidate_qra("EC-9670e932", edited_answer="...")  # → sparta_qra

# After human rejects (kept for GRPO training)
reject_candidate_qra("EC-9670e932", reason="Answer conflates HW and SW supply chain")
```

### QRA Lifecycle Flow

```
/create-evidence-case SATISFIED
  → quarantine_as_candidate_qra()
    → sparta_qra_candidates collection
    → optional /ask --cae-gap-review over the frozen QRA/evidence_case snapshot
    → /interview review form generated
      → Human reviews in browser/TUI
        → APPROVED → promote_candidate_qra() → sparta_qra
        → EDITED  → promote_candidate_qra(edited_answer) → sparta_qra
        → REJECTED → reject_candidate_qra(reason) → rejected_qras (GRPO)
        → DEFERRED → stays pending

/create-evidence-case INCONCLUSIVE
  → generate_gap_review_questions()
    → /interview gap review form
      → REAL GAP → /dogpile research task
      → FALSE GAP → adjust technique bridge thresholds
```

`/ask --cae-gap-review` is a downstream review layer, not the evidence-case
builder. Use it when a candidate QRA or existing QRA needs a bounded
Brandon/Margaret/Jennifer + judge review before human promotion, edit,
rejection, or deferral. `/create-evidence-case` remains responsible for
assembling the QRA, resolved controls, crosswalk chains, proof/SACM references
when present, and the cached `evidence_case` snapshot.

## Graph Model

- **ClaimNode**: Root question with verdict
- **DecompositionNode**: Given/Then structure with component queries (new in v4)
- **StrategyNode**: Verification approach (always "agent_driven" in v4)
- **EvidenceNode**: Individual QRA or skill result supporting the answer
- **VerdictNode**: Final judgment citing evidence


See [EXAMPLES.md](references/EXAMPLES.md) for the 50-question stress test, plausibility prompt optimization, and standards alignment.

## Common Mistakes

### WRONG: Regex-parsing control IDs from recall results
```python
import re
ids = re.findall(r'[A-Z]{2}-\d+', result_text)  # fragile regex
```

### RIGHT: Read structured fields from recall results
```python
control_ids = [qra["control_id"] for qra in recall_results]
```

### WRONG: Skipping the same-technique check and auto-passing
```python
if len(recall_results) > 0:
    verdict = "SATISFIED"  # entities exist but may span unrelated domains!
```

### RIGHT: Verify entities from different components share a technique
```python
# Check tactical_tags cluster into 1-2 techniques across components
# Check related_pairs for cross-component edges
# If no shared technique -> INCONCLUSIVE
```

### WRONG: Using /memory recall without specifying collections
```bash
.claude/skills/memory/run.sh recall --q "firmware tampering"  # searches everything
```

### RIGHT: Scope recall to sparta_qra collection
```bash
.claude/skills/memory/run.sh recall --q "firmware tampering" --collections sparta_qra --k 10
```

## Crosswalk Edge Reference (sparta_relationships)

When querying crosswalk chains, edges are stored in `sparta_relationships` with specific framework casing.

### Edge Types and Casing (CRITICAL)

| Edge Type | source_framework | target_framework | Count |
|-----------|-----------------|------------------|-------|
| CWE→SPARTA | `"CWE"` | `"SPARTA"` (uppercase) | 2,825+ |
| NIST→SPARTA | `"nist"` | `"sparta"` (lowercase) | 289+ per NIST |
| CAPEC→CWE | `"CAPEC"` | `"CWE"` | 1,212+ |
| CWE→CWE | `"cwe"` | `"cwe"` (lowercase) | parent/child |

**Always check both cases** when filtering for SPARTA targets:
```python
for tf_case in ["sparta", "SPARTA"]:
    resp = client.post("/list", json={
        "collection": "sparta_relationships",
        "filters": {"source_control_id": control_id, "target_framework": tf_case}
    })
```

### Crosswalk Lookup Paths

| Start | Path | Data Source |
|-------|------|-------------|
| CWE | CWE→SPARTA (direct edge) | SPARTA v3.1 `cwe_class_ids` |
| CWE | CWE→NIST→SPARTA (2-hop) | Heimdall `nist_control_ids` field + NIST→SPARTA edges |
| CWE | CWE→CAPEC→ATT&CK→SPARTA (4-hop) | MITRE curated `capec_ids`, `attack_technique_ids` |

### Key Document Fields

| Collection | Field | Contains |
|------------|-------|----------|
| `sparta_controls` (CWE) | `nist_control_ids` | NIST control IDs from Heimdall mapping |
| `sparta_controls` (CWE) | `capec_ids` | CAPEC attack pattern IDs |
| `sparta_controls` (SPARTA) | `cwe_class_ids` | CWE IDs this technique addresses |
| `sparta_controls` (any) | `source_framework` | Framework name (SPARTA, CWE, NIST, etc.) |

### Example: Finding SPARTA Targets for CWE-287

```python
# Path 1: Direct CWE→SPARTA edges (preferred)
resp = client.post("/list", json={
    "collection": "sparta_relationships",
    "filters": {"source_control_id": "CWE-287", "target_framework": "SPARTA"}
})
# Returns: DE-0001, IA-0001, etc.

# Path 2: NIST 2-hop (for unmapped CWEs like CWE-79)
cwe = fetch_control("CWE-79")  # nist_control_ids: ['SI-10']
for nist_id in cwe["nist_control_ids"]:
    resp = client.post("/list", json={
        "collection": "sparta_relationships",
        "filters": {"source_control_id": nist_id, "target_framework": "sparta"}  # lowercase!
    })
    # Returns: CM0001, CM0002, etc.
```

Files in this skill

  • COMPLETE_EXAMPLE.md31.1 KB
  • EVIDENCE_CASE_VALIDATION.md38.3 KB
  • SKILL.md29.9 KB
  • WALKTHROUGH.md34.6 KB
  • assets/pipeline-flow.svg24.6 KB
  • batch_50_f36.py21 KB
  • candidate_qra.py22.2 KB
  • collect.py46 KB
  • conftest.py6.9 KB
  • daemon_client.py24.3 KB
  • deterministic_test.py11.1 KB
  • docs/7-step-guide.html37.8 KB
  • docs/DESIGN_BOARD.md44.6 KB
  • docs/VIEWER_CONTRACT.md1.1 KB
  • docs/evidence-case-q01-live.html1.9 KB
  • docs/evidence-case-schema-viewer.html6.9 KB
  • evidence_case.py40.4 KB
  • figures/evidence_case_mockup.html30 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.