4 Commits

Author SHA1 Message Date
codex
f8a529e196 feat(hermes): propose fixes for SonarQube findings on a schedule
All checks were successful
Tests / Declarative: Post Actions passed: 1348
Triage has only ever entered on a failure. Static analysis is the opposite
shape - a standing backlog that never fails a build and so never asks anyone
for attention. On this instance that backlog is 139 open findings on Ariadne
alone, each already naming its file, its line, its rule and what is wrong.
That is better-located evidence than the console text the code-repair flow
normally mines, and it was being thrown away.

This is a second way into the same flow, not a second flow. A scheduled sweep
picks one finding and hands it to the existing proposal path, which is
unchanged: Hermes returns a patch as data, Ariadne validates it against the
file it names, pushes a branch, opens a pull request nobody merges. A finding
arriving from outside the build is not a reason to relax the gates that make a
proposal worth reading, so it does not.

Three deliberate limits. Security hotspots are never fetched: SonarQube models
them as needing human review, the quality gate here fails on exactly that
condition, and an automation that resolved them would be marking them
reviewed without review - defeating the control rather than satisfying it.
Findings already marked won't-fix carry a judgement someone made, and
reopening it produces pull requests that argue with a person. And the sweep
proposes one fix per run by default, because 139 pull requests nobody reads
would make the review gate theatre.

Selection is by SonarQube's own effort estimate rather than severity: effort
is the closest available proxy for the one-anchor change the patch validator
can actually check, so a trivial CRITICAL beats an involved MINOR. An
unparseable estimate is treated as ineligible, not as free.

Off by default. Triage reacts to a failure someone already cares about; this
opens pull requests nobody asked for, and that is a decision an operator makes
deliberately rather than inherits on upgrade.

Branch naming now sanitizes its token, since it arrives from a finding key as
well as a build number and a ref is one of the few places where an unexpected
character stops being cosmetic.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 22:08:42 -03:00
codex
54de192c8b feat(hermes): make the fix categories an operator setting
All checks were successful
Tests / Declarative: Post Actions passed: 1237
The three defect categories were implicit in code: an operator could neither
see which were active nor switch one off. ARIADNE_HERMES_FIX_CATEGORIES now
names them, an unrecognised entry is ignored rather than trusted, and an empty
setting means all three so the default stays obvious.

Kept separate from ARIADNE_HERMES_ALLOWED_ACTIONS on purpose. That allowlist
gates what Ariadne executes on its own authority, with nothing between the
decision and the change. Everything here becomes a pull request a person
reads. Sharing one list would let a patch clear the same gate as an autonomous
mutation, and that distinction is what makes the autonomous half defensible.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 20:46:27 -03:00
codex
c6aa448ee8 feat(hermes): add undefined-name and failing-assertion fix categories
All checks were successful
Tests / Declarative: Post Actions passed: 1233
Categories two and three of the mechanical-fix work, sharing the seam category
one established.

Undefined or misspelled names are located exactly by the runtime or compiler,
so the fix is one identifier or one import. Python names the file on a
traceback frame above the error, so the most recent frame is carried forward
and attached when the error arrives; a defect with no known file is dropped
because the patcher could not act on it.

Failing assertions now come from the structured test results junit publishes,
carrying the test, its class and the assertion. They deliberately carry no
path: the failing test is the symptom and the defect is usually in the code
under test, so naming the test file would invite weakening the assertion
instead of fixing the cause. The instruction says so explicitly.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 15:42:38 -03:00
codex
3a83e78f16 feat(hermes): hand the linter's own diagnosis to the patcher
All checks were successful
Tests / Declarative: Post Actions passed: 1217
Category one of the mechanical-fix work. A linter has already located the
defect precisely - file, line, rule, and what is wrong - so passing that
through converts an open-ended 'repair this build' request into a narrow
instruction whose result can be checked against the same evidence.

Recognises ruff/flake8, golangci-lint, and eslint diagnostics from console
evidence, deduplicates them, and bounds the list at twenty. A diagnostic for a
file outside the write allowlist is dropped at extraction rather than later,
so a defect is never reported for a file the patcher could not touch anyway.
Only categories whose fix is mechanical belong here; anything needing a
judgement about intended behaviour stays on the ordinary escalation path.

A build with no linter output is unaffected and falls back to the existing
open-ended request. The patch prompt moves to its own module, as the triage
prompt already had.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 15:26:54 -03:00