fix(policy): close audit --ci silent governance bypass + SCP/ADO URL parsing (#1159)#1164
Conversation
_parse_remote_url() in policy/discovery.py and _parse_ssh_url() in models/dependency/reference.py both hardcoded 'git@' as the SCP user prefix, silently dropping valid EMU/GHE SSH URLs that use a non-'git' account (e.g. enterprise-user@ghe.corp.com:org/repo.git). For #1159 audit auto-discovery, a dropped URL meant the org could not be extracted, which then triggered the silent fail-open (separate fix). Also fixes Azure DevOps SSH URLs of the form git@ssh.dev.azure.com:v3/<org>/<project>/<repo>: the leading 'v3/' segment is part of the protocol, not the org. Previously the org was parsed as 'v3'. Both functions now use an SCP-like regex that accepts any RFC-3986 username, and the ADO v3 prefix is detected and stripped. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pre-#1159, ``apm audit --ci`` silently swallowed the auto-discovery outcomes ``no_git_remote``, ``absent`` and ``empty`` (set ``fetch_result = None`` with no log line), leaving CI green even when no org policy was enforced. This is the audit-side fail-open governance bypass. Now those outcomes are explicit: * warn (default): ``[!]`` line on stderr explaining the cause and pointing at ``policy.fetch_failure_default=block`` for fail-closed parity with the install path. * block (when project apm.yml sets ``policy.fetch_failure_default: block``): ``[x]`` line on stderr + exit 1. * ``disabled`` outcome (auto-discovery turned off in apm.yml) emits a forensic ``[i]`` breadcrumb on stderr; exit 0. All new diagnostics route to STDERR via ``click.echo(..., err=True)`` so JSON / SARIF on stdout stays parseable for CI consumers. Existing fetch-failure outcomes (``malformed`` / ``cache_miss_fetch_fail`` / ``garbage_response``) keep their existing wording; the same outcomes on auto-discovery now reach the same code path. Explicit ``--policy <file>`` keeps the legacy fall-through for the no-policy outcomes -- an opt-in pointer at a baseline file should not regress. 13 unit tests in tests/unit/test_audit_ci_auto_discovery.py cover warn, block, disabled, and the explicit-policy negative case. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…es (#1159) Pre-#1159, the install pipeline silently swallowed the auto-discovery outcomes no_git_remote / absent / empty even when the project's apm.yml declared policy.fetch_failure_default: block. That was an install-side fail-open governance bypass symmetrical to the audit bug fixed in the previous commit. Now those outcomes are added to _FETCH_FAILURE_OUTCOMES in policy/outcome_routing.py, so the same block-on-no-policy semantics apply to BOTH install paths (install/phases/policy_gate.py and policy/install_preflight.py) without per-call duplication. The PolicyViolationError message is updated to be honest about scope. Default warn still keeps fail-open (no behavior change for projects that have not opted in). Two regression-trap tests in tests/unit/policy/test_fetch_failure_knob.py that asserted the buggy fail-open behavior are flipped to assert the opposite (DOES raise), and a new test_empty_block_raises plus a test_absent_warn_does_not_raise round out the matrix. Also: lint format + drop unused PolicyFetchResult.found kwarg in tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Real `git init` fixtures (no mocks on discovery layer) cover the three no-policy outcomes that pre-#1159 silently passed: - test_default_warn_emits_stderr_and_exits_zero (no remote, default warn) - test_block_fails_closed_with_exit_one (no remote + fetch_failure_default=block) - test_warn_when_not_a_git_repo (no .git at all) Asserts: stderr contains the new [!]/[x] diagnostics, stdout JSON stays clean, exit codes match contract. Refs #1159 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ault=block) Updates 5 surfaces to reflect new fail-closed parity for the no_git_remote / absent / empty outcomes: - CHANGELOG.md [Unreleased]: ### Fixed (audit + SCP), ### Changed (install parity) - docs/.../reference/manifest-schema.md: fetch_failure_default scope expanded - docs/.../enterprise/policy-reference.md: new section 9.5.1 No-policy outcomes - docs/.../enterprise/governance-guide.md: failure-semantics table + offline matrix - packages/apm-guide/.apm/skills/apm-usage/governance.md: section 9.5 + 9.8 Refs #1159 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR closes #1159 by ensuring org-policy auto-discovery cannot silently bypass governance in apm audit --ci, and by fixing SCP-like SSH remote parsing for non-git usernames (including Azure DevOps v3/<org> SSH forms). It also aligns install-path behavior so policy.fetch_failure_default: block consistently fails closed when no enforceable org policy is resolved.
Changes:
- Extend SCP-like SSH parsing to accept arbitrary usernames and correctly interpret Azure DevOps SSH
v3/<org>/...paths. - Make
apm audit --cisurface previously-silent no-policy outcomes (no_git_remote,absent,empty, plus a breadcrumb fordisabled) via stderr, honoringpolicy.fetch_failure_default: block. - Update unit/integration tests and documentation (manifest schema, policy reference, governance guide, apm-guide skill, changelog) to match the new fail-open/fail-closed semantics and stderr/stdout split.
Show a summary per file
| File | Description |
|---|---|
src/apm_cli/commands/audit.py |
Adds outcome-to-message helper and new stderr diagnostics for --ci auto-discovery no-policy outcomes, honoring fetch_failure_default. |
src/apm_cli/policy/discovery.py |
Uses an SCP-like regex to parse non-git SSH remotes and unwraps ADO v3/<org> org extraction. |
src/apm_cli/policy/outcome_routing.py |
Expands the set of outcomes gated by policy.fetch_failure_default to include no-policy outcomes for install-path parity. |
src/apm_cli/models/dependency/reference.py |
Broadens SCP-like dependency SSH parsing to accept arbitrary usernames and improves actionable error messaging. |
tests/unit/test_generic_git_urls.py |
Adds regression tests for SCP-like SSH dependencies with non-git usernames. |
tests/unit/policy/test_discovery.py |
Adds regression tests for policy remote parsing (non-git SCP and ADO SSH v3/). |
tests/unit/test_audit_ci_command.py |
Switches JSON/SARIF parsing to result.stdout to avoid stderr diagnostics corrupting JSON parsing. |
tests/unit/test_audit_policy_command.py |
Reads JSON from result.stdout for baseline-only --ci to avoid stderr warnings corrupting parsing. |
tests/unit/test_audit_ci_auto_discovery.py |
Adds coverage for warn/block behavior and stderr messaging for no-policy outcomes and disabled. |
tests/unit/policy/test_fetch_failure_knob.py |
Updates expectations so fetch_failure_default=block fails closed for absent/no-remote/empty outcomes (install parity). |
tests/integration/test_audit_silent_skip_e2e.py |
New e2e coverage using real git init verifying stderr diagnostics and exit codes for --ci with/without fail-closed. |
docs/src/content/docs/reference/manifest-schema.md |
Updates fetch_failure_default semantics to include no-policy outcomes and audit/install parity. |
docs/src/content/docs/enterprise/policy-reference.md |
Documents no-policy outcomes and their warn/block behavior, plus audit stderr/stdout contract. |
docs/src/content/docs/enterprise/governance-guide.md |
Updates offline/failure matrix to reflect no-policy outcomes and parity semantics. |
packages/apm-guide/.apm/skills/apm-usage/governance.md |
Updates governance skill docs to reflect new no-policy outcome behavior and stderr-only diagnostics. |
CHANGELOG.md |
Adds Unreleased entries for the audit silent-skip fix, SSH parsing fix, and install parity change. |
Copilot's findings
- Files reviewed: 16/16 changed files
- Comments generated: 5
| Used by both the ``warn`` (`[!]`) and ``block`` (`[x]`) branches so the | ||
| wording is identical; only the prefix and suffix change. Closes #1159 | ||
| by replacing the prior silent-skip with explicit, actionable causes | ||
| for ``no_git_remote`` / ``absent`` / ``empty`` outcomes (and matching | ||
| the existing wording for fetch failures). | ||
| """ | ||
| src = source or "unknown" | ||
| if outcome == "no_git_remote": | ||
| return "Could not determine org from git remote; org-policy enforcement skipped" |
| # Outcomes that honour the project-side ``policy.fetch_failure_default`` | ||
| # knob. Pre-#1159, ``absent`` / ``no_git_remote`` / ``empty`` were | ||
| # excluded here -- they meant "no org policy" and were always fail-open | ||
| # even when the project explicitly opted in to ``block``. That was an | ||
| # install-path silent-skip (governance bypass) symmetrical to the audit | ||
| # bug fixed in the same PR. They now route through the ``block`` | ||
| # branch so a project that asserts "no policy = no install" gets that | ||
| # guarantee on BOTH install and audit paths. | ||
| _FETCH_FAILURE_OUTCOMES = ( | ||
| "malformed", | ||
| "cache_miss_fetch_fail", | ||
| "garbage_response", | ||
| "no_git_remote", | ||
| "absent", | ||
| "empty", | ||
| ) |
| import requests | ||
| import yaml | ||
|
|
||
| from ..cache.url_normalize import _SCP_LIKE_RE | ||
| from ..utils.path_security import PathTraversalError, ensure_path_within |
| ssh_match = re.match( | ||
| r"^(?P<user>[a-zA-Z0-9_][a-zA-Z0-9_.+-]*)@(?P<host>[^:/]+):(?P<path>.+)$", | ||
| dependency_str, | ||
| ) |
| - `empty` -- the file exists but parses to an empty policy (no rules). | ||
|
|
||
| Pre-#1159, these outcomes were silently fail-open on BOTH `apm install` and `apm audit --ci` even when the project pinned `policy.fetch_failure_default: block`. As of v0.12.3 they honour the same knob as fetch failures: | ||
|
|
||
| - **`warn` (default):** `[!]` warning on stderr explaining the cause; install / audit proceeds. | ||
| - **`block`:** `[x]` error on stderr; install raises `PolicyViolationError`, `apm audit --ci` exits 1. | ||
|
|
||
| Explicit `--policy <file>` keeps the legacy fall-through for these three outcomes -- an opt-in pointer at a baseline file should not regress. |
Five inline review findings, all valid: 1. audit.py: drop the duplicated "; enforcement skipped" wording (cause string is now neutral so warn-suffix and block-suffix don't compound) 2. outcome_routing.py: rename _FETCH_FAILURE_OUTCOMES -> _OUTCOMES_HONORING_FETCH_FAILURE_DEFAULT (the constant now includes no-policy outcomes, not just fetch failures) 3. cache/url_normalize.py: rename _SCP_LIKE_RE -> SCP_LIKE_RE (public) so the cross-layer import from policy/discovery.py is intentional instead of reaching into cache internals 4. models/dependency/reference.py: replace the inline SCP regex with the shared SCP_LIKE_RE so dependency parsing and policy discovery cannot drift on what counts as an SCP-shorthand SSH URL (this was the consolidation the PR body claimed but didn't deliver) 5. docs/.../policy-reference.md: drop hardcoded "v0.12.3" in favor of linking #1159 -- next-release version pins go stale fast Verification: lint silent, 7701 unit tests pass. Refs #1159 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Addressed all 5 inline comments from @copilot-pull-request-reviewer in 3d5b8ab:
Verification: lint silent ( |
Closes the test-coverage gaps flagged by the apm-review-panel on PR #1164: TC-1: install-side parity for the no_git_remote + project policy.fetch_failure_default=block contract (real `git init`, real CliRunner, mocks only the downloader and update check) TC-2: SARIF stdout cleanliness on `apm audit --ci -f sarif` for both warn (parseable v2.1.0 JSON) and block (empty stdout, exit non-zero) paths TC-3: SCP/EMU + ADO v3 SSH URL parsing through three real consumers of the shared SCP_LIKE_RE: 1. _extract_org_from_git_remote (real git remote) 2. discover_policy_with_chain end-to-end (org routing) 3. APMPackage.from_apm_yml (dep parse codepath) All three test files are wired into scripts/test-integration.sh so the gating CI script now covers the full #1159 surface. TC-5 (legacy fetch-failure stderr cleanliness) was verified in src/apm_cli/commands/audit.py:495-516 and is already correct: the malformed / cache_miss_fetch_fail / garbage_response paths route to click.echo(..., err=True). No new test added. TC-4 / TC-6 are accepted as nits; not addressed in this commit. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Test-coverage panel finding dispositionPushed Closed in this commit
All three files are wired into Verified-already-correct (no test needed)TC-5 (legacy fetch-failure stderr cleanliness): inspected Pipeline-ordering note (relevant to TC-1)In Local verification
Not addressed
|
Per repo convention, docs reflect current behaviour only -- they do not reference past behaviour, fix history, PR numbers, or issue numbers. Strip three such references introduced by this branch and one pre-existing #829 callout in the same paragraph being edited: - governance-guide.md: drop "since #1159" qualifier from the decision-matrix row and the failure-semantics row - policy-reference.md: rewrite the no-policy-outcomes paragraph to describe current behaviour without the "Pre-#1159 ..." / "Since #1159 ..." framing and without the github.com issue link - policy-reference.md: strip the "(closes #829)" parenthetical in the cache-miss bullet Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…parsing (#1159) (#1164) * fix(policy): SCP shorthand and ADO v3 SSH org extraction (#1159) _parse_remote_url() in policy/discovery.py and _parse_ssh_url() in models/dependency/reference.py both hardcoded 'git@' as the SCP user prefix, silently dropping valid EMU/GHE SSH URLs that use a non-'git' account (e.g. enterprise-user@ghe.corp.com:org/repo.git). For #1159 audit auto-discovery, a dropped URL meant the org could not be extracted, which then triggered the silent fail-open (separate fix). Also fixes Azure DevOps SSH URLs of the form git@ssh.dev.azure.com:v3/<org>/<project>/<repo>: the leading 'v3/' segment is part of the protocol, not the org. Previously the org was parsed as 'v3'. Both functions now use an SCP-like regex that accepts any RFC-3986 username, and the ADO v3 prefix is detected and stripped. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(audit): surface no-policy auto-discovery outcomes in --ci (#1159) Pre-#1159, ``apm audit --ci`` silently swallowed the auto-discovery outcomes ``no_git_remote``, ``absent`` and ``empty`` (set ``fetch_result = None`` with no log line), leaving CI green even when no org policy was enforced. This is the audit-side fail-open governance bypass. Now those outcomes are explicit: * warn (default): ``[!]`` line on stderr explaining the cause and pointing at ``policy.fetch_failure_default=block`` for fail-closed parity with the install path. * block (when project apm.yml sets ``policy.fetch_failure_default: block``): ``[x]`` line on stderr + exit 1. * ``disabled`` outcome (auto-discovery turned off in apm.yml) emits a forensic ``[i]`` breadcrumb on stderr; exit 0. All new diagnostics route to STDERR via ``click.echo(..., err=True)`` so JSON / SARIF on stdout stays parseable for CI consumers. Existing fetch-failure outcomes (``malformed`` / ``cache_miss_fetch_fail`` / ``garbage_response``) keep their existing wording; the same outcomes on auto-discovery now reach the same code path. Explicit ``--policy <file>`` keeps the legacy fall-through for the no-policy outcomes -- an opt-in pointer at a baseline file should not regress. 13 unit tests in tests/unit/test_audit_ci_auto_discovery.py cover warn, block, disabled, and the explicit-policy negative case. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * fix(install): honour fetch_failure_default=block for no-policy outcomes (#1159) Pre-#1159, the install pipeline silently swallowed the auto-discovery outcomes no_git_remote / absent / empty even when the project's apm.yml declared policy.fetch_failure_default: block. That was an install-side fail-open governance bypass symmetrical to the audit bug fixed in the previous commit. Now those outcomes are added to _FETCH_FAILURE_OUTCOMES in policy/outcome_routing.py, so the same block-on-no-policy semantics apply to BOTH install paths (install/phases/policy_gate.py and policy/install_preflight.py) without per-call duplication. The PolicyViolationError message is updated to be honest about scope. Default warn still keeps fail-open (no behavior change for projects that have not opted in). Two regression-trap tests in tests/unit/policy/test_fetch_failure_knob.py that asserted the buggy fail-open behavior are flipped to assert the opposite (DOES raise), and a new test_empty_block_raises plus a test_absent_warn_does_not_raise round out the matrix. Also: lint format + drop unused PolicyFetchResult.found kwarg in tests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(audit): add e2e integration tests for --ci silent-skip fix Real `git init` fixtures (no mocks on discovery layer) cover the three no-policy outcomes that pre-#1159 silently passed: - test_default_warn_emits_stderr_and_exits_zero (no remote, default warn) - test_block_fails_closed_with_exit_one (no remote + fetch_failure_default=block) - test_warn_when_not_a_git_repo (no .git at all) Asserts: stderr contains the new [!]/[x] diagnostics, stdout JSON stays clean, exit codes match contract. Refs #1159 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: document #1159 fix (no-policy outcomes honour fetch_failure_default=block) Updates 5 surfaces to reflect new fail-closed parity for the no_git_remote / absent / empty outcomes: - CHANGELOG.md [Unreleased]: ### Fixed (audit + SCP), ### Changed (install parity) - docs/.../reference/manifest-schema.md: fetch_failure_default scope expanded - docs/.../enterprise/policy-reference.md: new section 9.5.1 No-policy outcomes - docs/.../enterprise/governance-guide.md: failure-semantics table + offline matrix - packages/apm-guide/.apm/skills/apm-usage/governance.md: section 9.5 + 9.8 Refs #1159 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * review: address Copilot reviewer comments on #1164 Five inline review findings, all valid: 1. audit.py: drop the duplicated "; enforcement skipped" wording (cause string is now neutral so warn-suffix and block-suffix don't compound) 2. outcome_routing.py: rename _FETCH_FAILURE_OUTCOMES -> _OUTCOMES_HONORING_FETCH_FAILURE_DEFAULT (the constant now includes no-policy outcomes, not just fetch failures) 3. cache/url_normalize.py: rename _SCP_LIKE_RE -> SCP_LIKE_RE (public) so the cross-layer import from policy/discovery.py is intentional instead of reaching into cache internals 4. models/dependency/reference.py: replace the inline SCP regex with the shared SCP_LIKE_RE so dependency parsing and policy discovery cannot drift on what counts as an SCP-shorthand SSH URL (this was the consolidation the PR body claimed but didn't deliver) 5. docs/.../policy-reference.md: drop hardcoded "v0.12.3" in favor of linking #1159 -- next-release version pins go stale fast Verification: lint silent, 7701 unit tests pass. Refs #1159 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * test(#1159): add E2E coverage for TC-1, TC-2, TC-3 from panel review Closes the test-coverage gaps flagged by the apm-review-panel on PR #1164: TC-1: install-side parity for the no_git_remote + project policy.fetch_failure_default=block contract (real `git init`, real CliRunner, mocks only the downloader and update check) TC-2: SARIF stdout cleanliness on `apm audit --ci -f sarif` for both warn (parseable v2.1.0 JSON) and block (empty stdout, exit non-zero) paths TC-3: SCP/EMU + ADO v3 SSH URL parsing through three real consumers of the shared SCP_LIKE_RE: 1. _extract_org_from_git_remote (real git remote) 2. discover_policy_with_chain end-to-end (org routing) 3. APMPackage.from_apm_yml (dep parse codepath) All three test files are wired into scripts/test-integration.sh so the gating CI script now covers the full #1159 surface. TC-5 (legacy fetch-failure stderr cleanliness) was verified in src/apm_cli/commands/audit.py:495-516 and is already correct: the malformed / cache_miss_fetch_fail / garbage_response paths route to click.echo(..., err=True). No new test added. TC-4 / TC-6 are accepted as nits; not addressed in this commit. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * docs: drop PR/issue references and historical phrasing from policy docs Per repo convention, docs reflect current behaviour only -- they do not reference past behaviour, fix history, PR numbers, or issue numbers. Strip three such references introduced by this branch and one pre-existing #829 callout in the same paragraph being edited: - governance-guide.md: drop "since #1159" qualifier from the decision-matrix row and the failure-semantics row - policy-reference.md: rewrite the no-policy-outcomes paragraph to describe current behaviour without the "Pre-#1159 ..." / "Since #1159 ..." framing and without the github.com issue link - policy-reference.md: strip the "(closes #829)" parenthetical in the cache-miss bullet Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Daniel Meppiel <copilot-rework@github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
TL;DR
Closes #1159. Fixes a silent governance bypass in
apm audit --ciplus a sibling SCP/ADO SSH URL parsing bug that produced the same class of failure on the install path. Adds install-path parity sopolicy.fetch_failure_default: blockactually fails closed for the three no-policy outcomes (no_git_remote,absent,empty) on BOTH install and audit. Documents the new contract on five doc surfaces.Problem (WHY)
#1159 reports two interlocking bugs:
apm audit --cisilently passed (exit 0, no log line) when auto-discovery resolved no enforceable policy -- specifically theno_git_remote(noorigin),absent(noapm-policy.ymlat the org), andempty(file present but empty) outcomes. CI stayed green even when no enforcement ran.policy.fetch_failure_default: blockdid NOT block these on the install path either -- onlymalformed/cache_miss_fetch_fail/garbage_responsehonoured the knob.gitusers failed to parse, dropping<user>@github.com:owner/repo(EMU) and<user>@ssh.dev.azure.com:v3/<org>/<project>/<repo>(Azure DevOps) on the floor. Auto-discovery then fell through tono_git_remote-- which (per Why do we need a GitHub token? #1) was silent. So an enterprise / ADO user got NO policy enforcement and NO warning.The two together meant: an EMU/ADO project with
policy.fetch_failure_default: blockset, expecting fail-closed CI gating, would ship green with zero enforcement. That is the silent-bypass class the issue calls out.Approach (WHAT)
Five phases, each its own commit:
_SCP_LIKE_REbetweenmodels/dependency/reference.pyandpolicy/discovery.pyso EMU and ADO patterns parse on both code paths. Unwrap ADOv3/<org>to the real org.apm audit --ci. For the three no-policy outcomes (no_git_remote,absent,empty) emit a[!]warning to stderr by default; honourpolicy.fetch_failure_default: blockto fail closed ([x]+ exit 1). Emit a forensic[i]line fordisabled(auto-discovery turned off) so it's visible in CI logs. All diagnostics on stderr -- JSON / SARIF on stdout stays clean._FETCH_FAILURE_OUTCOMESinpolicy/outcome_routing.pyfrom 3 to 6 outcomes.policy.fetch_failure_default: blocknow means what it says on BOTHapm installandapm audit --ci. RewordPolicyViolationErrorto "no enforceable org policy was resolved" (honest about scope -- covers both fetch failure and absent/empty).git init. No mocks on the discovery layer -- spawns a temp dir, runsgit init, asserts on real CLI behavior.Implementation (HOW)
src/apm_cli/policy/discovery.py_parse_remote_urluses shared_SCP_LIKE_RE; ADOv3/<org>unwrappedsrc/apm_cli/models/dependency/reference.py_parse_ssh_urluses shared_SCP_LIKE_RE; error messages cite captured usersrc/apm_cli/commands/audit.py_audit_outcome_cause()helper;auto_discoveredflag; per-outcomeclick.echo(..., err=True)(NOT logger -- logger routes to stdout via Rich Console and corrupts JSON)src/apm_cli/policy/outcome_routing.py_FETCH_FAILURE_OUTCOMESextended(malformed, cache_miss_fetch_fail, garbage_response, no_git_remote, absent, empty); consumed by bothinstall/phases/policy_gate.pyandpolicy/install_preflight.pyvia single dispatchExplicit
--policy <file>opt-out: the new no-policy warnings only fire whenauto_discovered=True. An explicit pointer at a minimal/empty baseline file does not regress.ASCII-only:
[!]warn,[x]error,[i]info -- per the repo's encoding contract.Click 8.2.1 stderr/stdout split:
result.stdout/result.stderrare now distinct (mix_stderrremoved). Tests parsing JSON updated fromresult.outputtoresult.stdoutto avoid stderr contamination.Trade-offs
logger(which routes to stdout). Latent JSON-corruption bug formalformed/cache_miss_fetch_fail/garbage_response. Out of scope here -- confined fix to the new code paths to limit blast radius. Tracked as a follow-up.--require-policyflag yet (noted in the issue). Deferred to a follow-up so this PR stays focused on the bypass.Validation evidence
uv run --extra dev ruff check src/ tests/ && uv run --extra dev ruff format --check src/ tests/-- silent.test_audit_silent_skip_e2e.py(3 new e2e),test_audit_ci_auto_discovery.py(13: 6 pre-existing + 7 new),test_audit_ci_command.py,test_audit_policy_command.py,test_fetch_failure_knob.py(4 -- 2 flipped from regression-trap, 2 new). All pass.How to test manually
Refs #1159