AUR-479 -- Split secrets across context providers
Outcome
A registered secret does not reach the model by being divided between two
context contributions. Before this card, filter.Redact ran per
contribution (internal/config/provider.go) before the contributions
were concatenated, and never over the assembled block. Two providers whose
texts each contained half of a secret therefore produced a final prompt
holding the whole secret verbatim. This is reachable today with two file
providers (.aurumcode/prompt.md plus a configured docs/skill file);
AUR-470's RAG slicing can also cut a secret in half by accident.
Fix
BuildContextBlockWithWarnings now redacts twice:
- each non-empty contribution is passed through the AUR-009
*redaction.Filterbefore it is appended, keeping defense in depth at the source; and - the newline-joined contributions are redacted once more as a single block, which is what makes a secret split across two contributions visible.
The assembled block is redacted exactly once, not on every partial concatenation, so the cost stays proportional to the final block. The AUR-009 filter itself is unchanged; it simply sees the assembled block too. Provider names are rendered separately so the redaction input preserves a plain newline boundary between contributions -- a joined pair of halves is still a contiguous match for the filter's line-break-tolerant registered secret rule.
Acceptance
tests/acceptance/AUR-479.sh is the program. The container profile is
bootstrap-readonly-v1 (Go + Bash image, network none).
AC-001-- a secret split across two contributions does not appear verbatim in the final prompt.AC-002-- a whole secret confined to one contribution is still redacted, and the per-contribution pass is load-bearing.AC-003-- a secret-shaped span that only exists across the boundary is redacted without destroying the legitimate prose around it.
Selectors: AC-001, AC-002, AC-003, TestAUR479,
IntegrationAUR479, E2EAUR479, MUT-001, MUT-002, all (runs every
lane). An unknown selector exits 64.
Skeptical mutations
MUT-001: remove the assembled-block redaction (redact only per contribution).AC-001goes RED because the split secret is reconstituted.MUT-002: remove the per-contribution redaction (trust only the assembled block).AC-002goes RED on the overlapping-registered-value case, where containing the whole value inside its own contribution before the join is what preservesabc\n[REDACTED].
Both mutation selectors apply the change to a staged copy of the source and restore nothing in the repository; the nominal run is the GREEN.
Exit codes
0 property holds; 1 behavioral RED (including a surviving mutant);
64 unknown selector; 79 infrastructure/inconclusive.
Rollback
Revert internal/config/provider.go and the AUR-479 test/spec files. No
external state is published.