Skip to content

feat(0045): evidence merge — policy-driven pack merging (merge-rules) - #10

Merged
siddhant573 merged 10 commits into
LambdaTest:mainfrom
siddhant573:feat/merge-command
Jul 8, 2026
Merged

feat(0045): evidence merge — policy-driven pack merging (merge-rules)#10
siddhant573 merged 10 commits into
LambdaTest:mainfrom
siddhant573:feat/merge-command

Conversation

@siddhant573

Copy link
Copy Markdown
Collaborator

Summary

New evidence merge command: combine N packs (sealed zips or live directories) into one under a declarative merge-rules policy — governed by new decision 0045 (committed first; governance 0001).

evidence merge shard-*.evidence --run-id nightly-2026-07-08 \
    --rules merge-rules.yaml -o merged.evidence [--finalize]

Role separation: merge assembles a live pack; the existing finalize derives (totals, hashes, root failure index, ended) and seals; validate checks. No derived artifact has two producers.

Policy (merge-rules.yaml, schema-validated)

  • Pack gates, cheap→expensive: manifest → version → require_status (default finalized) → require_valid (default L0) → generic {file: run.yaml, key, must, on_violation} rules. on_ineligible: abort | skip. Sequential eligibility: rules compare only against previously-eligible packs; must: same anchors on the first eligible pack; must: different keeps first occurrence (dedupes double-submitted shards). Absent==absent is same; absent-vs-present is different.
  • Collisions (same test id): result.yaml key rules fire first, else tests.on_collisionerror (strict default) | prefer_first | prefer_latest (later ended, fallback started, tie→CLI order) | discard (tombstones the id). Tests are atomic: the winner's whole tree travels.
  • Strict defaults when --rules omitted: finalized-only, L0-valid, abort on anything odd, collisions error.

Merged run.yaml

--run-id mandatory (identity is the caller's fact; merge is fully deterministic — no clock/randomness). title = first eligible pack's (--title overrides) · started = min · no ended/totals (finalize derives; --finalize seals with endedAt = max(source ended)) · metrics namespaced by flattening <i>-<run_id>/<name> (survives the 0012 typed shape) · environment: common subset stays run-level, divergent keys pushed down per test (0043's lossless merge; per-test values win) · merged_from lineage (the additive slice of 0014) · coverage nested coverage/<i>-<run_id>/ · root failure.yaml not copied — finalize regenerates it from the merged union (0044 guarantees hold by construction).

Exit codes: 0 merged (sanctioned skips included, reported in MergeReport) · 1 policy abort · 2 usage.

Changes

  • design/decisions/0045-merge-command-and-rules.md (+ 03-commands.md merge section, features.yaml, CHANGELOG)
  • src/schemas/merge-rules.schema.json (tool input, beside the versioned tree); run schema gains optional merged_from
  • src/merge/rules.ts (loader + pinned must-semantics), gates.ts, collide.ts, assemble.ts, index.ts (orchestrator), testkit.ts (staging helpers)
  • PackContainer.readFileBytes(rel) on all three container forms (dir/zip/remote)
  • CLI merge command + MergeReport reporter output; commander exitOverride routes its own failures to exit 2

Test plan

  • TDD throughout — every module red→green; 164/164 passing, tsc --noEmit clean
  • Gates: each require_*, anchor/dedupe/sequential-eligibility, per-rule override, zero/one-eligible
  • Collisions: all four actions, tie-break, 3-way tombstone, rule-before-default, fall-through
  • Assembly: min-started, title override, metrics flattening (eligible-only ordinals), env subset+push-down (per-test wins), whole-tree byte-copy, coverage nesting, refuse-overwrite, no derived artifacts
  • End-to-end --finalize: seals with max(source ended); regenerated failure index reflects a discarded loser; live pack validates clean at L1 (running), sealed pack at L1 (finalized)
  • CLI smoke against staged shards: human report, sealed zip inspected, validate --profile L1 green

🤖 Generated with Claude Code

siddhant573 and others added 10 commits July 8, 2026 17:14
…coverage/metrics namespacing

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BMuEZ1CZDLE9jjhmrNMNw6
Commander exitOverride routes its own failures (missing --run-id) to the
usage exit code (2); help/version stay 0. Merge outcomes: 0 merged, 1
policy abort, 2 usage.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BMuEZ1CZDLE9jjhmrNMNw6
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BMuEZ1CZDLE9jjhmrNMNw6
@siddhant573
siddhant573 merged commit 984d519 into LambdaTest:main Jul 8, 2026
1 check passed
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.

1 participant