feat(0045): evidence merge — policy-driven pack merging (merge-rules) - #10
Merged
Merged
Conversation
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
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BMuEZ1CZDLE9jjhmrNMNw6
…t/skip Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BMuEZ1CZDLE9jjhmrNMNw6
…ning Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BMuEZ1CZDLE9jjhmrNMNw6
…coverage/metrics namespacing 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
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
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BMuEZ1CZDLE9jjhmrNMNw6
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
New
evidence mergecommand: 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
finalizederives (totals, hashes, root failure index,ended) and seals;validatechecks. No derived artifact has two producers.Policy (merge-rules.yaml, schema-validated)
require_status(defaultfinalized) →require_valid(defaultL0) → generic{file: run.yaml, key, must, on_violation}rules.on_ineligible: abort | skip. Sequential eligibility: rules compare only against previously-eligible packs;must: sameanchors on the first eligible pack;must: differentkeeps first occurrence (dedupes double-submitted shards). Absent==absent is same; absent-vs-present is different.result.yamlkey rules fire first, elsetests.on_collision—error(strict default) |prefer_first|prefer_latest(laterended, fallbackstarted, tie→CLI order) |discard(tombstones the id). Tests are atomic: the winner's whole tree travels.--rulesomitted: finalized-only, L0-valid, abort on anything odd, collisions error.Merged run.yaml
--run-idmandatory (identity is the caller's fact; merge is fully deterministic — no clock/randomness).title= first eligible pack's (--titleoverrides) ·started= min · noended/totals(finalize derives;--finalizeseals withendedAt = 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_fromlineage (the additive slice of 0014) · coverage nestedcoverage/<i>-<run_id>/· rootfailure.yamlnot 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 optionalmerged_fromsrc/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)mergecommand +MergeReportreporter output; commanderexitOverrideroutes its own failures to exit 2Test plan
tsc --noEmitcleanrequire_*, anchor/dedupe/sequential-eligibility, per-rule override, zero/one-eligible--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)validate --profile L1green🤖 Generated with Claude Code