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
87 changes: 87 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,93 @@ All three workspace crates (`evidence-core`, `cargo-evidence`,
`evidence-mcp`) share a single version; release entries cover all
three unless noted.

## [Unreleased] — 0.1.4 hygiene track

### Added

- **`success: bool` field on every MCP wire shape**
(`JsonlToolResponse`, `RulesToolResponse`, `DiffToolResponse`).
Derived deterministically from `exit_code` + `terminal`/`error`
so MCP hosts have one canonical pass/fail field rather than
pattern-matching the colliding `exit_code = 2` against the
`_OK`-suffix string. Existing fields are unchanged; older
agents ignore the addition. (HLR-067 / LLR-074 / TEST-081)
- **`cargo-evidence --help` now lists every subcommand** when the
binary is invoked directly (`cargo-evidence --help`, not
`cargo evidence --help`). Reuses clap's render tree on
`EvidenceArgs`, so new subcommands appear automatically. The
redirect-stub form is retired. An `EXPECTED_SUBCOMMANDS` list
in the test guards drift. (HLR-068 / LLR-075 / TEST-082)
- **`editor_duplicates_locked` integration test** — mechanical
guard against ` N.<ext>` editor-duplicate filenames
(`helpers 2.rs`, `tests 2.toml`, `audit 2.yml`, etc.) anywhere
in the workspace tree. Three fixtures: clean-tree regression,
positive dogfood (synthetic `helpers 2.rs`), negative dogfood
with two distinct classes (`mcdc_2024.rs` four digits +
`mcdc_24.rs` two digits, no leading space) so a regression in
either the digit-count cap or the leading-space anchor fails
independently. (SYS-029 / HLR-069 / LLR-076 / TEST-083)
- **DAL-A MC/DC fail-loud** at cert / record profile. When any
in-scope crate is at DAL-A and `cert/boundary.toml` lacks a
`[dal.auxiliary_mcdc_tool]` table, generate emits
`BOUNDARY_DAL_A_MISSING_AUXILIARY_MCDC` and refuses to assemble
a bundle. Dev profile keeps the warn-and-continue behavior so
iterative work isn't blocked. Closes the silent-underclaim
sharp-edge an auditor would catch but a careless DER could
miss. (HLR-066 / LLR-073 / TEST-080)
- **`AuxiliaryMcdcTool` schema hook** under
`[dal.auxiliary_mcdc_tool]` carrying `name` (required),
optional `qualification_id`, optional bundle-relative
`report` path. Lets a DAL-A project declare LDRA TBvision /
VectorCAST / Rapita RVS evidence by reference today, without
waiting for stable Rust MC/DC support.
- **`CONTRIBUTING.md`** — PR loop, trace-first convention, local
CI gates, floors-only-up rule, style snapshots. Points at
`cert/trace/README.md` for the canonical UUID-generation
workflow.
- **`CODEOWNERS`** — single-maintainer default with cert-track
paths called out separately so future role splits are
mechanical.
- **`crates/cargo-evidence/src/cli/README.md`** — lifecycle
taxonomy of the 24 CLI files (bundle-producing /
bundle-consuming / source-tree-inspection / self-describing)
with an adding-a-new-verb checklist.
- **`editor-duplicate gate` surface** in `KNOWN_SURFACES`,
claimed by HLR-069.

### Changed

- **Trace discovery is now a single-source-of-truth function**:
`evidence_core::trace::default_trace_roots(workspace_root)` is
the only path consulted by `cargo evidence trace --validate`,
`cargo evidence check`, `cargo evidence floors`, and
`evidence_core::floors::count_trace_per_layer`. Replaces three
separate callsites that each implemented discovery
inconsistently and would silently under-count when the project
used a non-canonical trace location. Discovery order: `cert/
trace/` (canonical) → `cert/boundary.toml`'s `scope.trace_roots`.
No `tool/trace/` fallback; the project's self-trace lives at
`cert/trace/` to match the convention.
- **README content_hash section** explicitly distinguishes
"integrity re-check" from "DO-178C verification independence",
and forward-references the existing **Tool Qualification
Level** honesty section so a casual reader can't conflate the
two.

### Floors

| Dimension | 0.1.3 → unreleased |
|---|---|
| trace_sys | 28 → 29 |
| trace_hlr | 65 → 69 |
| trace_llr | 72 → 76 |
| trace_test | 77 → 81 |
| diagnostic_codes | 150 → 151 |
| known_surfaces | 21 → 22 |
| per_crate.evidence-core.test_count | 351 → 361 |
| per_crate.evidence-mcp.test_count | 37 → 45 |
| per_crate.cargo-evidence.test_count | 133 → 136 |

## [0.1.3] — 2026-04-30

### Added
Expand Down
25 changes: 25 additions & 0 deletions CODEOWNERS
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# CODEOWNERS for cargo-evidence.
#
# Single-maintainer project today. Default owner gets review
# requests on every PR. As the contributor base grows, narrow
# this file to per-directory ownership rather than expanding the
# global wildcard.
#
# Cert-track files (boundary, floors, trace) are called out
# separately so a future split between code review and
# certification review is mechanical: these paths own the
# DO-178C/DO-330 evidence shape, and a change there warrants a
# cert-aware reviewer regardless of how the codebase grows.

* @luofang34

# Cert-track configuration
/cert/ @luofang34
/cert/boundary.toml @luofang34
/cert/floors.toml @luofang34
/cert/trace/ @luofang34

# Workflow + repo-stewardship files
/.github/ @luofang34
/CODEOWNERS @luofang34
/CONTRIBUTING.md @luofang34
142 changes: 142 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# Contributing to cargo-evidence

This project ships software-of-interest (SoI) for DO-178C / DO-330
certification evidence. Every change has to clear the same gates a
downstream cert program would expect: trace coverage, ratchet floors,
deterministic bundles. The PR loop below mechanizes those gates.

If anything below is wrong or unclear, open an issue or PR — the
guidance evolves with the toolchain.

---

## PR loop

A landed PR carries:

1. **A trace-seed commit** (when adding a behavior an auditor would
review): SYS / HLR / LLR / TEST entries under `cert/trace/`,
added in the first commit on the branch, before any
implementation. The trace chain is the contract; the code
implements it. See [Trace-first convention](#trace-first-convention)
below.
2. **An implementation commit** that satisfies the seed.
3. **A guardrail in the same PR** — a test, lint, or CI check that
prevents this same regression from recurring. A fix without a
guardrail is temporary.

Both commits must be locally CI-clean before pushing. The PR landing
that's "almost green" tomorrow gets reverted today.

## Local CI

The minimum command set, mirroring `.github/workflows/ci.yml`:

```bash
cargo fmt --check
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace --all-targets
RUSTDOCFLAGS='-D missing_docs -D rustdoc::broken_intra_doc_links' \
cargo doc --workspace --no-deps
cargo build --release --workspace
cargo run -p cargo-evidence -- evidence trace --validate
cargo run -p cargo-evidence -- evidence floors --format=jsonl
```

The trace + floors gates are project-internal — they catch most
self-cert regressions before they reach CI. Don't skip them.

## Trace-first convention

Default: every PR seeds its SYS/HLR/LLR/TEST entries in the first
commit on the branch, before any implementation. UUIDs are **never
hand-crafted** (even with valid v4 syntax) and **never generated
externally** (e.g., a one-liner Python script): the tool's own
`cargo evidence trace --backfill-uuids` is the single authoritative
generator. The full rationale lives in `cert/trace/README.md`'s
"UUID policy" section.

Workflow for a new entry:

1. Append the entry to the appropriate trace file
(`cert/trace/sys.toml` / `hlr.toml` / `llr.toml` / `tests.toml`)
**without** a `uid` field. Set `traces_to` to point at the
parent layer's UID — those already exist in the file.
2. Run `cargo evidence trace --backfill-uuids` from the workspace
root. Discovery picks `cert/trace/` automatically; no
`--trace-roots` flag needed.
3. Commit the populated TOML.

Re-runs are no-ops; the `trace-self-validate` CI job asserts
backfill reports "all entries already have UUIDs", catching an
uncommitted backfill step before it reaches main.

Each trace seed bumps the matching counter in `cert/floors.toml`
(`trace_sys`, `trace_hlr`, `trace_llr`, `trace_test`). After the
implementation commit lands its tests, also bump the affected
`per_crate.<crate>.test_count` row to match the new measurement.
The `floors_equal_current_no_slack` test enforces equality, not
inequality — drift either way fails CI.

Exception — bidirectional contracts spanning two PRs: when a single
SYS-level claim covers both directions of a contract (e.g., forward-
enrichment in one PR + reverse-verification in a follow-up), the
*second* PR seeds the chain for both halves. The first PR ships
under an implicit trace obligation; the second PR's chain-seed
discharges it for both. This is rare — only legitimate when the two
halves form one logical deliverable and splitting the chain would
force referencing UUIDs that don't yet exist.

## Floors are ratchet-only

`cert/floors.toml` is a one-way gate: `current >= committed_floor`
on every dimension. The `current_measurements_satisfy_committed_floors`
test enforces this; the companion `floors_equal_current_no_slack`
test enforces `current == committed_floor` (no slack — a slack
floor lets a later PR delete things along that dimension without
firing the gate).

Lowering a floor requires either:

- Rare and rejustified: a `Lower-Floor: <dimension> <reason>` line
in the PR body or commit message, OR
- A schema break: bump `schema_version` in the file header.

Default expectation: floors only go up.

## What lands together, what stays separate

- **One issue per PR.** Break large refactors into independently
revertible steps. A PR that fixes two unrelated bugs is harder to
review and harder to revert.
- **No mixed reformatting.** `cargo fmt` results land as their own
commit, never co-mingled with logic changes.
- **No silent dependency adds.** Each new workspace dep gets a
one-line note in the commit body (why it's needed, what it
costs).
- **No unsafe.** `unsafe_code` is `forbid`-level workspace-wide.

## Style snapshots

The full style guide lives in `CLAUDE.md` at the repo root. The
high-impact rules:

- **Max 500 lines per `.rs` file.** Locked tests count too — split
to a sibling module before the limit, not after.
- **No `mod.rs`.** Use `foo.rs` + `foo/` directory pattern.
- **No `eprintln!` / `println!` for diagnostics** — use `tracing`
(`info`, `warn`, `error`, `debug`).
- **No `unwrap` / `expect` / `panic!` in library code.** Tests
may opt out via `#[allow(clippy::expect_used, clippy::panic)]`.
- **WHY-only comments.** No PR-number breadcrumbs, no absolute
line counts, no temporal phrasing (`migrated from`, `previously`).
These are mechanically enforced by `rot_prone_markers_locked`.
- **No editor-duplicate filenames** (`* 2.rs`, `* 2.toml`, …).
Mechanically enforced by `editor_duplicates_locked`.

## Reporting

Open an issue at <https://github.com/luofang34/Evidence/issues>.
Security-relevant findings: please prefer a private channel
first — the project has no formal security disclosure policy yet
but will respond to good-faith reports.
12 changes: 6 additions & 6 deletions cert/floors.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,20 @@ diagnostic_codes = 151
terminal_codes = 13

# cert/trace/sys.toml — System Requirements.
trace_sys = 28
trace_sys = 29
# cert/trace/hlr.toml — High-Level Requirements.
trace_hlr = 68
trace_hlr = 69
# cert/trace/llr.toml — Low-Level Requirements.
trace_llr = 75
trace_llr = 76
# cert/trace/tests.toml — Test Cases.
trace_test = 80
trace_test = 81

# evidence_core::trace::surfaces::KNOWN_SURFACES length — hand-curated
# catalog of CLI verbs + named observable contracts. Matching HLR
# coverage is enforced by `require_hlr_surface_bijection`; this floor
# guards against silently shrinking the catalog itself (which would
# relax the bijection without firing the check).
known_surfaces = 21
known_surfaces = 22

# --------------------------------------------------------------------
# Per-crate-true dimensions: one table per in-scope crate.
Expand All @@ -78,7 +78,7 @@ known_surfaces = 21

[per_crate.evidence-core]
# `#[test]` attribute count inside crates/evidence-core/**/*.rs.
test_count = 358
test_count = 361

[per_crate.cargo-evidence]
test_count = 136
Expand Down
43 changes: 43 additions & 0 deletions cert/trace/hlr.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1654,3 +1654,46 @@ The intercept should reuse that render tree, not paper over it.
verification_methods = ["test"]
traces_to = ["385c2c4c-748c-4486-bd8c-7622b12c9273"]
surfaces = ["agent MCP surface"]

[[requirements]]
uid = "202c2281-f2d7-4e43-8a6e-93da96f92add"
id = "HLR-069"
title = "Repository contains no editor-duplicate artifacts (` N.<ext>` filenames)"
owner = "tool"
scope = "component"
description = """
A mechanical gate refuses any path under the repository whose
basename matches the editor-duplicate pattern: stem + ` ` (single
ASCII space) + small-integer suffix + extension, for any of the
extensions the project recognizes as cert-relevant
(`.rs`, `.toml`, `.yml`, `.yaml`, `.md`, `.json`, `.lock`).

Concrete pattern, fixed in the test:

- Regex (basename, anchored): `^.+ ([0-9]{1,2})\\.(rs|toml|yml|yaml|md|json|lock)$`.
- Walks `crates/**`, `cert/**`, `tool/**`, `.github/**`, plus
repo-root `*.md` / `*.toml`. Excludes `target/`, `.git/`,
`node_modules/`, `fixtures/`, `cert/trace/` (audit journal).
- Also walks `.git/refs/` for branch-name duplicates (the
`main 2` ref artifact a misbehaving git client occasionally
leaves behind).

Failure mode the gate prevents: a file like
`crates/cargo-evidence/tests/cli 2.rs` lands silently, compiles
into the test binary, inflates `#[test]` counts seen by the
floors gate, and survives in-tree until a manual sweep catches
it. The user's auto-memory captures the same trap
(`project_stray_2rs_artifacts.md`) — promoting it to a project-
level mechanical gate makes the rule observable in CI rather
than relying on memory recall.

Reserved-text-style escape hatch: the test exposes a
`RESERVED_DUPLICATE_PATHS` const, initially empty. An editor
artifact that is genuinely load-bearing (say, a third-party
file the project consumes whose name happens to match the
pattern) gets an entry here with written justification. The
default state is zero exemptions.
"""
verification_methods = ["test"]
traces_to = ["4b63a601-9770-4c3a-bfe4-e1d70e050c65"]
surfaces = ["editor-duplicate gate"]
43 changes: 43 additions & 0 deletions cert/trace/llr.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2409,3 +2409,46 @@ The intercept lives at the top of `main()` (before the
errors out.
"""
verification_methods = ["test"]

[[requirements]]
uid = "f634ed35-6c3e-4d71-90a2-dbaee1d086ab"
id = "LLR-076"
title = "editor_duplicates_locked walks the repo and fires on ` N.<ext>` filenames"
owner = "tool"
traces_to = ["202c2281-f2d7-4e43-8a6e-93da96f92add"]
modules = ["evidence_core::tests::editor_duplicates_locked"]
derived = false
description = """
Integration test under `crates/evidence-core/tests/`. Walks the
workspace from `CARGO_MANIFEST_DIR`'s grandparent (the repo root)
using `walkdir::WalkDir::new(root).follow_links(false)`, prunes
the conventional skip directories
(`target`, `.git`, `node_modules`, `fixtures`), and emits a
collected list of every basename matching the pinned regex:

```text
^.+ ([0-9]{1,2})\\.(rs|toml|yml|yaml|md|json|lock)$
```

The leading-space-then-digit anchor matters: it specifically
catches the `cp old.rs 'old 2.rs'` artifact pattern without
flagging legitimate filenames that happen to contain a number
(e.g. `mcdc_2024.rs` is fine because there's no space before
the digits).

Test fails via `assert!` with a complete file:line listing for
every offending entry. Mirrors the failure shape of
`rot_prone_markers_locked` — the test's failure message is the
diagnostic; no `Diagnostic` wire shape, no `RULES` entry.

Exemption escape hatch: `RESERVED_DUPLICATE_PATHS` const in the
test module names workspace-relative paths (no glob; suffix
match) where the editor-duplicate-shaped name is actually
intentional. Initially empty. Each entry requires written
justification beside the const.

Cross-platform note: walks paths via `walkdir` and normalizes
separators to `/` before suffix-matching the reserved list, per
the `project_path_separator_on_windows` memory.
"""
verification_methods = ["test"]
Loading
Loading