Skip to content

fix(scan): PR-quality root-cause pass — Helm-aware workload patches, manual/auto delivery parity, collision + placeholder gates - #205

Merged
alimobrem merged 9 commits into
mainfrom
fix/pr-quality-root-cause
Jul 24, 2026
Merged

fix(scan): PR-quality root-cause pass — Helm-aware workload patches, manual/auto delivery parity, collision + placeholder gates#205
alimobrem merged 9 commits into
mainfrom
fix/pr-quality-root-cause

Conversation

@alimobrem

Copy link
Copy Markdown
Owner

Why

Prompted by a live, reproducing incident: pulse-agent was stuck at score 66.63 for 6 re-Assess cycles over 4+ hours while pinky/AgentIT both held 100. Caught PR pulse-agent#6 fabricating the identical deploy/deployment.yaml stub #5 was just closed for — generated by already-deployed #204 code, ten minutes after #5's close. That live evidence, plus a full audit of the delivery pipeline, produced this fix list.

What's in this PR (8 fixes, each its own commit)

  1. Helm-aware workload patchesworkload-replicas/workload-health-probes depended on analyzers.snapshot's active-snapshot ContextVar, only populated during run_assessment()'s own analyzer pass, always gone by the time a separate onboarding job calls these generators. New enrich_workload_files_from_repo() reuses the GitHub-REST read_file/tree_paths mechanism apply_containerfile_pin_only() already relies on; patches a Helm chart's values.yaml instead of rewriting a templated replicas: line. ha_dr.py's own detection was also Helm-blind — fixed too.
  2. Manual Deliver ↔ auto-delivery gate parityPOST /assessments/{id}/deliver now calls auto_validate_and_deliver() directly instead of a hand-maintained gate subset that had already drifted once (Hello-World gitops#31).
  3. Broadened placeholder detectionyour-org/hello-world.git and quay.io/org/pinky both reached merged content before being caught; has_unresolved_placeholders()/verify_argocd_application() now catch this class.
  4. Resource collision check — new find_resource_collisions(), the confirmed root cause of duplicate GVK/name resources blocking managed-pinky's real Argo sync.
  5. Soft dry-run reason labeling — distinguishes capability-limited (RBAC, grantable) from cluster-incompatible (CRD absent, will never sync).
  6. PR-body dedupexpected_clear_lines() no longer repeats the same sentence once per finding in the same category (pulse-agent#2 printed it 7 times).
  7. LLM-review copy auditreview_final_manifests()'s docstring and a stale AutoMode-era "LLM Safety Gate" settings label no longer imply a blocking gate for an advisory-only, fail-open review.
  8. Real end-to-end testtests/test_e2e_finding_clears.py runs generate → deliver → re-Assess for real (temp-dir repo, real analyzer/generation/enrichment code), for replicas/container. Surfaced and documented (not fixed) a further gap: HADRAnalyzer's replicas check is repo-wide, not workload-scoped.

Testing

Full non-browser suite green after every commit; final state: 3426 passed, 452 skipped, 0 failed (one pre-existing, unrelated test-isolation flake seen intermittently during development, confirmed to pass standalone and within its own file each time).

Docs

docs/history/changelog-dogfood-notes.md and docs/release-notes.md updated with this pass's summary, per this repo's own documentation convention.

Made with Cursor

Ali Mobrem and others added 9 commits July 23, 2026 14:22
…oy/deployment.yaml theater

pulse-agent#5/#6 (gitops): the workload-replicas/workload-health-probes
skills fabricate a disconnected deploy/deployment.yaml stub for every app,
even one with a perfectly real Helm chart -- root cause is
_workload_yaml_paths()/_snapshot_file_text() depending on an
analyzers.snapshot ContextVar that is only ever set during
run_assessment()'s own analyzer pass and is always gone by the time a
separate, later onboarding job calls these generators.

- source_patches.enrich_workload_files_from_repo(): the same GitHub-REST
  read_file/tree_paths mechanism apply_containerfile_pin_only() /
  enrich_sbom_from_repo() already use to find the app's real workload
  manifest instead of guessing. When the workload templates `replicas:` via
  `{{ .Values.<key> }}` (Helm), patches the chart's own values.yaml numeric
  key instead of rewriting the templated line -- avoids inserting a second,
  conflicting literal replicas: key that would corrupt the chart. Drops
  (fail-closed) rather than opens a PR when no real workload can be found or
  the chart key is ambiguous/nested, same posture as self_managed_hpa.py /
  fleet_hpa.py's scaleTargetRef gates.
- Wired into auto_delivery.py's existing pre-clear-evidence enrichment step
  and routes/assessments.py's manual Deliver path (parity).
- workload_patches.py: helm_templated_replicas_key/chart_root_for_template_path/
  values_yaml_path_for_chart/patch_values_numeric_key/values_yaml_replicas_at_least;
  verify_workload_replicas() now also accepts a values.yaml-shaped clear-evidence.
- analyzers/ha_dr.py: the `replicas` finding was also Helm-blind at
  detection time (raw literal-digit regex only) -- a chart correctly setting
  replicaCount>=2 in values.yaml would never clear the finding even after a
  perfect fix merged. Resolves a templated replicas: line against any
  values.yaml in the repo before flagging.

Tests: tests/test_workload_patches.py (new TestHelmChartAwareness,
TestEnrichWorkloadFilesFromRepo), tests/test_ha_dr.py (Helm values.yaml
resolution, still-flagged-below-minimum). Full non-browser suite: 3397
passed, 452 skipped, 0 failed.

Co-authored-by: Cursor <cursoragent@cursor.com>
… closing the gate gap

Hello-World gitops#31 class of bug: this route's own hand-maintained
subset of quality gates (Phase A finding gate, file filter, wrong-layer
strip, per-cluster cap, clear-evidence) had already drifted once from
auto_validate_and_deliver()'s full bar (missing Phase C SSA/property
checks, the fleet HPA scaleTargetRef gate, the capability filter, and the
bounded validate/fix converge loop) -- confirmed via a real entry-point x
gate audit.

POST /assessments/{id}/deliver's dry_run=False path now calls
auto_validate_and_deliver() directly instead of re-implementing a subset
of its own gates -- one pipeline every real-delivery entry point shares,
so this class of gap cannot reopen in a new shape. dry_run=True stays a
lightweight route_and_deliver(dry_run=True) preview, unchanged. The
narrow octocat/Hello-World probe-repo hard-refuse stays in the route
(not a general delivery gate, doesn't belong inside a function that must
stay reusable for any real app).

Test updates reflect real, expected behavior differences now that manual
Deliver shares the automatic pipeline's own validate/fix loop (which can
leave its own dry-run validation-probe delivery rows behind, and surfaces
"could not converge"-shaped refusal reasons for batches that previously
only failed a raw file-count cap) -- not weakened assertions; every test
still proves no PR/commit happens when it shouldn't. Also fixed a fixture
gap surfaced by the new Phase C property check now actually running for
this path: test_pr_repo_labeling.py's NetworkPolicy stub was missing a
real Ingress policyType, which the auto path's own validation would
always have refused.

Full non-browser suite: 3397 passed, 452 skipped, 0 failed.

Co-authored-by: Cursor <cursoragent@cursor.com>
…ge token

Two real incidents reached merged content before being caught live:
an Argo Application repoURL of "your-org/hello-world.git" (gitops#32)
and a container image of "quay.io/org/pinky" -- neither is
REPLACE_WITH_AGENTIT_IMAGE, the only string
has_unresolved_placeholders() ever checked.

- delivery.py::has_unresolved_placeholders(): adds field-scoped regexes
  for repoURL:/image: lines matching your-org/your_org, example.com, or
  quay.io/org/ -- deliberately field-scoped, not a blanket content scan,
  so a legitimate placeholder-shaped value elsewhere in a generated file
  (skills/incident/alertmanager-config.md's own sample
  "team@example.com" alerting address) is never mistaken for a
  fabricated deploy target. Deliberately does NOT flag generic
  "github.com/org/..." repo URLs -- this codebase's own test fixtures
  already rely on that exact shape for a fine, real-looking repo, so a
  broader match would have been a false-positive regression, not a fix.
- clear_evidence.py::verify_argocd_application(): the same "your-org"/
  "example.com" repoURL patterns were previously explicitly *accepted*
  ("Template placeholder still ok" pass-through) whenever a path/chart
  was also present -- now hard-refused outright, matching the general
  placeholder gate.

Tests: tests/test_delivery_router.py (new placeholder-catch and
real-looking-URL-not-flagged cases), tests/test_clear_evidence.py (new
refuse-your-org, refuse-example.com, generic-org-still-allowed cases).
Full non-browser suite: 3401 passed, 452 skipped, 1 pre-existing flaky
failure unrelated to this change (test_finding_escalation.py, passes
standalone and within its own file -- known test-isolation flakiness).

Co-authored-by: Cursor <cursoragent@cursor.com>
…amespace) resources

Confirmed root cause of a real incident: duplicate GVK/name resources
across generated files blocked managed-pinky's Argo sync after merge --
nothing in this pipeline ever checked for this pre-open. SSA dry-run
validates each file/resource independently; Argo's own "who owns this"
ownership conflict only ever surfaced live, after merge.

- quality_prs.find_resource_collisions(): parses every generated file's
  YAML (multi-doc `---` aware) and flags any exact (apiVersion, kind,
  namespace, name) repeat -- within one file or across the whole batch.
  Different API groups sharing a bare kind name (two CRDs both calling
  themselves "Policy") are correctly not a collision; only an exact GVK
  match is.
- cluster_validation_ok() gains an optional collisions= param -- a
  collision is a hard Phase C refusal, same posture as an SSA/property
  failure, not a silent drop-one-keep-one guess.
- Wired into auto_delivery.py's one cluster_validation_ok() call site --
  since manual Deliver now shares this same pipeline, both entry points
  get this check for free, no separate wiring needed.

Tests: tests/test_quality_prs.py (new TestResourceCollisions -- distinct
resources, same-name-different-kind, same-name-different-namespace,
cross-file and within-file collisions, different-API-group same-kind-name
non-collision, cluster_validation_ok wiring). Full non-browser suite:
3410 passed, 452 skipped, 0 failed.

Co-authored-by: Cursor <cursoragent@cursor.com>
…patible soft dry-run skips

Both an RBAC Forbidden and a genuinely-missing optional CRD classify as
"soft" (non-blocking, unchanged) -- but they are not the same kind of gap,
and classify_dry_run_error() folded them into one flat string. A human
reading a PR's dry-run notes could not tell "grant AgentIT's SA more
rights and this would apply" from "this cluster does not run that
operator at all, no RBAC change will ever make this sync" -- the real
ambiguity behind a permanently-unsyncable resource reaching a merged
GitOps PR (managed-pinky's real incident: missing Kyverno/Litmus/audit
CRDs needed manual cleanup after merge).

- classify_soft_dry_run_reason(): additive, does not change
  classify_dry_run_error()'s existing "hard"/"soft" contract (many
  callers/tests already assert on those exact two strings) -- further
  splits an already-soft message into capability_limited / cluster_
  incompatible / other.
- label_soft_dry_run_warning(): tags a dry-run warning string with which
  bucket it's in before it reaches skipped/warnings -- wired into both
  places dry_run_manifests_against_cluster() builds a soft-skip warning
  (the exception-catch path and the per-document errors path). Purely a
  labeling change: which files get soft-skipped (dropped from the
  delivery pack) is unchanged.

Tests: tests/test_cluster_dry_run.py (new TestClassifySoftDryRunReason --
capability_limited/cluster_incompatible/other cases, label wording,
original message preserved). Full non-browser suite: 3418 passed, 452
skipped, 0 failed.

Co-authored-by: Cursor <cursoragent@cursor.com>
…ding

pulse-agent#2, confirmed live: a PR targeting seven separate "container"
findings (:latest across three Dockerfiles, missing HEALTHCHECK, non-UBI
base) printed the identical "Clears \`container\` by pinning..." sentence
seven times in a row. expected_clear_lines() looped once per target
finding, but the emitted line's content depends only on the finding's
category (via contract_for(cat)), never its own description -- every
finding in the same category was always going to produce the exact same
sentence.

Now emits one line per distinct category (order of first appearance
preserved), not one per finding. Purely a PR-body readability fix --
does not change which categories/files are targeted or any gate
decision.

Tests: tests/test_skill_registry_agreement.py (new
TestExpectedClearLinesDeduped), tests/test_quality_prs.py (new
build_helpful_pr_body regression case using pulse-agent#2's real 7
container findings). Full non-browser suite: 3423 passed, 452 skipped,
0 failed.

Co-authored-by: Cursor <cursoragent@cursor.com>
Audited every "LLM" + "safety"/"gate" reference across live code, live
templates, and current (non-historical) docs for the class of
overstatement the underlying function's own docstring already warns
against: "Callers should therefore treat a 'not approved' result as
something to surface clearly... not as a reason to block PR creation."

Two real, live instances found and fixed; everything else already
correctly says "advisory"/"non-blocking"/"human gate remains merge":

- llm.py::review_final_manifests()'s own docstring opened with "Final
  pre-PR quality/safety gate..." -- directly contradicting its own next
  paragraph ("never a reason to block PR creation"). Reworded to
  "quality/completeness opinion... advisory only, never a gate".
- templates/settings.html's "LLM Safety Gate" stat card is a stale
  AutoMode-era label: AutoMode's own LLM classify_action (a genuine
  blocking safety gate) was removed entirely (2026-07-18); the stat it
  labels (llm_available) is just "is an LLM client configured" -- used by
  several mostly-advisory features (secret classification, skill
  generation, the final quality review, capability-scout's evidence
  gate), none of which is "the" safety gate. Relabeled "LLM Availability".

Deliberately left unchanged (already accurate, not the class of bug
this audit was for): quality_prs.py's PR-body "non-blocking — human gate
remains merge" copy; cli.py's "LLM review — first approver gate" for
CLI self-fix's review_fix() specifically, which really is fail-closed
(rejects on low confidence / LLM unavailable) -- an accurate label for a
genuinely different, blocking function, not the portal's advisory
review_final_manifests(); docs/history/*.md's historical/superseded
proposal docs, which document what shipped or was proposed at the time,
not current behavior.

Tests: tests/test_portal.py updated for the corrected settings.html
label. Full non-browser suite: 3422 passed, 452 skipped, 1 pre-existing
flaky error unrelated to this change (test_finding_scoped_verification.py,
passes standalone -- known test-isolation flakiness).

Co-authored-by: Cursor <cursoragent@cursor.com>
…ive findings

Every existing test proves staged content / routing / refuse reasons in
isolation -- none of them run the real loop this product's own contract
depends on ("a good AgentIT PR clears a real finding on next re-Assess").
That gap is exactly why the #199-#204 class of bug (a verifier too weak
to notice a "fix" that would not actually clear the finding) was only
ever caught by live dogfooding, one incident at a time, not CI.

tests/test_e2e_finding_clears.py: real temp-directory "repo" + the real
analyzer + skill-generation + enrichment functions; only the GitHub API
boundary (read_file/tree_paths) is backed by that same temp directory
instead of a live network call, so the whole loop runs hermetically and
fast.

- TestReplicasFindingActuallyClearsAfterMerge: pulse-agent's real Helm-
  templated replicas shape -- proves this session's workload-Helm-
  awareness fix closes the loop end-to-end (generate -> enrich -> merge
  -> re-Assess -> finding gone), not just that the enrichment function
  returns the right dict in isolation.
- TestContainerFindingActuallyClearsAfterMerge: pulse-agent's real
  Dockerfile :latest finding, proving the already-working containerfile-
  pin mechanism closes the same loop.
- A genuine, real gap this test-writing effort surfaced and documents
  rather than silently missing: HADRAnalyzer's replicas check is
  repo-wide (any YAML file with replicas>=2 anywhere in the tree), not
  scoped to the real deployed workload -- so even the OLD, un-enriched,
  fully disconnected stub also "clears" the finding dishonestly. Flagged
  as a known limitation (TestReplicasFindingActuallyClearsAfterMerge::
  test_known_gap_the_disconnected_stub_alone_also_fools_the_analyzer),
  not fixed in this pass -- the detection side has the same "is this the
  real workload" blind spot the generation side just had.

sbom (enrich_sbom_from_repo) is a natural next fixture to add here, not
yet built out. Full non-browser suite: 3426 passed, 452 skipped, 0
failed.

Co-authored-by: Cursor <cursoragent@cursor.com>
Per this repo's own established convention (README stays a short pitch;
dated writeups go to docs/history/changelog-dogfood-notes.md, which
docs/release-notes.md's current-state summary points readers to) --
records this branch's 8 fixes (Helm-aware workload patches, manual-
Deliver/auto-delivery gate parity, broadened placeholder detection,
resource-collision check, soft dry-run reason labeling, PR-body dedup,
LLM-review copy audit, real generate-deliver-reassess e2e test) and the
live incident that prompted them.

Co-authored-by: Cursor <cursoragent@cursor.com>
@alimobrem
alimobrem merged commit 4e82ee3 into main Jul 24, 2026
4 checks passed
@alimobrem
alimobrem deleted the fix/pr-quality-root-cause branch July 24, 2026 00:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant