Skip to content

feat: generalize new-class advisory into config-driven advice block (#347)#349

Merged
fdaviddpt merged 2 commits into
masterfrom
feat/generic-advice
Jun 26, 2026
Merged

feat: generalize new-class advisory into config-driven advice block (#347)#349
fdaviddpt merged 2 commits into
masterfrom
feat/generic-advice

Conversation

@fdaviddpt

Copy link
Copy Markdown
Contributor

Context

#347 asked for a hint when an edit/paste adds a component class or a public component attribute (XSD/cache regen reminder), gated behind a config flag like the existing adviseForNewTest. Rather than bolt on a second hardcoded advisory, this generalizes the mechanism: adviseForNewTest (a single-purpose bool) becomes a config-driven advice block of named rules, so projects add nudges as data instead of code.

What changed

  • advice block — top-level map of named rules. Each rule gates (all optional) on:
    • hooks_into — ops that trigger it (default: all mutating)
    • match — path glob
    • whennew-file | existing-file | always
    • contains — regex over the content the op added (lines after but not before), so it fires only when the op introduces the pattern, not when the file already held it
    • resolve / resolveFromValidator — a subprocess emitting a would-be target via the exit-3 + stderr contract
    • message{target}/{path}/{op} interpolate; a {target}-less message gets — consider <target> appended when a resolver produced one
  • resolveFromValidator accepts a validator name, not just true. true grabs the first validator declaring a resolver — in a multi-resolver config that's the wrong one (DVSI: phpstan-component's resolver precedes phpunit's, so the old adviseForNewTest was almost certainly resolving against the wrong script).
  • Threaded pre_content into both _run_with_validators call sites so contains matches added-content.
  • Docs (validators.md + configuration.md), .supertool.example.json, CHANGELOG.

Breaking

adviseForNewTest: true removed. Express it as an advice.newTest rule:

"advice": { "newTest": { "hooks_into": ["paste"], "match": "*.php", "when": "new-file", "resolveFromValidator": "phpunit", "message": "new class without test" } }

Tests

Rewrote the advice test block for the new API: newTest parity (7), contains-on-added-content (3), when gate, default ops, multi-rule, interpolation, named-validator disambiguation, and a regression test asserting the [advice] block uses real newlines (a literal-\n bug slipped past substring asserts and was caught in self-review). Full suite: 3479 passed.

Closes #347

🤖 Generated by Max

…#347)

Replace the single-purpose `adviseForNewTest` bool with a top-level `advice`
map of named rules. Each rule gates (all optional) on `hooks_into` (ops,
default all mutating), `match` (path glob), `when` (new-file|existing-file|
always), `contains` (regex over the content the op *added* — lines present
after but not before), and `resolve`/`resolveFromValidator` (a subprocess
emitting a would-be target via the exit-3 + stderr contract). `message` is the
rendered line, with `{target}`/`{path}`/`{op}` interpolation.

`resolveFromValidator` accepts a validator NAME (not just `true`) so a rule can
reuse a specific validator's resolver — `true` grabs the first resolver
declared, which in a multi-resolver config (DVSI: phpstan-component precedes
phpunit) is the wrong one.

BREAKING: `adviseForNewTest: true` removed — express as an `advice.newTest`
rule. See `.supertool.example.json` and docs/validators.md.

Closes #347

Co-Authored-By: Max <noreply>
…erpolation

- _advice_added_text: multiset (count-based) diff, not set membership — a line
  the op duplicates now counts as added even when an identical line pre-existed.
- _advice_wants_pre + both call sites: snapshot pre-edit bytes whenever a
  contains rule applies, so the added-content gate stays exact even when no
  validator has rollback_on_fail and no notifier is configured (previously fell
  back to whole-file matching → fired on content the op did not introduce).
- message render: interpolate {path}/{op} before inserting a resolver-produced
  target, so target text can't be re-scanned for placeholders; strip the leading
  space when the configured message is empty (no more 'ℹ  — consider').

Adds tests for the multiset duplicate-line case and _advice_wants_pre gating.

Co-Authored-By: Max <noreply>
@fdaviddpt

Copy link
Copy Markdown
Contributor Author

Addressed review in 0d601f1:

Tests added for the multiset case and _advice_wants_pre. Full suite: 3481 passed.

@fdaviddpt fdaviddpt merged commit 975e9d6 into master Jun 26, 2026
12 checks passed
@fdaviddpt fdaviddpt deleted the feat/generic-advice branch June 26, 2026 15:51
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.

edit/paste: advise XSD/cache regen when a new component class or public component property is added

1 participant