Skip to content

feat(boundary): DAL-A MC/DC fail-loud at cert/record; warn at dev#115

Merged
luofang34 merged 2 commits intomainfrom
pr/dal-a-mcdc-fail-loud
May 1, 2026
Merged

feat(boundary): DAL-A MC/DC fail-loud at cert/record; warn at dev#115
luofang34 merged 2 commits intomainfrom
pr/dal-a-mcdc-fail-loud

Conversation

@luofang34
Copy link
Copy Markdown
Owner

Summary

Closes the v0.1.3 review's #1 actionable item (the DAL-A MC/DC fail-loud).

Stable Rust cannot emit MC/DC instrumentation today — the unstable -Zcoverage-options=mcdc flag was removed by rust-lang/rust#144999 (merged 2025-08-08), and rust-lang/rust#124144 has no active reimplementation. The only viable DAL-A path today is to record an external qualified tool's evidence (LDRA TBvision, VectorCAST, Rapita RVS, etc.) by reference.

Pre-this-PR sharp edge: a DAL-A project could run cargo evidence generate --profile cert and get a bundle whose terminal was `VERIFY_OK` while `compliance/.json`'s A7-10 row reported `NotMet`. A careful auditor catches it; a careless DER signs off. This PR closes that asymmetry.

What changes

Layer Change
evidence_core::policy::dal New public type AuxiliaryMcdcTool (name, optional qualification_id, optional report) read from boundary.toml's [dal.auxiliary_mcdc_tool] table. New optional field on DalConfig.
evidence_core::boundary_check New check_dal_a_mcdc_evidence(dal_map, auxiliary_mcdc_tool) returning Ok(()) when no DAL-A in scope OR tool reference present, otherwise BoundaryCheckError::DalAMissingAuxiliaryMcdc with code BOUNDARY_DAL_A_MISSING_AUXILIARY_MCDC.
cargo_evidence::cli::generate::policy New enforce_dal_qualification runs after enforce_boundary_policy. Cert/record: hard fail with offender list + 3-path remediation prose. Dev: warning:-prefixed message + continue.
trace HLR-066 → LLR-073 → TEST-080 (3 unit + 1 integration selector).

Backwards compatibility

Existing projects without DAL-A in scope are unaffected — the gate fires only when at least one in-scope crate is Dal::A.

Existing DAL-A projects with no [dal.auxiliary_mcdc_tool] entry will start failing on cert/record. That's the intended behavior; the previous silent under-claim was the bug.

Refactors (workspace 500-line limit)

  • boundary_check.rs cargo-metadata struct subset extracted to boundary_check/metadata.rs.
  • The dev-warns integration test lives in its own integration-test file tests/dal_qualification_gate.rs.

Floors ratchet

Floor Was Now
diagnostic_codes 150 151
trace_hlr 65 66
trace_llr 72 73
trace_test 77 78
per_crate.evidence-core.test_count 351 355
per_crate.cargo-evidence.test_count 133 134

Test plan

  • `cargo fmt --check` — clean
  • `cargo clippy --workspace --all-targets -- -D warnings` — clean
  • `cargo test --workspace --all-targets` — green (4 new unit, 1 new integration)
  • `RUSTDOCFLAGS='-D missing_docs -D rustdoc::broken_intra_doc_links' cargo doc --workspace --no-deps` — green
  • `cargo build --release` — green
  • `cargo run -p cargo-evidence -- evidence trace --validate` → `TRACE_OK`
  • `cargo run -p cargo-evidence -- evidence floors --format=jsonl` → `FLOORS_OK`

Note on the trace-roots discovery bug (separate PR)

While reviewing this work, a separate architectural issue was identified: `evidence_core::floors::count_trace_per_layer` hardcodes `tool/trace/`, while `check` and `trace --validate` use `default_trace_roots` (which walks both `tool/trace` and `cert/trace`). A downstream project with traces under `cert/trace/` gets `current=0` from `floors` silently. That fix touches a public lib API, has higher blast radius, and deserves its own focused PR — landing right after this one.

🤖 Generated with Claude Code

Sokoly2024 added 2 commits May 1, 2026 17:30
…n gate

Trace-first seed for the DAL-A fail-loud feature in the
follow-up commit on this branch.

HLR-066: at cert/record profile, generate refuses to assemble a
bundle when an in-scope crate is at DAL-A and the project's
boundary.toml does not record an auxiliary qualified MC/DC tool
under `[dal.auxiliary_mcdc_tool]`. Rationale: stable Rust cannot
emit MC/DC instrumentation today (rust-lang/rust#144999 removed
the unstable `-Zcoverage-options=mcdc` flag on 2025-08-08;
tracking issue rust-lang/rust#124144 has no active
reimplementation), so the only viable DAL-A path is to record an
external qualified tool's evidence by reference (LDRA, VectorCAST,
Rapita RVS). Without that reference the bundle would silently
underclaim DO-178C Annex A Table A-7 Obj-7 — a known sharp edge
an auditor would catch but a careless DER might miss. Traces to
SYS-004 (policy-gated emission). Dev profile downgrades to a
stderr warning + continue.

LLR-073: `evidence_core::check_dal_a_mcdc_evidence(dal_map,
auxiliary_mcdc_tool)` returns Ok(()) when no in-scope crate is at
DAL-A OR an `AuxiliaryMcdcTool` reference is present, otherwise
`BoundaryCheckError::DalAMissingAuxiliaryMcdc { dal_a_crates,
count }` with code BOUNDARY_DAL_A_MISSING_AUXILIARY_MCDC.
Library is policy-free; the dev/cert severity split lives at the
CLI layer in `enforce_dal_qualification` called from cmd_generate
right after enforce_boundary_policy.

TEST-080: four selectors covering the full decision matrix —
library passes when no DAL-A in scope; library passes when
auxiliary tool set; library returns sorted offender list when
DAL-A is in scope without a tool reference; integration test
verifies dev profile emits a `warning:`-prefixed message and
does not abort on the gate's own envelope.
Closes the v0.1.3 review's #1 item (DAL-A fail-loud on missing
MC/DC). Stable Rust cannot emit MC/DC instrumentation
(rust-lang/rust#144999 removed the unstable flag on 2025-08-08;
rust-lang/rust#124144 has no active reimplementation), so the
only viable DAL-A path today is to record an external qualified
tool's evidence by reference. Pre-this-PR, a DAL-A bundle could
emit `VERIFY_OK` while `compliance/<crate>.json` reported
A7-10 (MC/DC) as `NotMet` — a careful auditor catches it; a
careless DER signs off. This PR makes the gap fail loud.

Library:
  - `evidence_core::AuxiliaryMcdcTool` (public): `name`,
    optional `qualification_id`, optional `report` (bundle-
    relative path). Reads from `boundary.toml`'s
    `[dal.auxiliary_mcdc_tool]` table.
  - `evidence_core::check_dal_a_mcdc_evidence(dal_map,
    auxiliary_mcdc_tool)` returns Ok(()) when no DAL-A in scope
    OR a tool reference is present; otherwise
    `BoundaryCheckError::DalAMissingAuxiliaryMcdc { dal_a_crates,
    count }` with code `BOUNDARY_DAL_A_MISSING_AUXILIARY_MCDC`
    (Domain::Boundary, Severity::Error).
  - The library is policy-free — dev/cert severity split lives
    at the CLI layer.

CLI:
  - `enforce_dal_qualification` runs from cmd_generate after
    enforce_boundary_policy. Cert/record: standard failure
    envelope (`fail` helper) + Ok(Some(EXIT_ERROR)). Dev:
    `warning: DAL-A qualification gap...` to stderr + Ok(None);
    iteration continues unblocked.
  - Error message names every offender, lists three remediation
    paths (record auxiliary tool, lower DAL, wait for upstream),
    and cites both upstream issues so the auditor can verify
    the rustc state independently.

Trace + tests:
  - HLR-066 traces to SYS-004 (policy-gated emission).
  - LLR-073 traces to HLR-066, emits the new code.
  - TEST-080: 3 unit + 1 integration selector.

Refactor:
  - `boundary_check.rs` cargo-metadata struct subset extracted
    to a sibling `boundary_check/metadata.rs` (the new variant
    + check function pushed the parent over the workspace
    500-line limit).
  - The integration test for the dev-warns path lives in its
    own integration-test file `tests/dal_qualification_gate.rs`
    (cli.rs hit the same 500-line limit).

Floors ratchet (post-0.1.3 baseline 150/65/72/77/133/351):
  - diagnostic_codes 150 → 151 (+1: BOUNDARY_DAL_A_MISSING_AUXILIARY_MCDC)
  - trace_hlr 65 → 66 (HLR-066)
  - trace_llr 72 → 73 (LLR-073)
  - trace_test 77 → 78 (TEST-080)
  - per_crate.evidence-core.test_count 351 → 355 (+4 unit)
  - per_crate.cargo-evidence.test_count 133 → 134 (+1 integration)

Golden fixture regenerated for the new code.
@luofang34 luofang34 merged commit 7f4f5c2 into main May 1, 2026
28 of 30 checks passed
@luofang34 luofang34 deleted the pr/dal-a-mcdc-fail-loud branch May 1, 2026 21:42
luofang34 pushed a commit that referenced this pull request May 2, 2026
…rface claim, CHANGELOG

- **Blocker 1**: CONTRIBUTING.md's UUID workflow now points at the
  established `cargo evidence trace --backfill-uuids` policy and
  cross-references `cert/trace/README.md`'s rationale. The
  python3 one-liner the prior draft suggested was the exact
  hand-crafting form the existing policy bans (collision risk,
  weakens the "UUIDs are opaque" contract, tempts reading meaning
  into digits). Two contradictory authority docs on the same
  workflow is a quality-of-process regression — fixed.

- **Blocker 2**: Sharpened the negative dogfood. The previous test
  used `mcdc_2024.rs`, which fails the duplicate regex for two
  independent reasons (no leading space + 4 digits exceeds the
  2-digit cap). A regression that drops only the leading-space
  anchor would still pass. Added `mcdc_24.rs` (2 digits, no
  leading space) so each anchor is isolated by its own fixture.

- **Minor 3**: HLR-069 now claims `surfaces = ["editor-duplicate
  gate"]`, matching the convention HLR-044 / HLR-046 / HLR-047
  established for sibling hygiene gates. New entry in
  `KNOWN_SURFACES`. `cert/floors.toml` known_surfaces 21→22.

- **Minor 5**: 0.1.4 hygiene-track entry added to CHANGELOG. Bundles
  the changes already in main from PRs #115 / #116 / #117 plus the
  changes landing in this PR. Per-PR cadence rather than batched
  release-prep PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

2 participants