Repository navigation
feat(#9): Phase 5 — baseline & stale detection - #10
Merged
Merged
Conversation
Baseline against the shipped Phase 4 interfaces. Two refinements of the master sketch, both recorded as normative: the fingerprint's terminal token is the terminal FQMN or the TerminalKind value, not a per-reason category (coarser survives resolver improvements without re-opening grandfathered findings), and suppression moves out of ChainTraversal into a fired-tracking SuppressionFilter because stale-ignore detection needs to know what fired. Stale detection is skipped under --changed-only, where almost every entry would look stale.
Two new fixture cases exercising the baseline flow end-to-end through the existing folder-mode harness: - baseline-filters-known: two distinct chains; a committed baseline.json covers exactly one (the Controller->ServiceA->Mailer chain); the harness confirms the other chain is reported while the baselined one is invisible to stdout JSON, the exit code, and the stale reporter. - baseline-stale-entry: a baseline containing only a fabricated 40-zero hash that matches nothing; the run's single chain is reported unchanged on stdout, and the stale line is written to stderr only — proving the harness (which compares stdout JSON) is unaffected by stale reporting.
- README: status line bumped to Phase 5; new Baseline section with the adoption story (generate -> commit -> gate -> shrink), the refactor-stable fingerprint semantics, --fail-on-stale, and the --changed-only interaction note (stale detection skipped under --changed-only — full runs only). Dropped the 'reserved for Phase 5' wording from the two baseline flag rows and the config 'baseline' key row; added --fail-on-stale to the CLI listing. - docs/ci.md: legacy-adoption recipes — a full-scan baseline-gated job with --fail-on-stale, and a diff-aware PR job composing --changed-only --baseline (no --fail-on-stale there: stale detection is skipped under --changed-only). - docs/plan.md: frozen rule 10 records the fingerprint-token refinement made normative this phase (terminal FQMN when present, else the TerminalKind backing value, not a per-reason category); Phase 5 marked complete with 5.1/5.2/5.3 checked off, and 5.1's superseded 'truncated:<reason-category>' sketch rewritten to match the refinement. - Application help text: --fail-on-stale added to the Baseline section; Status line now mentions Phase 5 baseline shipped. None of the three baseline flags carry 'reserved' wording.
Baseline held two index-aligned lists (fingerprint hashes + raw entry lines) and probed them with in_array, so has() and staleEntries() were O(findings × entries) linear scans and carried a "keep these two arrays in step" invariant. Collapse them into one hash => line map: membership is now an O(1) isset, staleEntries builds a live-hash set once, and the parallel-array coupling is gone. generate() reuses Report\JsonEncoder instead of hand-rolling the same JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES pair, picking up its encode-failure guard — the JSON reporters already call it from the same uncaught path.
matchedKeysFor kept its own $seen map to dedup keys across a finding's hops, but filter() already dedups every key against a run-global $seen before recording it — the inner pass was dead work duplicating the outer loop. Remove it; the returned list feeds only the emptiness test and that global dedup, neither of which duplicates affect. Add a test pinning the invariant this now leans on: two chains through one suppressed method fire its key exactly once (the existing dedup test used two distinct keys, so never exercised a repeated key).
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.
Closes #9
Implements Phase 5: refactor-stable finding baselines and stale-entry detection.
What's new
--generate-baseline <file>— writes current findings (errors + warnings) to a baseline document. Maintenance mode: exits 0 regardless of findings.--baseline <file>— ignores findings recorded in the baseline; a baselined finding is invisible to every reporter and the exit code. Generation wins if both flags are set.--fail-on-stale— exit 1 when a baseline entry or suppression matched nothing (full runs only; skipped under--changed-only, where the diff filter would make almost everything look stale).Fingerprinthashes only the semantic identity (origin, param, terminal token), never chain shape or source locations, so shortening a chain, moving a file, or shifting a line does not re-open a baselined finding.generate()round-trips byte-stably on an unchanged tree.ChainTraversalused to make silently is extracted intoSuppressionFilter, which returns aSuppressionOutcomerecording every suppression key that fired. That's what makes stale-suppression reporting possible.Design notes
Baselineis stored as ahash => linemap: membership is O(1), and staleness isentries − live.StaleReporter, kept separate fromApplicationto stay under the PHPMD complexity ceiling.ConfigLoader).Verification
composer checkgreen (PSR-12, PHPStan level max, PHPMD codesize, 374 tests / 1245 assertions).composer infection:diff— MSI 94% (gate is 80).Docs (
README.md,docs/ci.md,docs/plan.md,docs/plans/phase-5.md) updated for baseline usage and Phase 5 status.