Development record
ultra-v0.6.8 altitude 2: the opt-in gitleaks augmentation is bolted onto history.Capture alone (internal/core/history/history.go), while the other write paths that build scanner.New — capture, memory ingest, launch dry-run, repolint, the CLI scan — never see it. Deeper fix: fold the gitleaks adapter into internal/adapter/scanner so scanner.New reads the opt-in, ScanText/ScanBundle return the union, and ErrConfiguredNotFound surfaces as the existing Unavailable state; every consumer then inherits it.
issueresolvedminoriss-2608291814575788
ultra-v0.6.8 altitude 2: the opt-in gitleaks augmentation is bolted onto history.Capture alone (internal/core/history/history.go), while the other write paths that build scanner.New — capture, memory ingest, launch dry-run, repolint, the CLI scan — never see it. Deeper fix: fold the gitleaks adapter into internal/adapter/scanner so scanner.New reads the opt-in, ScanText/ScanBundle return the union, and ErrConfiguredNotFound surfaces as the existing Unavailable state; every consumer then inherits it.
Remedy grounds (2026-09-29)
- Why: the design is ruled in dcn-2609250000000025 (2026-09-25), so the remedy applies it; it supersedes the body's proposal to fold the adapter into internal/adapter/scanner, which the ruling shows is an import cycle.
- Rejected: keeping the augmentation on history.Capture alone, which leaves every other scanner consumer without the opt-in the repository armed.
Grounds
- pursued: every scanner consumer reports a fake augmenter's finding and behaves on the not-found gap as the 2026-09-25 ruling says (tests per consumer); a consumer that builds a scanner and neither reports an armed augmenter's finding nor names its gap would show it wrong
Frontmatter
| id | iss-2608291814575788 |
|---|---|
| type | issue |
| lifecycle | resolved |
| severity | minor |
.abcd/work/issues/resolved/iss-2608291814575788-gitleaks-augmentation-is-history-only.md
open on GitHub ↗ · commit history ↗
mentions3
- dcn-26092500000000252026-09-25 — iss-2608291814575788 (the opt-in gitleaks augmentation reaches `history.Capture` alone) is answered by inverting the import edge, not by folding the adapter into `internal/adapter/scanner` as the record proposes: `internal/adapter/gitleaks` imports the scanner for its `Finding` type, so the fold is an import cycle. The scanner declares `type Augmenter interface { Available() error; Scan(text, file string) []Finding }` and an option `WithAugmenter(Augmenter)`; the gitleaks adapter keeps importing the scanner and is wired at the composition root (`cmd/abcd`, or the core constructor that already reads the opt-in), so the scanner never imports gitleaks. `ScanText` and `ScanBundle` append the augmenter's findings and deduplicate on file, line and span. `ErrConfiguredNotFound` becomes one state with one consequence per consumer: launch fails closed (an `Unscanned` reason, and `HardFails` incremented), while capture, history and memory write and record the gap in their receipt. It is built in its own lane of about 150 lines, not in the scanner-cluster fix round, so the record carries a v0.10.0 deferral naming this ruling. Technical ruling by the orchestrator of autonomous run A, on the design the scanner-cluster review set out (its item 4); recorded by the implementer of lane scanner, fix round 1.
- dcn-26093000000000152026-09-30 — Building the 2026-09-25 ruling on iss-2608291814575788 (lane gitleaksAug of autonomous run A) took four calls the ruling left open. (1) Only the not-installed binary is the recorded gap: the scanner declares `scanner.ErrAugmenterNotFound`, which `gitleaks.ErrConfiguredNotFound` matches, and every other state the augmenter reports — a refused configured path, a broken `gitleaks.json`, a run that fails, a report the scanner cannot place at its line and column, or more than 10,000 findings from one scan — degrades the scanner, so every consumer treats it as it treats a broken `pii.json`. The ruled `Scan(text, file) []Finding` has no error, so a failed run is reported through the next `Available()`, which the scanner asks after every scan; a write path therefore consults `Unavailable()` after scanning as well as before. (2) The two outward-bound consumers the ruling does not name fail closed on the gap as launch does: `disembark pack` refuses (a lifeboat leaves the repository as a release does), and the privacy lint reports it as an error finding citing `.abcd/config/gitleaks.json`. (3) The augmenter's output is untrusted: the scanner keeps only a finding whose bytes sit where it says, forces hard_fail, namespaces a kind that would choose a native redaction (an identity, network or PEM kind, or any un-namespaced one) under `augmented:`, drops its snippet and suggestion for its own, and sanitises and caps an error it prints; the gitleaks runner runs in `gitutil.IsolatedEnv` and discards the binary's own output. (4) A write path verifies what the augmenter found by its bytes (`scanner.UnsealedAugmented`) beside a native-only re-scan (`ScanTextNative`), never by re-running gitleaks over redacted text; `history migrate` builds its scanner without the augmenter, as its redaction of lineage scalars never ran gitleaks.
- iss-2609301307566557abcd implement record --transcript stores each transcript through the history capture but drops the capture's scan gap: in a repository that armed gitleaks where gitleaks is not installed, the run's transcripts are stored with the native scanner only and neither the text render nor --json says so, while history capture names the gap (iss-2608291814575788). The run record's transcript entry carries no field for it.