evidence-pack Skill
Assemble per-release evidence packages for projects that have adopted the regulated-industry
track. The skill is the release-time partner of the traceability skill: where traceability
maintains the per-commit matrix, evidence-pack packages that matrix together with surrounding
artifacts (CI logs, PR reviews, signed commits, research citations, risk file) into a single
directory an external auditor can consume without further stitching.
The skill is a Design History File (DHF) generator in spirit. It does not invent evidence; it collects what already exists.
Usage
/evidence-pack v1.2.0 # Build full pack for tag v1.2.0
/evidence-pack v1.2.0 --include matrix,ci_run_log # Only collect specific kinds
/evidence-pack v1.2.0 --exclude pr_review # Collect everything except PR review records
/evidence-pack v1.2.0 --dry-run # Plan only; print what would be collected and exit
/evidence-pack v1.2.0 --force # Overwrite an existing evidence/v1.2.0 pack
Exactly one positional argument is required: the release version or git tag. Flags are optional.
Arguments
| Position / Flag | Behavior |
|---|---|
<version> (positional, required) |
Release identifier used as the output subdirectory name. Accepts semantic versions (v1.2.0), date-tagged releases (2026-05-04), or any other ASCII slug matching [A-Za-z0-9._-]+. Refuses values containing path separators or whitespace. |
--include <pattern> |
Comma-separated allowlist of artifact kind values to collect. When set, all kinds not in the list are skipped. See reference/manifest-schema.md for valid kinds. |
--exclude <pattern> |
Comma-separated denylist of artifact kind values to skip. Mutually independent from --include; if both are set, the include list is applied first, then the exclude list removes from it. |
--dry-run |
Run the planning phase only. Print the manifest plan (kinds, sources, collection commands) to stdout and exit zero. No files are written. Useful for previewing in CI before allowing real collection. |
--force |
Overwrite an existing evidence/<version>/ directory. Without --force, the skill refuses to clobber a previously generated pack and exits non-zero (idempotency contract). |
Inputs
| Path | Required | Purpose |
|---|---|---|
evidence/ (parent dir at repo root) |
Yes (gate) | Opt-in marker. Skill is a no-op when this directory is absent. |
docs/.index/traceability.yaml |
Optional | Verbatim copy collected as the matrix artifact. Skipped when absent. |
risk-file/ |
Optional | Recursively collected as the risk_file artifact. Skipped when absent. |
docs/research/ |
Optional | Citation files collected as research_artifact entries. Skipped when absent. |
gh CLI authenticated to the current repo |
Yes for ci_run_log and pr_review kinds |
Used to query workflow runs and PR review history. Skipped (with one info line per kind) if gh is not on PATH. |
git history with the release tag reachable |
Yes for signed_commits kind |
Used to enumerate commits between the previous tag and <version>. |
The detailed source-to-artifact mapping is documented in reference/source-mapping.md.
Outputs
All outputs live under evidence/<version>/ (relative to the consumer project root):
| Path | Format | Purpose |
|---|---|---|
evidence/<version>/manifest.yaml |
YAML | Top-level manifest. One entry per collected artifact. Schema: reference/manifest-schema.md. |
evidence/<version>/INTEGRITY |
Plain text | Single-line sha256:<hex> over the sorted manifest entries. Detect tampering of the manifest itself. |
evidence/<version>/<kind>/... |
Varies by kind | Per-artifact subdirectory. One subdirectory per kind value in the manifest. Layout per kind: see reference/source-mapping.md. |
The skill writes manifest.yaml and INTEGRITY atomically: each goes to a sibling *.tmp
file and is renamed on success. A failed run never leaves a half-written manifest.
Opt-in Gate
The skill is purely additive and must not break repos that have not adopted the regulated track.
if [[ ! -d evidence ]]; then
echo "evidence-pack: ./evidence directory absent -- skill is a no-op for this repo"
exit 0
fi
This check runs before any other phase. The exit code is 0 so CI invocations on
non-regulated repos do not fail. To opt in, a consumer project creates an empty evidence/
directory at the repo root (a .gitkeep is sufficient) and tracks the per-release subdirectories
that the skill produces.
Instructions
Phase 0: Validate Environment
- Confirm
evidence/exists at the repo root; if not, print the no-op message and exit 0. - Validate
<version>matches^[A-Za-z0-9._-]+$. If it contains a path separator, whitespace, or is empty, exit non-zero with a clear message. - If
evidence/<version>/already exists and--forceis not set, exit non-zero with the path and the suggestion to pass--force. - Resolve git HEAD (
git rev-parse HEAD) and the current tag (git describe --tags --exact-matchorgit rev-parse --short HEADas fallback). Record both for the manifest header. - Detect optional collectors:
ghonPATHand authenticated (probe withgh auth status).gitavailable and the current directory is a git work tree.sha256sum(Linux) orshasum -a 256(macOS) for checksumming. Missing optional collectors do not fail the skill; they downgrade the affected kinds to skipped-with-reason in the manifest.
Phase 1: Plan the Manifest
Reference: reference/manifest-schema.md defines the row shape of each manifest entry, and
reference/source-mapping.md defines per-kind collection commands and target subdirectories.
- Build the candidate kind list from the schema (
matrix,ci_run_log,pr_review,signed_commits,research_artifact,risk_file). - Apply
--includeif set: keep only kinds in the include list. - Apply
--excludeif set: remove kinds in the exclude list. - For each remaining kind, run the source mapping's "presence check" (e.g.
[[ -f docs/.index/traceability.yaml ]]formatrix). Kinds whose source is absent are recorded asskipped: source_absentin the manifest plan rather than triggering an error. - Emit the planned manifest (header + per-kind plan rows) to stdout.
Phase 2: Dry-run Exit Point
If --dry-run is set, exit zero immediately after Phase 1. Print the planned manifest as
YAML to stdout but do not create evidence/<version>/.
Phase 3: Collect Artifacts
For each kind that survived Phase 1 with a present source, run the collector defined in
reference/source-mapping.md and write the output under evidence/<version>/<kind>/. The
collector for each kind is documented there; the skill body must not invent new collection
strategies.
For each artifact written:
- Compute
sha256of the file (or, for directory-shaped kinds likerisk_fileorresearch_artifact, the sha256 of a deterministic listing produced byfind <dir> -type f | sort | xargs sha256sum). - Record
collected_atas the UTC ISO 8601 timestamp at which the collector finished. - Record
sourceas the original repo-relative path or thegh/gitcommand that produced the artifact (verbatim, including arguments). This is the audit trail. - Record
related_clausesas the list of clause IDs the artifact bears on (e.g. the matrix maps toIEC-62304-5.2.6andISO-13485-7.3.6; CI run logs map toIEC-62304-5.5.5andISO-13485-7.3.6). The mapping table lives inreference/source-mapping.md.
If a collector errors out (non-zero exit, network failure, permission denied), the entry is
recorded as failed: <message> rather than silently dropped. The overall skill exits
non-zero only when a collector fails for a kind that was explicitly requested via --include;
failures for default-collected kinds emit a warning but allow the pack to be produced
(the manifest will mark the kind as failed so an auditor can see the gap).
Phase 4: Write Manifest and Integrity File
Skip if --dry-run was already handled in Phase 2.
- Write
evidence/<version>/manifest.yaml.tmpwith the schema documented inreference/manifest-schema.md. Sort entries bykindthensourcefor stable diffs. - Compute
sha256over the canonical (sorted, no extra whitespace) manifest content. Write the result toevidence/<version>/INTEGRITY.tmpas a single line:sha256:<hex>\n. - On success, rename both
*.tmpfiles to their final names. On any error, delete the*.tmpfiles plus any partial per-artifact subdirectories that this invocation created and exit non-zero. (Existing artifacts underevidence/<version>/from a prior--forceoverwrite are left alone unless this invocation produced them.)
Phase 5: Report
Emit a summary block on stdout:
## Evidence Pack Generated
| Metric | Value |
|--------|-------|
| Version | <version> |
| Kinds collected | N |
| Kinds skipped | N (source_absent) |
| Kinds failed | N |
| manifest.yaml | X bytes |
| INTEGRITY sha256 | <16-char-prefix>... |
| Output | evidence/<version>/ |
Append a per-kind table when --dry-run was passed or when at least one kind failed.
Output
The skill produces the per-version evidence directory plus the on-stdout summary. No other files in the consumer project are modified -- in particular, the skill never edits source code, never runs builds, and never publishes to external systems. Submission to an external eQMS is explicitly out of scope (see issue #595 "Out of Scope").
Error Handling
| Condition | Action |
|---|---|
evidence/ parent absent |
No-op exit 0 (opt-in gate, see Phase 0). |
<version> argument missing or invalid |
Exit non-zero with usage hint. |
evidence/<version>/ exists and --force not set |
Exit non-zero with the existing path and --force suggestion. |
gh not authenticated (kinds ci_run_log, pr_review) |
Mark affected kinds as skipped: tool_unavailable; do not fail the skill. |
Collector for an --include-requested kind fails |
Exit non-zero after writing a partial manifest that records the failure. |
| Collector for a default-collected kind fails | Mark the kind as failed: <message> in the manifest; continue. |
| Write step fails (permission, disk) | Delete *.tmp files and any subdirectories this invocation created; report the path; exit non-zero. |
<version> already collected (without --force) |
See above; refusal is the correct behavior to preserve audit history. |
Policies
Side Effects and Loop-Safety
This skill is loop_safe: false. Each invocation produces an immutable per-release artifact
directory, and re-running for the same <version> either refuses (default) or destructively
overwrites (--force). Wrapping in /loop would either no-op after the first iteration or
churn the same directory; both outcomes are wrong.
The idempotency contract is "one pack per version" rather than "no-side-effect retry": existing packs are evidence in their own right and must not be silently replaced.
Command-Specific Rules
| Item | Rule |
|---|---|
| Inputs | Only existing artifacts produced by other tools (matrix from traceability, CI logs from gh, etc.). The skill never invents data. |
| Outputs | Only files under evidence/<version>/. No other directory is touched. |
| Opt-in | Absent evidence/ parent is a no-op exit 0, never a failure. |
| Atomicity | Manifest and integrity file go to *.tmp first, then rename on success. Failure deletes them. |
| Idempotency | Re-running for an existing <version> is refused unless --force is passed. |
| External submission | Never. Submission to eQMS / regulator portals is out of scope. |
| Cryptographic signing | Out of scope. The INTEGRITY checksum protects against accidental tampering only; it is not a signed attestation. |
How Other Components Use the Pack
| Consumer | Use |
|---|---|
| External auditor | Reads evidence/<version>/manifest.yaml and walks per-kind subdirectories. The INTEGRITY file lets them detect post-hoc edits to the manifest. |
release skill (future P2) |
Could invoke evidence-pack <version> automatically after a release tag is created. |
pr-work skill (future P2) |
Could surface "evidence pack updated" as part of release-candidate PR descriptions. |
traceability skill (sibling P0-1) |
Produces docs/.index/traceability.yaml which becomes the matrix artifact in this pack. |
References
- Manifest schema:
reference/manifest-schema.md - Per-kind source mapping:
reference/source-mapping.md - Sibling matrix producer:
global/skills/_internal/traceability/SKILL.md - Compliance clause source files:
compliance/iec-62304.md,compliance/iso-13485.md - Parent epic:
kcenon/claude-config#588 - Originating issue:
kcenon/claude-config#595