Audit remediation: 2026-08-09 repository audit findings - #2
Merged
Conversation
…ranch Committed as its own layer so the audit-remediation commits that follow are reviewable in isolation. These hunks were present in the working tree before the 2026-08-09 repository audit began (or appeared alongside it) and were NOT authored by that audit: - argus/audit/open_llm_adapter.py — Ollama env-var resolution (ARGUS_LLM_MODEL / OLLAMA_MODEL / OLLAMA_URL). Not reviewed as part of the audit. - argus/cost/resume.py — in-flight edits. - tests/test_multilanguage_audit.py, tests/test_open_llm_adapter.py — accompanying tests. The whole suite is green with them present (1219 passed, mypy clean over 71 files), which is the basis on which they are carried forward rather than a review of their content. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
23 .pyc files and coverage.xml were tracked despite .gitignore:2 listing `__pycache__/`. They were force-added past an explicit ignore rule, were stale cpython-312 bytecode while the project supports 3.10-3.12, shipped inside the sdist, and churned in every diff (coverage.xml alone is 4,187 lines). Untracked via `git rm --cached` — the files remain on disk. Adds coverage.xml to .gitignore so the generated report cannot be re-added by accident. Audit finding F-5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
render_security_review_report filtered findings on `f.get("detector_id")`. Recording
has no such field — the pipeline passes `Recording.model_dump()`, which carries
`rule_id` and a `locators` list. The filter therefore always matched nothing.
Reproduced end to end on a fixture repo carrying a live-shaped credential: the
scanner detected it and wrote a `hardcoded_secret` row to .argus/findings/, while
security-review.md reported "Total Security Findings: 0" and "No high-entropy secrets
or hardcoded credentials detected in audited source files." A security auditor
affirmatively denying a credential it had just found.
The architecture report had the same shape defect in its Location column: it read a
top-level `file_path`/`line_number` that a real Recording does not carry, so every
locator rendered blank.
Fix: one shape adapter (_finding_rule_id / _finding_location / _finding_ast_span /
_finding_masked_value) read by both reports, accepting the real Recording shape and
the legacy flat dict. The masked-value cell now states the value was discarded at
detection (NFR-S2) rather than guessing "High Entropy Token" — a real Recording
carries no value by design.
Why 95% line coverage did not catch it: tests/test_report_generator.py built finding
dicts by hand containing `detector_id`, a key production never emits, so the fixture
agreed with the bug. Four regression tests now mint findings through the real
build_recording path.
Verified: fixture repo goes 0 -> 2 reported secrets with correct `svc.py:4` locators;
architecture report Location and Details columns populate.
Audit finding F-10 (Critical).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…nt globs, resume drift Four defects found by the 2026-08-09 repository audit. F-11 — --passes/--skip-pass/--ignore-path/--ignore-pattern did nothing. All five production call sites of _detect_per_file/_orphan_findings omitted the `request` argument, so the `None` branch (a convenience default) was the only branch that ever ran: the hardcoded full pass set was substituted and the ignore lists were replaced with empty tuples. The report then keyed its status line on request.enabled_passes and printed "Secret Scan Status: SKIPPED (Pass Deselected)" for a scan that had in fact run and emitted a finding. `request` is now REQUIRED, so a future call site that forgets it is a TypeError rather than a silently degraded audit. Verified: default=4 findings; --skip-pass security=3; --passes coverage=0; --ignore-path svc.py suppresses the secret. F-12 — path matching depended on the host OS. Seven fnmatch.fnmatch call sites compare through os.path.normcase, which lower-cases on Windows and is identity on POSIX. ledger/critical_subsystems already documents why this is forbidden (NFR-P1) and uses fnmatchcase; intake/ignore_rules and detectors/secret_suppression did not. Consequences were real in both directions: ignore_rules decides which files are enumerated at all, moving the ledger denominator, the deep ratio, the verdict and the exit code; and secret_suppression decides whether a detected credential is suppressed, so a secret in Tests/Config.py was reported on Linux and hidden on Windows. Now fnmatchcase throughout, which also errs toward auditing more and suppressing less. Story 3.5's AC enumerates host/locale/encoding/CWD/hash-seed and omits filename case, which is how this survived; four tests close that gap, including a structural guard that fails on any reintroduction of fnmatch.fnmatch or os.path.normcase. F-13 — resume derived the critical-subsystem set from a subset. _detect_per_file was called with only the resume targets, so every carried-forward file was missing from identify_critical_subsystems, which feeds both the persisted artifact and the verdict's critical_subsystems_all_deep clause. That contradicts the AC2 keystone the function asserts at length. Candidacy is now derived over the full assessed population through a shared _critical_candidate builder, so the fresh and resume paths cannot disagree. Also forwards source_state so resumed reports keep their provenance disclosure. F-16 — removed two no-op `if ...: pass` branches whose comments described behaviour the code did not perform. F-15 — the persisted negative-assurance artifact said "within the assessed scope" while reporting whole-ledger counts, omitting the narrowing entirely. It now appends the scoped ratio and held-out count, so a scoped RELEASE_READY cannot be read without also reading what was held out. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ped behaviour Documentation drift, not behaviour change. Each of these asserted something the code did not do — the same class of defect this tool exists to find elsewhere. F-6 — cli.py's frozen-contract docstring stated `--commit` is "REQUIRED ... there is no silent HEAD default — an unpinned audit is not reproducible", while the parser sets `default="HEAD"`. The default was relaxed and enforcement moved to --strict; the paragraph was never updated, so the module's own contract documentation asserted the opposite of shipped behaviour on the one flag carrying the FR1 determinism guarantee. Rewritten to describe --strict as the actual enforcement. F-14 — budget_governor and exhaustion claimed "Reuse BY IMPORT, never fork (AR7)" and "the Minions guardrails are the single hard-ceiling authority". Story 3.1 did require wrapping the upstream Minions cost-guardrails module by import, precisely so no fork existed. The repo separation then VENDORED that file to argus/shared/budget_guardrails.py, which is the fork the requirement existed to prevent: there is no upstream left to stay in sync with, and Argus now owns an unmaintained private copy carrying pod/worker-pool concepts meaningless here, a datetime.now() call in a tree whose AR4 forbids clocks, and float policy fields. Story 9.1 retired the imports and 9.2 swept the provenance tokens; neither addressed the copy. The decision semantics are unchanged and still accurate — only the provenance claim was stale. Retiring the vendored copy changes an epic-stated design decision and belongs in a story, so it is recorded, not done here. Also corrected: the float-precision concern raised during the audit is NOT reachable — dataclass annotations do not coerce, so the ceiling stays an int and the comparison is exact at every magnitude. The containment note now says so. The repo's own Story 9.2 guard (minions_core token allowlist) rejected an earlier draft of this change; the wording respects it rather than widening the allowlist. F-16 — removed an empty `if TYPE_CHECKING: pass` block and its now-unused import. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Output of the correct-course workflow run against the 2026-08-09 repository audit. Four findings survived story-traceability validation. Three capabilities entered the shipped contract through the separation seed commit (084c6a7, a 426-file squashed drop) without passing a story gate, and a release status was asserted over a CI workflow that had never executed: - DF-AUD-APAA-C (10.1) — sprint-change-proposal-2026-07-28 declared "READY FOR RELEASE" on a LOCAL pytest run, while item 6 of that same proposal created audit-ci.yml, whose only master run is `failure`. The CI breaks are repaired; the EVIDENCE STANDARD is the open item. - DF-AUD-APAA-D (10.2) — multi-language AST grounding shipped in V1 while PRD L23/L180 and architecture L220/L237 still designate it V2, so the V2 roadmap double-counts delivered work; and one grammar_version resolved from tree-sitter-python is recorded for a 10-language index, which would not bust the R3 cache key on a non-Python grammar change. Same silent-staleness class as DF-5-1-A. Fix the key BEFORE the Epic-5 store is wired, while it is still free. - DF-AUD-APAA-E (10.3) — four CLI flags accepted by the parser and specified nowhere, on a contract Story 1.7 declares LOCKED. Two suppress security findings with no threat model. Inert until 2026-08-09, so neither blessing nor removal is a behavioural break. - DF-AUD-APAA-F (10.4) — a grammar that fails to LOAD is reported as MISSING, so the remedy the report gives cannot work. Epic 10 is a delta following the Epic 8/9 precedent. Epics 1-9 are NOT reopened or regenerated — their retrospectives are signed, and editing their scope now would falsify the record, which is the same error as DF-AUD-APAA-C. Sequence is gate-first: 10.1 is the control that would have caught the rest. Scope: MODERATE. Contract edits (PRD FR30, PRD V1/V2 re-baseline, architecture R3 key, README/CHANGELOG) are specified with OLD->NEW text in the proposal but deliberately NOT applied — they need PO/PM and Architect approval, mirroring how 2026-08-03 held CR-1 and CR-3 as PROPOSED. Not closed by this epic: DF-AUD-APAA-A (orphan cache/, open since 2026-07-04), the vendored budget_guardrails extraction, the config.yaml/architecture.md broken planning-artifacts paths, and the audit's own coverage limit (~29.5% read by eye). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…locale F-21, found by the repaired CI gate on its first ever run (PR #1, run 31322760552). On POSIX with LC_ALL=C and PYTHONUTF8=0 — the default in many containers, cron jobs and minimal CI images — Python decodes filenames with the ASCII codec plus surrogateescape, so a file named `café` arrives as 'caf\udcc3\udca9'. Those strings reach every Recording locator, and str.encode("utf-8") refuses lone surrogates: UnicodeEncodeError: 'utf-8' codec can't encode characters in position 112-113: surrogates not allowed -> argus.pipeline.PipelineError: analysis stage failed: UnicodeEncodeError So auditing ANY repository containing a non-ASCII filename crashed to exit 1 / AUDIT_FAILED on those hosts. AR10 requires honest degradation, never a crash; NFR-P1 requires identical bytes on every host. Regression origin: pipeline.run_audit_detailed was switched from load_repo_at_commit (which decodes `git ls-files -z` as explicit UTF-8) to resolve_source_state (which walks the filesystem via os.walk, inheriting the locale-dependent decode) when the AR10 audit-any-directory relaxation landed. That bypassed the boundary Story 3.5 established and reintroduced what it records as "the ONE Epic-1 review FAIL". Fixed at canonicalize()'s str leaf — the single point every recorded value passes through. encode(surrogateescape) -> decode(utf-8, replace) reverses the OS's lossy decode and re-reads the original bytes as UTF-8. Proven byte-identical: the C-locale view and the UTF-8 view of `café/тесты.py` now serialize to the same bytes and the same content_hash. A UTF-8 host never produces surrogates and takes the fast path unchanged. Not fixed at intake deliberately: the surrogate-bearing string is the OS-native, OPENABLE form. Normalising it at the walk would produce a path the C-locale host can no longer open(). Repairing at serialisation keeps I/O on the native string while making everything RECORDED host-independent. Four tests assert the property from simulated inputs so the guard fires on every host — the existing cross-locale legs only fail on POSIX, which is why a green Windows suite (1219 passed) said nothing about this. Also recorded in the docstring: an earlier draft of it used the word "policy", which is a criticality signal token, so assess_criticality reclassified this module as a critical subsystem and grew the dogfood critical set 50 -> 51, failing the Story 8.5 re-derivation. Whole-file content including comments feeds that heuristic, so a documentation word can move a verdict gate. Filed as an audit finding, not silently worked around. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
F-22, the remaining two failures from the repaired CI gate's first run (PR #1, run 31322760552). `ApaaStorePaths.resolve` used the host-native `Path`, which answers differently per OS. On Windows `C:\evil.json` is absolute and `..\..\x` is a traversal, so both raise. On POSIX a backslash is an ordinary filename character and there are no drive letters, so both are single legal filenames — contained, and silently accepted. The method's docstring has always claimed it rejects "a Windows-backslash traversal, or a drive-letter absolute"; on Linux that was an over-claim. This is NOT merely test portability. `.argus/` state is designed to travel between hosts — NFR-P1 asserts byte-identity across hosts, and the resume path reads a store that may have been written elsewhere. A locator written on Linux as the perfectly legal filename `..\..\escape.json` becomes a REAL traversal the moment that store is resolved on Windows. Containment has to be decided identically everywhere or it is not containment. Fix: reject at the STRING level before the native `Path` logic — POSIX-absolute, a drive-letter prefix, and any `..` segment after normalising backslashes. The rules mirror `index/partitioner.normalize_rel_path`, which already got this right; they are restated rather than imported because `store` sits below `index` and must not depend upward. Verified: all four host-dependent vectors are now refused by string rules alone, and the locators the store actually writes (state/, findings/, assignments/, cache/) are still accepted. Five new tests pin the decision as host-independent, so they fail on Linux and Windows alike if the guard is removed. Whole suite: 1228 passed, mypy clean over 71 files. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
varinderpratap
force-pushed
the
fix/honest-verdict-reporting
branch
from
August 9, 2026 21:28
65ea45e to
ebdca75
Compare
🛡️ ArgusAgent Code Quality & Assurance ReportHello! ArgusAgent has audited your commit (882d7c7). 🛡️ Security Review Report
Detected Secret Indicators
💡 Student Tip: Make sure you have no bare tests or committed API keys in your repository before submitting! |
…h the surrogate repair Follow-up to F-21, found by the CI run on PR #2 (31336912954). The canonical-layer repair moved the crash from position 112-113 to position 7-8 — a shorter string, i.e. a different encode point. Two sites hash path-derived strings WITHOUT going through `dumps`, so the repair never reached them: index/partitioner.py:_partition_id — sha256 over the joined member paths intake/source_state.py:_digest_of — sha256 over each relative path Both still raised UnicodeEncodeError on the lone surrogates a C-locale POSIX host produces for a non-ASCII filename. Worse than a crash: both are content-addressed IDENTIFIERS, so an unrepaired surrogate would key an artifact differently per host — exactly what NFR-P1 forbids. Adds `canonical.safe_utf8_bytes()` as the single encode helper for any call site that hashes or writes a path-derived string outside `dumps`, and routes both sites through it. `dumps_bytes` now uses it too, so there is one repair point rather than two parallel ones. A grep confirms no bare `.encode("utf-8")` remains outside canonical. Local: 1228 passed, mypy clean over 71 files. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The previous commit's scripted edit to this site silently did not apply — the bare
`.encode("utf-8")` survived, and Windows cannot reveal it because it never produces
surrogates. Caught by re-grepping rather than by the green local suite, which is the
same blind spot that let F-21 exist.
`_partition_id` hashes the joined member paths into a content-addressed
`assignments/<id>.json` filename, so an unrepaired surrogate would both crash on a
C-locale POSIX host and key the artifact differently per host (NFR-P1).
Verified: no bare `.encode("utf-8")` remains anywhere in argus/ outside canonical.py.
1228 passed, mypy clean.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`_run_git` decodes git's output as explicit UTF-8, so a non-ASCII filename reaches `RepoIntake.source_files` as true text (`café`). That is the correct form to RECORD, but on POSIX under LC_ALL=C with PYTHONUTF8=0 the filesystem encoding is ASCII, so every downstream open() re-encodes it and raises `UnicodeEncodeError: 'ascii' codec can't encode character`. Only the resume path still routes through `load_repo_at_commit` (a fresh run uses `intake.source_state`, which walks the filesystem and already yields the native form), so this surfaced as a resume-only crash on exactly those hosts — a DIFFERENT defect from the surrogate crash fixed in f7c666e/266bb28, and one Windows cannot observe at all. `to_native_fs_path` re-reads git's real on-disk bytes through the host's own filename rule, giving back the surrogate-bearing str that host can open. It is the exact inverse of `canonical._repair_surrogates`, so the recorded bytes stay identical across locales, and it is an identity on a UTF-8 host. Both intake producers now agree on one contract: `source_files` holds OS-NATIVE strings. Adds the round-trip lock in both directions, asserted from simulated inputs so the guard fires on Windows too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
With the loader returning OS-native paths, the resume plan compared a prior
ledger holding the RECORDED form ('src/caf\xe9_calc.py') against a current index
holding the NATIVE form ('src/caf\udcc3\udca9_calc.py'). Under LC_ALL=C those
differ, so every carried-forward path looked "absent from the current index" and
the resume was refused as a diverged tree — a confident, entirely wrong
diagnosis: nothing had diverged.
Resume is the one place the flow runs backwards, prior paths re-entering memory
in their recorded form. `_to_native_payload` converts them back at that single
read boundary, restoring the invariant the rest of the pipeline already holds:
native in memory, portable on disk.
The walk covers the whole payload tree rather than named path fields, mirroring
`canonicalize`'s own blanket repair, so the two cannot drift apart as models
gain fields and no nested locator can be missed. Non-path strings round trip
exactly, so persisted bytes — and the AC2 byte-identity keystone — are unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Inan15
pushed a commit
that referenced
this pull request
Aug 24, 2026
…FOOD-001-49 master went RED the moment PR #5 landed, and PR #6 repaired it the same day. This entry records why, and the decision taken. THE MECHANISM. TC-ArgusAgent-DOGFOOD-001-49 requires each dogfood artifact to cite a provenance sha that is an ancestor of HEAD. A merge commit keeps the branch's commits as ancestors; squash and rebase rewrite them, orphaning the citation the instant it lands. IT IS INVISIBLE UNTIL TOO LATE, which is why the remedy is a setting and not a habit. The identical tree passed all four checks on PR #5, and passes locally, because there the sha is still an ancestor. Only the push to master can observe it. The history shows when practice changed and it was not announced: PRs #2 and #3 landed as merge commits with master green; #4, #5 and #6 landed sha-rewritten. PR #4 is also why a 2026-08-24 audit finding had to be withdrawn -- its rewrite made 52143eb and 797bba8, one patch under two shas, look like two independent dispositions. The same change cost a false finding and a red master in two days. THE EXCEPTION THAT MAKES THE RULE PRECISE. PR #6 was also sha-rewritten and did NOT red master, because its regeneration cited 932cec9 -- master's own HEAD -- which survives any rewrite of the branch. So the hazard is narrower than "sha-rewriting is unsafe": it is an artifact regenerated on a branch citing a branch sha. That is also the normal case, because regeneration happens where the work happens. DECISION by XAgent007: restrict the repository to merge commits, disable squash and rebase, enable head-branch deletion. Linear history is lost and that cost is stated rather than hidden; it is accepted because this repository's guards rest on sha ancestry of artifact provenance, and of the two, provenance carries the assurance argument. It restores the practice of #2 and #3 rather than inventing one. STATUS: DECIDED, NOT YET APPLIED, and the entry stays OPEN until it is. The API call returned 404, which GitHub returns for a settings update when the caller lacks admin; push, PR and merge all work, so it is an admin-scope gap. The manual remedy is recorded in the entry. The rejected alternative is recorded so it is not re-proposed as new: a master CI job that regenerates and fails on a dirty tree converts a preventable class into a detected-after-the-fact one, and master still reds first. Loosening -49 is not available at all (DF-8-5-B). Append-only, +61/-0, 0 CRLF, lone CR 1 -> 1. ledger_closed_ids unchanged at 35. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
varinderpratap
pushed a commit
that referenced
this pull request
Aug 29, 2026
Delivers Epic 20 (FR38/FR39/FR40) and corrects what a checkpoint review of it found. All three capabilities are disposed `library-seam`: built, typed, tested, and reachable from nothing -- no importer in argus/, no cli.py reference, no console script. The PRD says so at the destination; DF-20-1-A/-2-A/-3-A file it. THE REMEDIATION ENGINE PROPOSED PATCHES THAT BREAK THE TEST. Story 20.2's review round 1 flagged the no-assignable-state fallbacks as vacuous (Medium #2). Round 2 closed it on `assert len(locals()) > 0`, recorded as "inspects local state". Measured at checkpoint: that predicate is False in a scope holding no locals, and in BOTH cases the suite pinned -- an empty `pass` body, and a vacuous assert standing before its assignment -- the patched scope holds none. Executed, not inferred: both patched sources raise AssertionError. A vacuous test that passed became a test that fails. `verify_patch_dry_run` could not see it: it validates AST syntax, and the patch is syntactically perfect. The two unit tests could not see it either -- they asserted the emitted STRING, not the behaviour of the patched test. All five fallback sites now DECLINE when no prior assignment is in scope. AR10 honest degradation: propose nothing rather than fabricate an assertion. AC2's "concrete, non-vacuous" requirement beats its enumeration of shapes; the tension is stated in the story rather than buried. FOUR GUARDS HAD ENCODED "NO RELEASE HAS EVER HAPPENED" AS A FACT. Same class as the two 8c05a10 fixed, found by walking the rest of the family: * TC-DOCS-001-55b asserted the interim caveats were ON DISK while -55 demanded they be removed once a tag exists. The pair could not both be green after a real release -- the guard that polices a transition could not survive it. The pre-release corpus is now simulated, as direction 1 already simulated a deletion. No real tag, and now no real caveat either. * TC-DOCS-001-71 checked tokens that the SUPERSEDED sentence still satisfies, so it would have passed unchanged on a measurement that had inverted underneath it. The live result is pinned too, and the two must disagree. * TC-RELEASE-001-10 required "has never executed" in release.yml, which -55 requires REMOVED once a tag exists. Now asserts the header is in one of the two honest states D2/D13 allow: disclaims execution, or cites a run. * _FIGURE_CLAIMS pinned the literal `argus_agent-0.1.0.tar.gz`. At 1.0.0 it matches nothing, and the failure it raises is "a published measurement was DELETED" -- pointing the next reader at a deletion that never happened. `_Delivery.same_act` was a self-certifying exemption from an honesty guard: a bare boolean asserting "admitted and disposed in one act" that nothing measured. It is now derived from the PRD's own `amendments:` record -- named in exactly one amendment, dated the day it was disposed. Verified to discriminate: FR29 (2026-08-11) and FR7 (unnamed) both fail it. Distribution figures re-derived from a fresh build rather than re-typed: 108 modules, 108 importable, 116 wheel entries, 115 sdist members. README's 0.1.0 artifact filenames corrected struck-not-deleted. argus.spec is ignored, not committed: build-binaries.yml invokes pyinstaller with CLI flags and never reads a spec file, and sprint-change-proposal-2026-08-10 records as measured fact that no `.spec` exists in this tree. Committing it would falsify that line with no guard to catch it. Full suite green, exit 0 (one skip: naming's exemption list is empty, which is the intended end state). mypy argus/ clean over 108 source files. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
Remediation for the 2026-08-09 Repository Audit Protocol run. Stacked on #1 so this
diff shows only the remediation; it retargets to
masterautomatically once #1 merges.The critical one
render_security_review_reportfiltered findings onf.get("detector_id").Recordinghas no such field — the pipeline passes
Recording.model_dump(), which carriesrule_idand a
locatorslist. The filter always matched nothing.Reproduced end to end on a fixture repo with a live-shaped credential: the scanner
detected it and wrote a
hardcoded_secretrow to.argus/findings/, whilesecurity-review.mdreported:A security auditor affirmatively denying a credential it had just found. 95% line coverage
missed it because the test fixture hand-built dicts containing
detector_id— a keyproduction never emits — so the test agreed with the bug.
Also fixed
--passes/--skip-pass/--ignore-path/--ignore-patternwere inert — all 5 call sites dropped therequestarg. The report then printed "Secret Scan Status: SKIPPED" for a scan that had run and emitted a finding.fnmatch.fnmatchsites compare throughos.path.normcase→ a secret inTests/Config.pywas reported on Linux and hidden on Windows.ignore_rulesalso moves the ledger denominator, so the verdict itself was host-dependent..pyc+coverage.xml; corrected in-code docs that asserted the opposite of shipped behaviour.Tests
1219 passed (was 1211), mypy clean over 71 files. 8 new regression tests, including a
structural guard that fails on any reintroduction of
fnmatch.fnmatch/os.path.normcase.Planning
Adds Epic 10 and ledger entries
DF-AUD-APAA-C..Ffor four findings that arespecification decisions, not code fixes — validated against the epics and stories rather
than assumed. Two earlier findings did not survive that validation and were withdrawn.
Contract edits (PRD FR30, PRD V1/V2 re-baseline, architecture R3 cache key,
README/CHANGELOG) are specified with OLD→NEW text in
sprint-change-proposal-2026-08-09.mdbut deliberately not applied — they need PO/PMand Architect approval.
Review note
The first commit,
chore(branch): carry forward in-flight work, isolates changes that werealready in the tree and were not authored or reviewed by this audit (the Ollama
env-var change in
open_llm_adapter.py,cost/resume.py). Separated so it doesn't inheritthis PR's review status by association.
🤖 Generated with Claude Code